diff --git a/.gitignore b/.gitignore
index 8cf7405..6411252c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -350,6 +350,9 @@
 /third_party/google_appengine_cloudstorage
 /third_party/google_toolbox_for_mac/src
 /third_party/googlemac
+/third_party/gvr-android-sdk/common_library.aar
+/third_party/gvr-android-sdk/libgvr_shim_static_arm.a
+/third_party/gvr-android-sdk/libgvr_shim_static_arm64.a
 /third_party/gvr-android-sdk/src
 /third_party/gperf
 /third_party/grpc
diff --git a/BUILD.gn b/BUILD.gn
index 73c3053..f589a3a 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -13,6 +13,7 @@
 import("//build/config/sanitizers/sanitizers.gni")
 import("//build/config/ui.gni")
 import("//build_overrides/v8.gni")
+import("//extensions/features/features.gni")
 import("//media/media_options.gni")
 import("//remoting/remoting_enable.gni")
 import("//third_party/openh264/openh264_args.gni")
@@ -524,7 +525,7 @@
     ]
   }
 
-  if (is_win) {
+  if (is_win || is_mac) {
     deps += [ "//third_party/swiftshader" ]
   }
 
diff --git a/DEPS b/DEPS
index 607ff5e..c1baca5 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '58b130681db4432c4937c2cb1b2529de628c6b19',
+  'skia_revision': '2563601fc2b0505619f905f86bd249ae630197cc',
   # 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': '0bf9e237ceac41b95b684542e27617b2938faa9b',
+  'v8_revision': 'e4f7dbd2552de1019a8671e946946a9598655307',
   # 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.
@@ -60,11 +60,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '8be4110779a653fe1ae051f089bce9340147a8d1',
+  'swiftshader_revision': 'e1d853076fa446532b6239fb64f2ca824e8802ea',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'a0d3231037816d3f0e377e9c57a2bfaa1372e151',
+  'pdfium_revision': 'c40697b24550182898b30de639790eaf82ebf158',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -96,7 +96,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '7eb93efcacdcc927f3c71785aff48a676cc5cded',
+  'catapult_revision': '3cf7880978348ea06bd7f4275b9190d3ed60715e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -146,7 +146,7 @@
     Var('chromium_git') + '/external/colorama.git' + '@' + '799604a1041e9b3bc5d2789ecbd7e8db2e18e6b8',
 
   'src/third_party/icu':
-    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + 'e0d9b90cae887a97977b0992f7a746eb1c01b9ac',
+    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '7ddf5e9ba15a65e692767d3e45260a75f9419f0b',
 
   'src/third_party/hunspell_dictionaries':
     Var('chromium_git') + '/chromium/deps/hunspell_dictionaries.git' + '@' + 'dc6e7c25bf47cbfb466e0701fd2728b4a12e79d5',
@@ -866,6 +866,39 @@
                 '-s', 'src/buildtools/linux64/clang-format.sha1',
     ],
   },
+  {
+    'name': 'gvr_static_shim_android_arm',
+    'pattern': '\\.sha1',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=linux*',
+                '--no_auth',
+                '--bucket', 'chromium-gvr-static-shim',
+                '-s', 'src/third_party/gvr-android-sdk/libgvr_shim_static_arm.a.sha1',
+    ],
+  },
+  {
+    'name': 'gvr_static_shim_android_arm64',
+    'pattern': '\\.sha1',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=linux*',
+                '--no_auth',
+                '--bucket', 'chromium-gvr-static-shim',
+                '-s', 'src/third_party/gvr-android-sdk/libgvr_shim_static_arm64.a.sha1',
+    ],
+  },
+  {
+    'name': 'gvr_common_aar',
+    'pattern': '\\.sha1',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=linux*',
+                '--no_auth',
+                '--bucket', 'chromium-gvr-static-shim',
+                '-s', 'src/third_party/gvr-android-sdk/common_library.aar.sha1',
+    ],
+  },
   # Pull luci-go binaries (isolate, swarming) using checked-in hashes.
   {
     'name': 'luci-go_win',
diff --git a/WATCHLISTS b/WATCHLISTS
index ec3279f..e7e7e8a 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1023,6 +1023,11 @@
                   'chrome/test/data/chromeos/virtual_keyboard/'\
                   'chrome/common/extensions/api/virtual_keyboard',
     },
+    'virtual_reality': {
+      'filepath': 'vr_shell/|'\
+                  '/vr/|'\
+                  'third_party/gvr',
+    },
     'webrtc_browser_tests': {
       'filepath': 'chrome/browser/media/.*webrtc.*browsertest|'\
                   'content/browser/media/.*webrtc.*browsertest',
@@ -2030,6 +2035,7 @@
     'views': ['tfarina@chromium.org'],
     'virtual_keyboard': ['dfaden+virtualkb@google.com',
                          'groby+virtualkb@chromium.org'],
+    'virtual_reality': ['feature-vr-reviews@chromium.org'],
     'webrtc_browser_tests': ['phoglund+watch@chromium.org'],
     'website_settings': ['markusheintz@chromium.org',
                          'msramek+watch@chromium.org',
diff --git a/android_webview/browser/hardware_renderer.cc b/android_webview/browser/hardware_renderer.cc
index 45bf988..ea682cb9 100644
--- a/android_webview/browser/hardware_renderer.cc
+++ b/android_webview/browser/hardware_renderer.cc
@@ -53,7 +53,7 @@
   // Reset draw constraints.
   render_thread_manager_->PostExternalDrawConstraintsToChildCompositorOnRT(
       ParentCompositorDrawConstraints());
-  for (auto& child_frame : child_frames_) {
+  for (auto& child_frame : child_frame_queue_) {
     child_frame->WaitOnFutureIfNeeded();
     ReturnChildFrame(std::move(child_frame));
   }
@@ -70,29 +70,28 @@
     return;
   // Insert all except last, ie current frame.
   while (child_frames.size() > 1u) {
-    child_frames_.emplace_back(std::move(child_frames.front()));
+    child_frame_queue_.emplace_back(std::move(child_frames.front()));
     child_frames.pop_front();
   }
-  for (auto& pruned_frame : WaitAndPruneFrameQueue(&child_frames_))
+  for (auto& pruned_frame : WaitAndPruneFrameQueue(&child_frame_queue_))
     ReturnChildFrame(std::move(pruned_frame));
-  DCHECK_LE(child_frames_.size(), 1u);
-  child_frames_.emplace_back(std::move(child_frames.front()));
+  DCHECK_LE(child_frame_queue_.size(), 1u);
+  child_frame_queue_.emplace_back(std::move(child_frames.front()));
 }
 
 void HardwareRenderer::DrawGL(AwDrawGLInfo* draw_info) {
   TRACE_EVENT0("android_webview", "HardwareRenderer::DrawGL");
 
-  for (auto& pruned_frame : WaitAndPruneFrameQueue(&child_frames_))
+  for (auto& pruned_frame : WaitAndPruneFrameQueue(&child_frame_queue_))
     ReturnChildFrame(std::move(pruned_frame));
-  DCHECK_LE(child_frames_.size(), 1u);
-  std::unique_ptr<ChildFrame> child_frame;
-  if (!child_frames_.empty()) {
-    child_frame = std::move(child_frames_.front());
-    child_frames_.clear();
+  DCHECK_LE(child_frame_queue_.size(), 1u);
+  if (!child_frame_queue_.empty()) {
+    child_frame_ = std::move(child_frame_queue_.front());
+    child_frame_queue_.clear();
   }
-  if (child_frame) {
+  if (child_frame_) {
     last_committed_compositor_frame_sink_id_ =
-        child_frame->compositor_frame_sink_id;
+        child_frame_->compositor_frame_sink_id;
   }
 
   // We need to watch if the current Android context has changed and enforce
@@ -108,22 +107,22 @@
   // during "kModeSync" stage (which does not allow GL) might result in extra
   // kModeProcess. Instead, submit the frame in "kModeDraw" stage to avoid
   // unnecessary kModeProcess.
-  if (child_frame.get() && child_frame->frame.get()) {
-    if (!compositor_id_.Equals(child_frame->compositor_id) ||
+  if (child_frame_.get() && child_frame_->frame.get()) {
+    if (!compositor_id_.Equals(child_frame_->compositor_id) ||
         last_submitted_compositor_frame_sink_id_ !=
-            child_frame->compositor_frame_sink_id) {
+            child_frame_->compositor_frame_sink_id) {
       if (child_id_.is_valid())
         DestroySurface();
 
       // This will return all the resources to the previous compositor.
       surface_factory_->Reset();
-      compositor_id_ = child_frame->compositor_id;
+      compositor_id_ = child_frame_->compositor_id;
       last_submitted_compositor_frame_sink_id_ =
-          child_frame->compositor_frame_sink_id;
+          child_frame_->compositor_frame_sink_id;
     }
 
     std::unique_ptr<cc::CompositorFrame> child_compositor_frame =
-        std::move(child_frame->frame);
+        std::move(child_frame_->frame);
 
     gfx::Size frame_size =
         child_compositor_frame->render_pass_list.back()->output_rect.size();
@@ -150,7 +149,7 @@
   // compositor might not have the tiles rasterized as the animation goes on.
   ParentCompositorDrawConstraints draw_constraints(
       draw_info->is_layer, transform, viewport.IsEmpty());
-  if (!child_frame.get() || draw_constraints.NeedUpdate(*child_frame)) {
+  if (!child_frame_.get() || draw_constraints.NeedUpdate(*child_frame_)) {
     render_thread_manager_->PostExternalDrawConstraintsToChildCompositorOnRT(
         draw_constraints);
   }
diff --git a/android_webview/browser/hardware_renderer.h b/android_webview/browser/hardware_renderer.h
index 37f6044ac..4519150 100644
--- a/android_webview/browser/hardware_renderer.h
+++ b/android_webview/browser/hardware_renderer.h
@@ -74,10 +74,12 @@
   // Infromation from UI on last commit.
   gfx::Vector2d scroll_offset_;
 
+  ChildFrameQueue child_frame_queue_;
+
   // This holds the last ChildFrame received. Contains the frame info of the
-  // last frame. The |frame| member may be null if it's already submitted to
-  // SurfaceFactory.
-  ChildFrameQueue child_frames_;
+  // last frame. The |frame| member is always null since frame has already
+  // been submitted.
+  std::unique_ptr<ChildFrame> child_frame_;
 
   const scoped_refptr<SurfacesInstance> surfaces_;
   cc::FrameSinkId frame_sink_id_;
diff --git a/android_webview/tools/automated_ui_tests/BUILD.gn b/android_webview/tools/automated_ui_tests/BUILD.gn
index 487b11d8..a7dac42 100644
--- a/android_webview/tools/automated_ui_tests/BUILD.gn
+++ b/android_webview/tools/automated_ui_tests/BUILD.gn
@@ -41,6 +41,7 @@
   apk_under_test = ":webview_ui_test_app_apk"
   android_manifest = "javatests/AndroidManifest.xml"
   java_files = [
+    "javatests/src/org/chromium/webview_ui_test/test/ActionModeTest.java",
     "javatests/src/org/chromium/webview_ui_test/test/WebViewJSTest.java",
     "javatests/src/org/chromium/webview_ui_test/test/util/UseLayout.java",
     "javatests/src/org/chromium/webview_ui_test/test/util/WebViewUiTestRule.java",
@@ -53,8 +54,7 @@
     "//base:base_java_test_support",
     "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
-    "//third_party/espresso:espresso_core_java",
-    "//third_party/espresso:espresso_web_java",
+    "//third_party/espresso:espresso_all_java",
     "//third_party/hamcrest:hamcrest_java",
     "//third_party/junit",
     "//third_party/ub-uiautomator:ub_uiautomator_java",
diff --git a/android_webview/tools/automated_ui_tests/java/res/layout/edittext_webview.xml b/android_webview/tools/automated_ui_tests/java/res/layout/edittext_webview.xml
new file mode 100644
index 0000000..5f5a7e7d
--- /dev/null
+++ b/android_webview/tools/automated_ui_tests/java/res/layout/edittext_webview.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    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="280dp" />
+    <EditText
+        android:id="@+id/edittext"
+        android:inputType="text"
+        android:layout_below="@+id/webview"
+        android:layout_width="match_parent"
+        android:layout_height="60dp" />
+</RelativeLayout>
+
diff --git a/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/ActionModeTest.java b/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/ActionModeTest.java
new file mode 100644
index 0000000..f7269eb8
--- /dev/null
+++ b/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/ActionModeTest.java
@@ -0,0 +1,288 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.webview_ui_test.test;
+
+import static android.support.test.espresso.Espresso.onData;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.actionWithAssertions;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.intent.Intents.assertNoUnverifiedIntents;
+import static android.support.test.espresso.intent.Intents.intended;
+import static android.support.test.espresso.intent.Intents.intending;
+import static android.support.test.espresso.intent.matcher.BundleMatchers.hasEntry;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.anyIntent;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasExtra;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasExtras;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasType;
+import static android.support.test.espresso.matcher.RootMatchers.DEFAULT;
+import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
+import static android.support.test.espresso.matcher.ViewMatchers.withChild;
+import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
+import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static android.support.test.espresso.web.assertion.WebViewAssertions.webMatches;
+import static android.support.test.espresso.web.sugar.Web.onWebView;
+import static android.support.test.espresso.web.webdriver.DriverAtoms.findElement;
+import static android.support.test.espresso.web.webdriver.DriverAtoms.getText;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.endsWith;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.core.AnyOf.anyOf;
+import static org.junit.Assert.assertTrue;
+
+import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
+
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.os.Build;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.espresso.NoMatchingViewException;
+import android.support.test.espresso.PerformException;
+import android.support.test.espresso.Root;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.action.GeneralClickAction;
+import android.support.test.espresso.action.GeneralLocation;
+import android.support.test.espresso.action.Press;
+import android.support.test.espresso.action.Tap;
+import android.support.test.espresso.intent.Intents;
+import android.support.test.espresso.web.webdriver.Locator;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiSelector;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.MenuItem;
+
+import junit.framework.AssertionFailedError;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.Log;
+import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.webview_ui_test.R;
+import org.chromium.webview_ui_test.WebViewUiTestActivity;
+import org.chromium.webview_ui_test.test.util.UseLayout;
+import org.chromium.webview_ui_test.test.util.WebViewUiTestRule;
+
+import java.lang.reflect.Method;
+
+/**
+ * Tests for WebView ActionMode.
+ */
+@RunWith(BaseJUnit4ClassRunner.class)
+public class ActionModeTest {
+    private static final String TAG = "ActionModeTest";
+
+    // Actions available in action mode
+    private static final String ASSIST_ACTION = "Assist";
+    private static final String COPY_ACTION = "Copy";
+    private static final String MORE_OPTIONS_ACTION = "More options";
+    private static final String PASTE_ACTION = "Paste";
+    private static final String SHARE_ACTION = "Share";
+    private static final String SELECT_ALL_ACTION = "Select All";
+    private static final String WEB_SEARCH_ACTION = "Web search";
+
+    private static final String QUICK_SEARCH_BOX_PKG = "com.google.android.googlequicksearchbox";
+    private static final long ASSIST_TIMEOUT = scaleTimeout(5000);
+
+    @Rule
+    public WebViewUiTestRule mWebViewActivityRule =
+            new WebViewUiTestRule(WebViewUiTestActivity.class);
+
+    @Before
+    public void setUp() {
+        mWebViewActivityRule.launchActivity();
+        onWebView().forceJavascriptEnabled();
+        mWebViewActivityRule.loadDataSync(
+                "<html><body><p>Hello world</p></body></html>", "text/html", "utf-8", false);
+        onWebView(withId(R.id.webview))
+                .withElement(findElement(Locator.TAG_NAME, "p"))
+                .check(webMatches(getText(), containsString("Hello world")));
+        disableAnimation();
+    }
+
+    /**
+     * Only way to disable popup animations.
+     */
+    private void disableAnimation() {
+        try {
+            // This is a hidden method to disable animations.  It is also being used by CTS tests.
+            Method setDurationScale = ValueAnimator.class.getMethod(
+                    "setDurationScale", float.class);
+            setDurationScale.invoke(null, 0.0f);
+        } catch (Exception e) {
+            Log.e(TAG, "Couldn't disable animation", e);
+        }
+    }
+
+    /**
+     * Test Copy and Paste
+     */
+    @Test
+    @SmallTest
+    @UseLayout("edittext_webview")
+    public void testCopyPaste() {
+        onView(withId(R.id.webview)).perform(longClickOnLastWord());
+        clickPopupAction(COPY_ACTION);
+        onView(withId(R.id.edittext)).perform(longClickOnLastWord());
+        clickPopupAction(PASTE_ACTION);
+        onView(withId(R.id.edittext))
+                .check(matches(withText("world")));
+    }
+
+    /**
+     * Test Select All
+     */
+    @Test
+    @SmallTest
+    @UseLayout("edittext_webview")
+    public void testSelectAll() {
+        onView(withId(R.id.webview)).perform(longClickOnLastWord());
+        clickPopupAction("Select all");
+        clickPopupAction(COPY_ACTION);
+        onView(withId(R.id.edittext)).perform(longClickOnLastWord());
+        clickPopupAction(PASTE_ACTION);
+        onView(withId(R.id.edittext))
+                .check(matches(withText("Hello world")));
+    }
+
+    /**
+     * Test Share
+     */
+    @Test
+    @SmallTest
+    @UseLayout("edittext_webview")
+    public void testShare() {
+        Intents.init();
+        intending(anyIntent())
+                .respondWith(new Instrumentation.ActivityResult(Activity.RESULT_OK, new Intent()));
+
+        onView(withId(R.id.webview)).perform(longClickOnLastWord());
+        clickPopupAction(SHARE_ACTION);
+
+        intended(allOf(hasAction(Intent.ACTION_CHOOSER),
+                hasExtras(allOf(hasEntry(Intent.EXTRA_TITLE, SHARE_ACTION),
+                        hasEntry(Intent.EXTRA_INTENT,
+                                allOf(hasAction(Intent.ACTION_SEND), hasType("text/plain"),
+                                        hasExtra(Intent.EXTRA_TEXT, "world")))))));
+        assertNoUnverifiedIntents();
+    }
+
+    /**
+     * Test Web Search
+     */
+    @Test
+    @SmallTest
+    @UseLayout("edittext_webview")
+    public void testWebSearch() {
+        Intents.init();
+        intending(anyIntent())
+                .respondWith(new Instrumentation.ActivityResult(Activity.RESULT_OK, new Intent()));
+        onView(withId(R.id.webview)).perform(longClickOnLastWord());
+        clickPopupAction(WEB_SEARCH_ACTION);
+        intended(allOf(hasAction(Intent.ACTION_WEB_SEARCH),
+                hasExtras(allOf(hasEntry("com.android.browser.application_id",
+                                         "org.chromium.webview_ui_test"),
+                                hasEntry("query", "world"),
+                                hasEntry("new_search", true)))));
+        assertNoUnverifiedIntents();
+    }
+
+    /**
+     * Test Assist
+     */
+    @Test
+    @SmallTest
+    @UseLayout("edittext_webview")
+    public void testAssist() {
+        // TODO(aluo): Get SdkSuppress to work with the test runner
+        if (Build.VERSION.SDK_INT < 24) return;
+        onView(withId(R.id.webview)).perform(longClickOnLastWord());
+        clickPopupAction(ASSIST_ACTION);
+        UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        UiObject assistUi = device.findObject(new UiSelector().packageName(QUICK_SEARCH_BOX_PKG));
+        assertTrue(assistUi.waitForExists(ASSIST_TIMEOUT));
+        device.pressBack();
+    }
+
+    /**
+     * Click an item on the Action Mode popup
+     */
+    public void clickPopupAction(final String name) {
+        Matcher<Root> rootMatcher;
+
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+            try {
+                // On L and lower, use the espresso DEFAULT root matcher if ActionBar is detected
+                onView(withClassName(endsWith("widget.ActionBarContextView")))
+                        .check(matches(isDisplayed()));
+                rootMatcher = DEFAULT;
+            } catch (NoMatchingViewException | AssertionFailedError e) {
+                // Else match in a popup
+                rootMatcher = withDecorView(withChild(withText(name)));
+            }
+        } else {
+            // On M and above, can use the decoreView matcher
+            rootMatcher = withDecorView(isEnabled());
+        }
+        try {
+            onView(anyOf(withText(name), withContentDescription(name)))
+                    .inRoot(rootMatcher)
+                    .perform(click());
+        } catch (PerformException e) {
+            // Take care of case when the item is in the overflow menu
+            onView(withContentDescription(MORE_OPTIONS_ACTION))
+                    .inRoot(withDecorView(isEnabled()))
+                    .perform(click());
+            onData(new MenuItemMatcher(equalTo(name))).inRoot(rootMatcher).perform(click());
+        }
+    }
+
+    /**
+     * This view action clicks on center right of a view to select the last word
+     */
+    private static final ViewAction longClickOnLastWord() {
+        // TODO(aluo): This function is not guaranteed to click on element. Change to
+        // implementation that gets bounding box for elements using Javascript.
+        return actionWithAssertions(
+                new GeneralClickAction(Tap.LONG, GeneralLocation.CENTER_RIGHT, Press.FINGER));
+    }
+
+    /**
+     * Matches an item on the Action Mode popup by the title
+     */
+    private static class MenuItemMatcher extends TypeSafeMatcher<MenuItem> {
+        private Matcher<String> mTitleMatcher;
+
+        public MenuItemMatcher(Matcher<String> titleMatcher) {
+            mTitleMatcher = titleMatcher;
+        }
+
+        @Override
+        protected boolean matchesSafely(MenuItem item) {
+            return mTitleMatcher.matches(item.getTitle());
+        }
+
+        @Override
+        public void describeTo(Description description) {
+            description.appendText("has MenuItem with title: ");
+            description.appendDescriptionOf(mTitleMatcher);
+        }
+    }
+}
diff --git a/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/util/WebViewSyncWrapper.java b/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/util/WebViewSyncWrapper.java
index f45902a..eeec330 100644
--- a/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/util/WebViewSyncWrapper.java
+++ b/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/util/WebViewSyncWrapper.java
@@ -117,6 +117,27 @@
         });
     }
 
+    public void loadDataSync(final String data, final String mimeType, final String encoding,
+            boolean confirmByJavaScript) throws InterruptedException {
+        mErrorMessageList.clear();
+        int currentPageCount = mPageCallback.getCallCount();
+        int currentJsCount = mJsCallback.getCallCount();
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mWebView.loadData(data, mimeType, encoding);
+            }
+        });
+        try {
+            if (confirmByJavaScript) {
+                mJsCallback.waitForCallback(currentJsCount);
+            }
+            mPageCallback.waitForCallback(currentPageCount);
+        } catch (TimeoutException e) {
+            Assert.fail(e.getMessage());
+        }
+    }
+
     public void loadFileSync(final String html, boolean confirmByJavaScript)
             throws InterruptedException {
         mErrorMessageList.clear();
diff --git a/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/util/WebViewUiTestRule.java b/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/util/WebViewUiTestRule.java
index 40438dcf..d60a16bd 100644
--- a/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/util/WebViewUiTestRule.java
+++ b/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/util/WebViewUiTestRule.java
@@ -57,6 +57,15 @@
         return launchActivity(new Intent());
     }
 
+    public void loadDataSync(
+            String data, String mimeType, String encoding, boolean confirmByJavaScript) {
+        try {
+            mSyncWrapper.loadDataSync(data, mimeType, encoding, confirmByJavaScript);
+        } catch (InterruptedException e) {
+            Assert.fail(e.getMessage());
+        }
+    }
+
     public void loadJavaScriptSync(String js, boolean appendConfirmationJavascript) {
         try {
             mSyncWrapper.loadJavaScriptSync(js, appendConfirmationJavascript);
diff --git a/apps/BUILD.gn b/apps/BUILD.gn
index ede4e7e..7acd495 100644
--- a/apps/BUILD.gn
+++ b/apps/BUILD.gn
@@ -4,6 +4,7 @@
 
 import("//build/config/features.gni")
 import("//build/config/ui.gni")
+import("//extensions/features/features.gni")
 
 assert(!is_android && !is_ios)
 
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 41ab177..3fe326a 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -191,7 +191,6 @@
     "common/system/brightness_control_delegate.h",
     "common/system/chromeos/audio/audio_detailed_view.cc",
     "common/system/chromeos/audio/audio_detailed_view.h",
-    "common/system/chromeos/audio/audio_observer.h",
     "common/system/chromeos/audio/tray_audio.cc",
     "common/system/chromeos/audio/tray_audio.h",
     "common/system/chromeos/audio/tray_audio_delegate.h",
diff --git a/ash/common/BUILD.gn b/ash/common/BUILD.gn
index 02716f99..eedb73c 100644
--- a/ash/common/BUILD.gn
+++ b/ash/common/BUILD.gn
@@ -28,6 +28,7 @@
 
   if (is_chromeos) {
     sources += [
+      "system/chromeos/audio/tray_audio_unittest.cc",
       "system/chromeos/brightness/tray_brightness_unittest.cc",
       "system/chromeos/screen_security/screen_tray_item_unittest.cc",
       "system/chromeos/supervised/tray_supervised_user_unittest.cc",
diff --git a/ash/common/system/chromeos/audio/audio_observer.h b/ash/common/system/chromeos/audio/audio_observer.h
deleted file mode 100644
index ed937ad1..0000000
--- a/ash/common/system/chromeos/audio/audio_observer.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 ASH_COMMON_SYSTEM_CHROMEOS_AUDIO_AUDIO_OBSERVER_H_
-#define ASH_COMMON_SYSTEM_CHROMEOS_AUDIO_AUDIO_OBSERVER_H_
-
-#include <stdint.h>
-
-namespace ash {
-
-class AudioObserver {
- public:
-  virtual ~AudioObserver() {}
-
-  // Called when an active output device's volume changed.
-  // |node_id|: id of the active node.
-  // |volume|: volume as a percentage, 0.0 -- 100.0.
-  virtual void OnOutputNodeVolumeChanged(uint64_t node_id, double volume) = 0;
-
-  // Called when output mute state changed.
-  // |mute_on|: True if output is muted.
-  // |system_adjust|: True if the mute state is adjusted by the system
-  // automatically, UI should be consistent with the system's mute state,
-  // but it should not be too loud, e.g., the volume pop up window should not
-  // be triggered.
-  virtual void OnOutputMuteChanged(bool mute_on, bool system_adjust) = 0;
-
-  // Called when audio nodes changed.
-  virtual void OnAudioNodesChanged() = 0;
-
-  // Called when active audio output node changed.
-  virtual void OnActiveOutputNodeChanged() = 0;
-
-  // Called when active audio input node changed.
-  virtual void OnActiveInputNodeChanged() = 0;
-};
-
-}  // namespace ash
-
-#endif  // ASH_COMMON_SYSTEM_CHROMEOS_AUDIO_AUDIO_OBSERVER_H_
diff --git a/ash/common/system/chromeos/audio/tray_audio.cc b/ash/common/system/chromeos/audio/tray_audio.cc
index 3510da42..db597c0a 100644
--- a/ash/common/system/chromeos/audio/tray_audio.cc
+++ b/ash/common/system/chromeos/audio/tray_audio.cc
@@ -7,9 +7,11 @@
 #include "ash/common/system/chromeos/audio/audio_detailed_view.h"
 #include "ash/common/system/chromeos/audio/tray_audio_delegate_chromeos.h"
 #include "ash/common/system/chromeos/audio/volume_view.h"
-#include "ash/common/system/tray/system_tray_notifier.h"
+#include "ash/common/system/tray/system_tray.h"
 #include "ash/common/system/tray/tray_constants.h"
+#include "ash/common/wm_root_window_controller.h"
 #include "ash/common/wm_shell.h"
+#include "ash/common/wm_window.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "grit/ash_resources.h"
 #include "ui/display/display.h"
@@ -19,6 +21,7 @@
 
 namespace ash {
 
+using chromeos::CrasAudioHandler;
 using chromeos::DBusThreadManager;
 using system::TrayAudioDelegate;
 using system::TrayAudioDelegateChromeOs;
@@ -29,7 +32,8 @@
       volume_view_(nullptr),
       pop_up_volume_view_(false),
       audio_detail_view_(nullptr) {
-  WmShell::Get()->system_tray_notifier()->AddAudioObserver(this);
+  if (CrasAudioHandler::IsInitialized())
+    CrasAudioHandler::Get()->AddAudioObserver(this);
   display::Screen::GetScreen()->AddObserver(this);
   DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
 }
@@ -37,7 +41,21 @@
 TrayAudio::~TrayAudio() {
   DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
   display::Screen::GetScreen()->RemoveObserver(this);
-  WmShell::Get()->system_tray_notifier()->RemoveAudioObserver(this);
+  if (CrasAudioHandler::IsInitialized())
+    CrasAudioHandler::Get()->RemoveAudioObserver(this);
+}
+
+// static
+void TrayAudio::ShowPopUpVolumeView() {
+  // Show the popup on all monitors with a system tray.
+  for (WmWindow* root : WmShell::Get()->GetAllRootWindows()) {
+    SystemTray* system_tray = root->GetRootWindowController()->GetSystemTray();
+    if (!system_tray)
+      continue;
+    // Show the popup by simulating a volume change. The provided node id and
+    // volume value are ignored.
+    system_tray->GetTrayAudio()->OnOutputNodeVolumeChanged(0, 0);
+  }
 }
 
 bool TrayAudio::GetInitialVisibility() {
@@ -83,7 +101,7 @@
 }
 
 void TrayAudio::OnOutputNodeVolumeChanged(uint64_t /* node_id */,
-                                          double /* volume */) {
+                                          int /* volume */) {
   float percent =
       static_cast<float>(audio_delegate_->GetOutputVolumeLevel()) / 100.0f;
   if (tray_view())
diff --git a/ash/common/system/chromeos/audio/tray_audio.h b/ash/common/system/chromeos/audio/tray_audio.h
index a468f572..314b360 100644
--- a/ash/common/system/chromeos/audio/tray_audio.h
+++ b/ash/common/system/chromeos/audio/tray_audio.h
@@ -10,9 +10,9 @@
 #include <memory>
 
 #include "ash/ash_export.h"
-#include "ash/common/system/chromeos/audio/audio_observer.h"
 #include "ash/common/system/tray/tray_image_item.h"
 #include "base/macros.h"
+#include "chromeos/audio/cras_audio_handler.h"
 #include "chromeos/dbus/power_manager_client.h"
 #include "ui/display/display_observer.h"
 
@@ -29,13 +29,20 @@
 
 // The system tray item for audio input and output.
 class ASH_EXPORT TrayAudio : public TrayImageItem,
-                             public AudioObserver,
+                             public chromeos::CrasAudioHandler::AudioObserver,
                              public display::DisplayObserver,
                              public chromeos::PowerManagerClient::Observer {
  public:
   explicit TrayAudio(SystemTray* system_tray);
   ~TrayAudio() override;
 
+  // Temporarily shows the pop-up volume slider on all displays. Used by ARC++
+  // when an Android app changes the system volume.
+  static void ShowPopUpVolumeView();
+
+  tray::VolumeView* volume_view_for_testing() { return volume_view_; }
+  bool pop_up_volume_view_for_testing() { return pop_up_volume_view_; }
+
  private:
   // Overridden from display::DisplayObserver.
   void OnDisplayAdded(const display::Display& new_display) override;
@@ -54,8 +61,8 @@
   bool ShouldHideArrow() const override;
   bool ShouldShowShelf() const override;
 
-  // Overridden from AudioObserver.
-  void OnOutputNodeVolumeChanged(uint64_t node_id, double volume) override;
+  // Overridden from CrasAudioHandler::AudioObserver.
+  void OnOutputNodeVolumeChanged(uint64_t node_id, int volume) override;
   void OnOutputMuteChanged(bool mute_on, bool system_adjust) override;
   void OnAudioNodesChanged() override;
   void OnActiveOutputNodeChanged() override;
diff --git a/ash/common/system/chromeos/audio/tray_audio_unittest.cc b/ash/common/system/chromeos/audio/tray_audio_unittest.cc
new file mode 100644
index 0000000..e0a21ac
--- /dev/null
+++ b/ash/common/system/chromeos/audio/tray_audio_unittest.cc
@@ -0,0 +1,31 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/common/system/chromeos/audio/tray_audio.h"
+
+#include "ash/common/system/tray/system_tray.h"
+#include "ash/common/test/ash_test.h"
+
+namespace ash {
+
+using TrayAudioTest = AshTest;
+
+// Tests that the volume popup view can be explicitly shown.
+TEST_F(TrayAudioTest, ShowPopUpVolumeView) {
+  TrayAudio* tray_audio = GetPrimarySystemTray()->GetTrayAudio();
+  ASSERT_TRUE(tray_audio);
+
+  // The volume popup is not visible initially.
+  EXPECT_FALSE(tray_audio->volume_view_for_testing());
+  EXPECT_FALSE(tray_audio->pop_up_volume_view_for_testing());
+
+  // Simulate ARC asking to show the volume view.
+  TrayAudio::ShowPopUpVolumeView();
+
+  // Volume view is now visible.
+  EXPECT_TRUE(tray_audio->volume_view_for_testing());
+  EXPECT_TRUE(tray_audio->pop_up_volume_view_for_testing());
+}
+
+}  // namespace ash
diff --git a/ash/common/system/chromeos/audio/volume_view.cc b/ash/common/system/chromeos/audio/volume_view.cc
index 38d050b..c033b1c 100644
--- a/ash/common/system/chromeos/audio/volume_view.cc
+++ b/ash/common/system/chromeos/audio/volume_view.cc
@@ -182,6 +182,9 @@
   more_button_->SetFocusBehavior(FocusBehavior::NEVER);
 
   device_type_ = TrayPopupUtils::CreateMoreImageView();
+  more_button_->SetBorder(views::CreateEmptyBorder(
+      0, GetTrayConstant(TRAY_POPUP_ITEM_MORE_REGION_HORIZONTAL_INSET), 0,
+      GetTrayConstant(TRAY_POPUP_ITEM_MORE_REGION_HORIZONTAL_INSET)));
   more_button_->AddChildView(device_type_);
 
   views::ImageView* more_arrow = TrayPopupUtils::CreateMoreImageView();
diff --git a/ash/common/system/date/date_view.cc b/ash/common/system/date/date_view.cc
index 6f9d12a4..80dc31e 100644
--- a/ash/common/system/date/date_view.cc
+++ b/ash/common/system/date/date_view.cc
@@ -8,6 +8,7 @@
 #include "ash/common/system/tray/system_tray_controller.h"
 #include "ash/common/system/tray/tray_constants.h"
 #include "ash/common/system/tray/tray_popup_item_style.h"
+#include "ash/common/system/tray/tray_popup_utils.h"
 #include "ash/common/system/tray/tray_utils.h"
 #include "ash/common/wm_shell.h"
 #include "base/i18n/rtl.h"
@@ -54,6 +55,10 @@
 // vertically aligned.
 const int kClockLeadingPadding = 8;
 
+bool UseMd() {
+  return MaterialDesignController::IsSystemTrayMenuMaterial();
+}
+
 base::string16 FormatDateWithPattern(const base::Time& time,
                                      const char* pattern) {
   UErrorCode status = U_ZERO_ERROR;
@@ -74,7 +79,7 @@
 }
 
 base::string16 FormatDate(const base::Time& time) {
-  if (MaterialDesignController::IsSystemTrayMenuMaterial()) {
+  if (UseMd()) {
     // Use 'short' month format (e.g., "Oct") followed by non-padded day of
     // month (e.g., "2", "10").
     return FormatDateWithPattern(time, "LLLd");
@@ -161,22 +166,24 @@
 
 DateView::DateView(SystemTrayItem* owner)
     : BaseDateTimeView(owner), action_(DateAction::NONE) {
-  if (MaterialDesignController::IsSystemTrayMenuMaterial()) {
+  if (UseMd()) {
     // TODO(tdanderson): Tweak spacing and layout for material design.
     views::BoxLayout* box_layout =
-        new views::BoxLayout(views::BoxLayout::kHorizontal, 12, 0, 0);
+        new views::BoxLayout(views::BoxLayout::kHorizontal, 8, 0, 0);
     box_layout->set_main_axis_alignment(
         views::BoxLayout::MAIN_AXIS_ALIGNMENT_START);
     box_layout->set_cross_axis_alignment(
         views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
+    box_layout->set_minimum_cross_axis_size(
+        GetTrayConstant(TRAY_POPUP_ITEM_HEIGHT));
     SetLayoutManager(box_layout);
   } else {
     SetLayoutManager(
         new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
   }
-  date_label_ = new views::Label();
+  date_label_ = TrayPopupUtils::CreateDefaultLabel();
   date_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  if (!MaterialDesignController::IsSystemTrayMenuMaterial())
+  if (!UseMd())
     date_label_->SetEnabledColor(kHeaderTextColorNormal);
   UpdateTextInternal(base::Time::Now());
   AddChildView(date_label_);
@@ -187,8 +194,7 @@
 void DateView::SetAction(DateAction action) {
   if (action == action_)
     return;
-  if (IsMouseHovered() &&
-      !MaterialDesignController::IsSystemTrayMenuMaterial()) {
+  if (IsMouseHovered() && !UseMd()) {
     date_label_->SetEnabledColor(action == DateAction::NONE
                                      ? kHeaderTextColorNormal
                                      : kHeaderTextColorHover);
@@ -201,9 +207,11 @@
   // Disable |this| when not clickable so that the material design ripple is
   // not shown.
   // TODO(tdanderson|bruthig): Add the material design ripple to |this|.
-  if (MaterialDesignController::IsSystemTrayMenuMaterial()) {
+  if (UseMd()) {
     SetState(action_ == DateAction::NONE ? views::Button::STATE_DISABLED
                                          : views::Button::STATE_NORMAL);
+    if (action_ != DateAction::NONE)
+      SetInkDropMode(views::InkDropHostView::InkDropMode::ON);
   }
 }
 
@@ -217,7 +225,7 @@
 }
 
 void DateView::SetActive(bool active) {
-  if (MaterialDesignController::IsSystemTrayMenuMaterial())
+  if (UseMd())
     return;
 
   date_label_->SetEnabledColor(active ? kHeaderTextColorHover
diff --git a/ash/common/system/tray/fixed_sized_scroll_view.cc b/ash/common/system/tray/fixed_sized_scroll_view.cc
index 4bc8922..52bf75a 100644
--- a/ash/common/system/tray/fixed_sized_scroll_view.cc
+++ b/ash/common/system/tray/fixed_sized_scroll_view.cc
@@ -28,7 +28,8 @@
 
 void FixedSizedScrollView::SetContentsView(views::View* view) {
   SetContents(view);
-  view->SetBoundsRect(gfx::Rect(view->GetPreferredSize()));
+  if (!UseMd())
+    view->SetBoundsRect(gfx::Rect(view->GetPreferredSize()));
 }
 
 void FixedSizedScrollView::SetFixedSize(const gfx::Size& size) {
@@ -45,6 +46,9 @@
 }
 
 gfx::Size FixedSizedScrollView::GetPreferredSize() const {
+  if (UseMd())
+    return views::View::GetPreferredSize();
+
   gfx::Size size =
       fixed_size_.IsEmpty() ? contents()->GetPreferredSize() : fixed_size_;
   gfx::Insets insets = GetInsets();
@@ -53,10 +57,8 @@
 }
 
 void FixedSizedScrollView::Layout() {
-  if (UseMd()) {
-    views::ScrollView::Layout();
-    return;
-  }
+  if (UseMd())
+    return views::ScrollView::Layout();
 
   gfx::Rect bounds = gfx::Rect(contents()->GetPreferredSize());
   bounds.set_width(std::max(0, width() - GetScrollBarWidth()));
diff --git a/ash/common/system/tray/system_tray_notifier.cc b/ash/common/system/tray/system_tray_notifier.cc
index 0c9ee28..e530b8c 100644
--- a/ash/common/system/tray/system_tray_notifier.cc
+++ b/ash/common/system/tray/system_tray_notifier.cc
@@ -11,7 +11,6 @@
 #include "ash/common/system/user/user_observer.h"
 
 #if defined(OS_CHROMEOS)
-#include "ash/common/system/chromeos/audio/audio_observer.h"
 #include "ash/common/system/chromeos/bluetooth/bluetooth_observer.h"
 #include "ash/common/system/chromeos/enterprise/enterprise_domain_observer.h"
 #include "ash/common/system/chromeos/media_security/media_capture_observer.h"
@@ -129,41 +128,6 @@
 
 #if defined(OS_CHROMEOS)
 
-void SystemTrayNotifier::AddAudioObserver(AudioObserver* observer) {
-  audio_observers_.AddObserver(observer);
-}
-
-void SystemTrayNotifier::RemoveAudioObserver(AudioObserver* observer) {
-  audio_observers_.RemoveObserver(observer);
-}
-
-void SystemTrayNotifier::NotifyAudioOutputVolumeChanged(uint64_t node_id,
-                                                        double volume) {
-  for (auto& observer : audio_observers_)
-    observer.OnOutputNodeVolumeChanged(node_id, volume);
-}
-
-void SystemTrayNotifier::NotifyAudioOutputMuteChanged(bool mute_on,
-                                                      bool system_adjust) {
-  for (auto& observer : audio_observers_)
-    observer.OnOutputMuteChanged(mute_on, system_adjust);
-}
-
-void SystemTrayNotifier::NotifyAudioNodesChanged() {
-  for (auto& observer : audio_observers_)
-    observer.OnAudioNodesChanged();
-}
-
-void SystemTrayNotifier::NotifyAudioActiveOutputNodeChanged() {
-  for (auto& observer : audio_observers_)
-    observer.OnActiveOutputNodeChanged();
-}
-
-void SystemTrayNotifier::NotifyAudioActiveInputNodeChanged() {
-  for (auto& observer : audio_observers_)
-    observer.OnActiveInputNodeChanged();
-}
-
 void SystemTrayNotifier::AddBluetoothObserver(BluetoothObserver* observer) {
   bluetooth_observers_.AddObserver(observer);
 }
diff --git a/ash/common/system/tray/system_tray_notifier.h b/ash/common/system/tray/system_tray_notifier.h
index 8b6e4ec63..7b1ccb6 100644
--- a/ash/common/system/tray/system_tray_notifier.h
+++ b/ash/common/system/tray/system_tray_notifier.h
@@ -27,7 +27,6 @@
 class UserObserver;
 
 #if defined(OS_CHROMEOS)
-class AudioObserver;
 class BluetoothObserver;
 class EnterpriseDomainObserver;
 class LastWindowClosedObserver;
@@ -80,15 +79,6 @@
   void NotifyUserAddedToSession();
 
 #if defined(OS_CHROMEOS)
-  // Audio.
-  void AddAudioObserver(AudioObserver* observer);
-  void RemoveAudioObserver(AudioObserver* observer);
-  void NotifyAudioOutputVolumeChanged(uint64_t node_id, double volume);
-  void NotifyAudioOutputMuteChanged(bool mute_on, bool system_adjust);
-  void NotifyAudioNodesChanged();
-  void NotifyAudioActiveOutputNodeChanged();
-  void NotifyAudioActiveInputNodeChanged();
-
   // Bluetooth.
   void AddBluetoothObserver(BluetoothObserver* observer);
   void RemoveBluetoothObserver(BluetoothObserver* observer);
@@ -167,7 +157,6 @@
   base::ObserverList<UserObserver> user_observers_;
 
 #if defined(OS_CHROMEOS)
-  base::ObserverList<AudioObserver> audio_observers_;
   base::ObserverList<BluetoothObserver> bluetooth_observers_;
   base::ObserverList<EnterpriseDomainObserver> enterprise_domain_observers_;
   base::ObserverList<LastWindowClosedObserver> last_window_closed_observers_;
diff --git a/ash/common/system/tray/tray_details_view.cc b/ash/common/system/tray/tray_details_view.cc
index 80617af..9ca7faf 100644
--- a/ash/common/system/tray/tray_details_view.cc
+++ b/ash/common/system/tray/tray_details_view.cc
@@ -258,13 +258,14 @@
 
 TrayDetailsView::TrayDetailsView(SystemTrayItem* owner)
     : owner_(owner),
+      box_layout_(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0)),
       title_row_(nullptr),
       scroller_(nullptr),
       scroll_content_(nullptr),
       progress_bar_(nullptr),
       scroll_border_(nullptr),
       back_button_(nullptr) {
-  SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
+  SetLayoutManager(box_layout_);
   set_background(views::Background::CreateSolidBackground(kBackgroundColor));
 }
 
@@ -321,12 +322,23 @@
   scroll_content_ = new ScrollContentsView();
   scroller_ = new FixedSizedScrollView;
   scroller_->SetContentsView(scroll_content_);
+  // Make the |scroller_| have a layer to clip |scroll_content_|'s children.
+  // TODO(varkha): Make the sticky rows work with EnableViewPortLayer().
+  scroller_->SetPaintToLayer(true);
+  scroller_->set_background(
+      views::Background::CreateSolidBackground(kBackgroundColor));
+  scroller_->layer()->SetMasksToBounds(true);
 
   // Note: |scroller_| takes ownership of |scroll_border_|.
-  scroll_border_ = new ScrollBorder;
-  scroller_->SetBorder(std::unique_ptr<views::Border>(scroll_border_));
+  if (!UseMd()) {
+    // In MD, the scroller is always the last thing, so this border is
+    // unnecessary and reserves extra space we don't want.
+    scroll_border_ = new ScrollBorder;
+    scroller_->SetBorder(std::unique_ptr<views::Border>(scroll_border_));
+  }
 
   AddChildView(scroller_);
+  box_layout_->SetFlexForView(scroller_, 1);
 }
 
 void TrayDetailsView::AddScrollSeparator() {
@@ -389,34 +401,33 @@
 }
 
 void TrayDetailsView::Layout() {
-  if (bounds().IsEmpty()) {
+  if (UseMd()) {
+    views::View::Layout();
+    if (scroller_ && !scroller_->is_bounded())
+      scroller_->ClipHeightTo(0, scroller_->height());
+  }
+
+  if (UseMd() || bounds().IsEmpty()) {
     views::View::Layout();
     return;
   }
 
   if (scroller_) {
-    if (UseMd()) {
-      gfx::Size scroller_size = scroller()->GetPreferredSize();
-      gfx::Size pref_size = GetPreferredSize();
-      scroller()->ClipHeightTo(
-          0, scroller_size.height() - (pref_size.height() - height()));
-    } else {
-      scroller_->set_fixed_size(gfx::Size());
-      gfx::Size size = GetPreferredSize();
+    scroller_->set_fixed_size(gfx::Size());
+    gfx::Size size = GetPreferredSize();
 
-      // Set the scroller to fill the space above the bottom row, so that the
-      // bottom row of the detailed view will always stay just above the title
-      // row.
-      gfx::Size scroller_size = scroll_content_->GetPreferredSize();
-      scroller_->set_fixed_size(
-          gfx::Size(width() + scroller_->GetScrollBarWidth(),
-                    scroller_size.height() - (size.height() - height())));
-    }
+    // Set the scroller to fill the space above the bottom row, so that the
+    // bottom row of the detailed view will always stay just above the title
+    // row.
+    gfx::Size scroller_size = scroll_content_->GetPreferredSize();
+    scroller_->set_fixed_size(
+        gfx::Size(width() + scroller_->GetScrollBarWidth(),
+                  scroller_size.height() - (size.height() - height())));
   }
 
   views::View::Layout();
 
-  if (title_row_ && !UseMd()) {
+  if (title_row_) {
     // Always make sure the title row is bottom-aligned in non-MD.
     gfx::Rect fbounds = title_row_->bounds();
     fbounds.set_y(height() - title_row_->height());
@@ -424,6 +435,16 @@
   }
 }
 
+int TrayDetailsView::GetHeightForWidth(int width) const {
+  if (!UseMd() || bounds().IsEmpty())
+    return views::View::GetHeightForWidth(width);
+
+  // The height of the bubble that contains this detailed view is set to
+  // the preferred height of the default view, and that determines the
+  // initial height of |this|. Always request to stay the same height.
+  return height();
+}
+
 void TrayDetailsView::OnPaintBorder(gfx::Canvas* canvas) {
   if (scroll_border_) {
     int index = GetIndexOf(scroller_);
diff --git a/ash/common/system/tray/tray_details_view.h b/ash/common/system/tray/tray_details_view.h
index 98b2614d..b1560053 100644
--- a/ash/common/system/tray/tray_details_view.h
+++ b/ash/common/system/tray/tray_details_view.h
@@ -13,6 +13,7 @@
 #include "ui/views/view.h"
 
 namespace views {
+class BoxLayout;
 class ScrollView;
 class ProgressBar;
 }  // namespace views
@@ -49,6 +50,7 @@
  protected:
   // views::View:
   void Layout() override;
+  int GetHeightForWidth(int width) const override;
   void OnPaintBorder(gfx::Canvas* canvas) override;
 
   // Creates the row containing the back button and title. For material design
@@ -90,6 +92,7 @@
   void TransitionToDefaultView();
 
   SystemTrayItem* owner_;
+  views::BoxLayout* box_layout_;
   SpecialPopupRow* title_row_;
   FixedSizedScrollView* scroller_;
   views::View* scroll_content_;
diff --git a/ash/common/system/tray/tray_popup_utils.cc b/ash/common/system/tray/tray_popup_utils.cc
index 3864fa8..30dda06b 100644
--- a/ash/common/system/tray/tray_popup_utils.cc
+++ b/ash/common/system/tray/tray_popup_utils.cc
@@ -32,25 +32,60 @@
 // stretched horizontally and centered vertically.
 std::unique_ptr<views::LayoutManager> CreateDefaultCenterLayoutManager() {
   // TODO(bruthig): Use constants instead of magic numbers.
-  views::BoxLayout* box_layout =
-      new views::BoxLayout(views::BoxLayout::kVertical, 4, 8, 4);
+  auto box_layout =
+      base::MakeUnique<views::BoxLayout>(views::BoxLayout::kVertical, 4, 8, 4);
   box_layout->set_main_axis_alignment(
       views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
   box_layout->set_cross_axis_alignment(
       views::BoxLayout::CROSS_AXIS_ALIGNMENT_STRETCH);
-  return std::unique_ptr<views::LayoutManager>(box_layout);
+  return std::move(box_layout);
 }
 
 // Creates a layout manager that positions Views horizontally. The Views will be
 // centered along the horizontal and vertical axis.
 std::unique_ptr<views::LayoutManager> CreateDefaultEndsLayoutManager() {
-  views::BoxLayout* box_layout =
-      new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0);
+  auto box_layout = base::MakeUnique<views::BoxLayout>(
+      views::BoxLayout::kHorizontal, 0, 0, 0);
   box_layout->set_main_axis_alignment(
       views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
   box_layout->set_cross_axis_alignment(
-      views::BoxLayout::CROSS_AXIS_ALIGNMENT_STRETCH);
-  return std::unique_ptr<views::LayoutManager>(box_layout);
+      views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
+  return std::move(box_layout);
+}
+
+std::unique_ptr<views::LayoutManager> CreateDefaultLayoutManager(
+    TriView::Container container) {
+  switch (container) {
+    case TriView::Container::START:
+    case TriView::Container::END:
+      return CreateDefaultEndsLayoutManager();
+    case TriView::Container::CENTER:
+      return CreateDefaultCenterLayoutManager();
+  }
+  // Required by some compilers.
+  NOTREACHED();
+  return nullptr;
+}
+
+// Configures the default size and flex value for the specified |container|
+// of the given |tri_view|. Used by CreateDefaultRowView().
+void ConfigureDefaultSizeAndFlex(TriView* tri_view,
+                                 TriView::Container container) {
+  int min_width = 0;
+  switch (container) {
+    case TriView::Container::START:
+      min_width = GetTrayConstant(TRAY_POPUP_ITEM_MIN_START_WIDTH);
+      break;
+    case TriView::Container::CENTER:
+      tri_view->SetFlexForContainer(TriView::Container::CENTER, 1.f);
+      break;
+    case TriView::Container::END:
+      min_width = GetTrayConstant(TRAY_POPUP_ITEM_MIN_END_WIDTH);
+      break;
+  }
+
+  tri_view->SetMinSize(
+      container, gfx::Size(min_width, GetTrayConstant(TRAY_POPUP_ITEM_HEIGHT)));
 }
 
 class BorderlessLabelButton : public views::LabelButton {
@@ -102,12 +137,6 @@
 
 TriView* TrayPopupUtils::CreateDefaultRowView() {
   TriView* tri_view = CreateMultiTargetRowView();
-  tri_view->SetContainerBorder(TriView::Container::START,
-                               CreateDefaultBorder(TriView::Container::START));
-  tri_view->SetContainerBorder(TriView::Container::CENTER,
-                               CreateDefaultBorder(TriView::Container::CENTER));
-  tri_view->SetContainerBorder(TriView::Container::END,
-                               CreateDefaultBorder(TriView::Container::END));
 
   tri_view->SetContainerLayout(
       TriView::Container::START,
@@ -128,7 +157,6 @@
   tri_view->SetInsets(
       gfx::Insets(0, GetTrayConstant(TRAY_POPUP_ITEM_LEFT_INSET), 0,
                   GetTrayConstant(TRAY_POPUP_ITEM_RIGHT_INSET)));
-  tri_view->SetMinCrossAxisSize(GetTrayConstant(TRAY_POPUP_ITEM_HEIGHT));
 
   ConfigureDefaultSizeAndFlex(tri_view, TriView::Container::START);
   ConfigureDefaultSizeAndFlex(tri_view, TriView::Container::CENTER);
@@ -147,9 +175,6 @@
 views::Label* TrayPopupUtils::CreateDefaultLabel() {
   views::Label* label = new views::Label();
   label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  label->SetBorder(views::CreateEmptyBorder(0, kTrayPopupLabelHorizontalPadding,
-                                            0,
-                                            kTrayPopupLabelHorizontalPadding));
 
   // TODO(bruthig): Fix this so that |label| uses the kBackgroundColor to
   // perform subpixel rendering instead of disabling subpixel rendering.
@@ -192,30 +217,10 @@
 
 void TrayPopupUtils::ConfigureContainer(TriView::Container container,
                                         views::View* container_view) {
-  container_view->SetBorder(CreateDefaultBorder(container));
   container_view->SetLayoutManager(
       CreateDefaultLayoutManager(container).release());
 }
 
-void TrayPopupUtils::ConfigureDefaultSizeAndFlex(TriView* tri_view,
-                                                 TriView::Container container) {
-  switch (container) {
-    case TriView::Container::START:
-      tri_view->SetMinSize(
-          TriView::Container::START,
-          gfx::Size(GetTrayConstant(TRAY_POPUP_ITEM_MIN_START_WIDTH), 0));
-      break;
-    case TriView::Container::CENTER:
-      tri_view->SetFlexForContainer(TriView::Container::CENTER, 1.f);
-      break;
-    case TriView::Container::END:
-      tri_view->SetMinSize(
-          TriView::Container::END,
-          gfx::Size(GetTrayConstant(TRAY_POPUP_ITEM_MIN_END_WIDTH), 0));
-      break;
-  }
-}
-
 views::LabelButton* TrayPopupUtils::CreateTrayPopupBorderlessButton(
     views::ButtonListener* listener,
     const base::string16& text) {
@@ -250,40 +255,4 @@
          !WmShell::Get()->GetSessionStateDelegate()->IsInSecondaryLoginScreen();
 }
 
-std::unique_ptr<views::LayoutManager>
-TrayPopupUtils::CreateDefaultLayoutManager(TriView::Container container) {
-  switch (container) {
-    case TriView::Container::START:
-    case TriView::Container::END:
-      return CreateDefaultEndsLayoutManager();
-    case TriView::Container::CENTER:
-      return CreateDefaultCenterLayoutManager();
-  }
-  // Required by some compilers.
-  NOTREACHED();
-  return nullptr;
-}
-
-std::unique_ptr<views::Border> TrayPopupUtils::CreateDefaultBorder(
-    TriView::Container container) {
-  switch (container) {
-    case TriView::Container::START:
-      // TODO(bruthig): Update the 'Main' images to have a fixed size that is
-      // just the painted size and add a border.
-      return nullptr;
-      break;
-    case TriView::Container::CENTER:
-      return nullptr;
-      break;
-    case TriView::Container::END:
-      return views::CreateEmptyBorder(
-          0, GetTrayConstant(TRAY_POPUP_ITEM_MORE_REGION_HORIZONTAL_INSET), 0,
-          GetTrayConstant(TRAY_POPUP_ITEM_MORE_REGION_HORIZONTAL_INSET));
-      break;
-  }
-  // Required by some compilers.
-  NOTREACHED();
-  return nullptr;
-}
-
 }  // namespace ash
diff --git a/ash/common/system/tray/tray_popup_utils.h b/ash/common/system/tray/tray_popup_utils.h
index 7d0d572..a48cc78 100644
--- a/ash/common/system/tray/tray_popup_utils.h
+++ b/ash/common/system/tray/tray_popup_utils.h
@@ -116,23 +116,7 @@
   static bool CanOpenWebUISettings(LoginStatus status);
 
  private:
-  TrayPopupUtils() = delete;
-  ~TrayPopupUtils() = delete;
-
-  // Configures the default size and flex value for the specified |container|
-  // of the given |tri_view|. Used by CreateDefaultRowView().
-  static void ConfigureDefaultSizeAndFlex(TriView* tri_view,
-                                          TriView::Container container);
-
-  // Returns the default layout manager used by CreateDefaultRowView() and
-  // ConfigureContainer() for the given |container|.
-  static std::unique_ptr<views::LayoutManager> CreateDefaultLayoutManager(
-      TriView::Container container);
-
-  // Returns the default border used by CreateDefaultRow() and
-  // ConfigureContainer() for the given |container|.
-  static std::unique_ptr<views::Border> CreateDefaultBorder(
-      TriView::Container container);
+  DISALLOW_IMPLICIT_CONSTRUCTORS(TrayPopupUtils);
 };
 
 }  // namespace ash
diff --git a/ash/common/system/tray/tri_view.cc b/ash/common/system/tray/tri_view.cc
index b474267..8f557682 100644
--- a/ash/common/system/tray/tri_view.cc
+++ b/ash/common/system/tray/tri_view.cc
@@ -55,13 +55,8 @@
   GetContainer(Container::END)
       ->SetLayoutManager(GetLayoutManager(Container::END));
 
-  GetLayoutManager(Container::START)
-      ->SetLayoutManager(CreateDefaultLayoutManager(orientation));
-  GetLayoutManager(Container::CENTER)
-      ->SetLayoutManager(CreateDefaultLayoutManager(orientation));
-  GetLayoutManager(Container::END)
-      ->SetLayoutManager(CreateDefaultLayoutManager(orientation));
-
+  box_layout_->set_cross_axis_alignment(
+      views::BoxLayout::CROSS_AXIS_ALIGNMENT_START);
   SetLayoutManager(box_layout_);
 
   enable_hierarchy_changed_dcheck_ = true;
@@ -71,10 +66,6 @@
   enable_hierarchy_changed_dcheck_ = false;
 }
 
-void TriView::SetMinCrossAxisSize(int min_size) {
-  box_layout_->set_minimum_cross_axis_size(min_size);
-}
-
 void TriView::SetMinSize(Container container, const gfx::Size& size) {
   GetLayoutManager(container)->SetMinSize(size);
 }
@@ -131,22 +122,11 @@
   }
 }
 
-std::unique_ptr<views::LayoutManager> TriView::CreateDefaultLayoutManager(
-    Orientation orientation) const {
-  views::BoxLayout* box_layout =
-      new views::BoxLayout(GetOrientation(orientation), 0, 0, 0);
-  box_layout->set_main_axis_alignment(
-      views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
-  box_layout->set_cross_axis_alignment(
-      views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
-  return std::unique_ptr<views::LayoutManager>(box_layout);
+views::View* TriView::GetContainer(Container container) {
+  return child_at(static_cast<int>(container));
 }
 
-views::View* TriView::GetContainer(Container container) const {
-  return const_cast<views::View*>(child_at(static_cast<int>(container)));
-}
-
-SizeRangeLayout* TriView::GetLayoutManager(Container container) const {
+SizeRangeLayout* TriView::GetLayoutManager(Container container) {
   switch (container) {
     case Container::START:
       return start_container_layout_manager_;
diff --git a/ash/common/system/tray/tri_view.h b/ash/common/system/tray/tri_view.h
index 79851c5..5bb6ed6e 100644
--- a/ash/common/system/tray/tri_view.h
+++ b/ash/common/system/tray/tri_view.h
@@ -70,10 +70,6 @@
 
   ~TriView() override;
 
-  // Set the minimum cross axis size, i.e. the minimum height for a horizontal
-  // orientation.
-  void SetMinCrossAxisSize(int min_size);
-
   // Set the minimum size for the given |container|.
   void SetMinSize(Container container, const gfx::Size& size);
 
@@ -126,15 +122,11 @@
  private:
   friend class TriViewTest;
 
-  // Creates a default LayoutManager for the given |orientation|.
-  std::unique_ptr<views::LayoutManager> CreateDefaultLayoutManager(
-      Orientation orientation) const;
-
   // Returns the View for the given |container|.
-  views::View* GetContainer(Container container) const;
+  views::View* GetContainer(Container container);
 
   // Returns the layout manager for the given |container|.
-  SizeRangeLayout* GetLayoutManager(Container container) const;
+  SizeRangeLayout* GetLayoutManager(Container container);
 
   // Type spcific layout manager installed on |this|. Responsible for laying out
   // the container Views.
diff --git a/ash/common/system/tray/tri_view_unittest.cc b/ash/common/system/tray/tri_view_unittest.cc
index 95c9dd5..215add6e 100644
--- a/ash/common/system/tray/tri_view_unittest.cc
+++ b/ash/common/system/tray/tri_view_unittest.cc
@@ -98,14 +98,6 @@
   EXPECT_EQ(kViewWidth * 2, GetBoundsInHost(end_child).y());
 }
 
-TEST_F(TriViewTest, MinCrossAxisSize) {
-  const int kMinCrossAxisSize = 15;
-  EXPECT_EQ(0, tri_view_->GetPreferredSize().height());
-  tri_view_->SetMinCrossAxisSize(kMinCrossAxisSize);
-  EXPECT_EQ(kMinCrossAxisSize, tri_view_->GetPreferredSize().height());
-  EXPECT_EQ(kMinCrossAxisSize, tri_view_->GetHeightForWidth(0));
-}
-
 TEST_F(TriViewTest, MainAxisMinSize) {
   tri_view_->SetBounds(0, 0, 100, 10);
   const gfx::Size kMinSize(15, 10);
diff --git a/ash/common/wm_root_window_controller.cc b/ash/common/wm_root_window_controller.cc
index 4b5048e..d4f1be97 100644
--- a/ash/common/wm_root_window_controller.cc
+++ b/ash/common/wm_root_window_controller.cc
@@ -224,6 +224,13 @@
   shelf->shelf_widget()->status_area_widget()->Show();
 }
 
+SystemTray* WmRootWindowController::GetSystemTray() {
+  ShelfWidget* shelf_widget = GetShelf()->shelf_widget();
+  if (!shelf_widget || !shelf_widget->status_area_widget())
+    return nullptr;
+  return shelf_widget->status_area_widget()->system_tray();
+}
+
 WmWindow* WmRootWindowController::GetContainer(int container_id) {
   return root_->GetChildByShellWindowId(container_id);
 }
diff --git a/ash/common/wm_root_window_controller.h b/ash/common/wm_root_window_controller.h
index 43eca2e..b0f75c78 100644
--- a/ash/common/wm_root_window_controller.h
+++ b/ash/common/wm_root_window_controller.h
@@ -31,6 +31,7 @@
 class DockedWindowLayoutManager;
 class PanelLayoutManager;
 class SystemModalContainerLayoutManager;
+class SystemTray;
 class WallpaperWidgetController;
 class WmShelf;
 class WmShell;
@@ -103,6 +104,9 @@
   // TODO(jamescook): Eliminate this and handle show via Shelf.
   void ShowShelf();
 
+  // Returns the system tray controller. May be null for external displays.
+  SystemTray* GetSystemTray();
+
   // Returns the window associated with this WmRootWindowController.
   virtual WmWindow* GetWindow() = 0;
 
diff --git a/ash/mus/app_launch_unittest.cc b/ash/mus/app_launch_unittest.cc
index 3f30b75..6f175ee 100644
--- a/ash/mus/app_launch_unittest.cc
+++ b/ash/mus/app_launch_unittest.cc
@@ -18,7 +18,7 @@
 
 class AppLaunchTest : public service_manager::test::ServiceTest {
  public:
-  AppLaunchTest() : ServiceTest("exe:mash_unittests") {}
+  AppLaunchTest() : ServiceTest("service:mash_unittests") {}
   ~AppLaunchTest() override {}
 
  private:
diff --git a/ash/mus/window_manager_unittest.cc b/ash/mus/window_manager_unittest.cc
index cb233f3..c868b3bf 100644
--- a/ash/mus/window_manager_unittest.cc
+++ b/ash/mus/window_manager_unittest.cc
@@ -34,7 +34,7 @@
 class WindowManagerTest : public service_manager::test::ServiceTest {
  public:
   WindowManagerTest()
-      : service_manager::test::ServiceTest("exe:mash_unittests") {}
+      : service_manager::test::ServiceTest("service:mash_unittests") {}
   ~WindowManagerTest() override {}
 
  private:
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index abcd84c1d..1118fda 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -1957,6 +1957,12 @@
     keyboard::SetAccessibilityKeyboardEnabled(true);
   }
 
+  // test::AshTestBase:
+  void TearDown() override {
+    keyboard::SetAccessibilityKeyboardEnabled(false);
+    test::AshTestBase::TearDown();
+  }
+
   void InitKeyboardBounds() {
     gfx::Rect work_area(
         display::Screen::GetScreen()->GetPrimaryDisplay().work_area());
diff --git a/base/BUILD.gn b/base/BUILD.gn
index f3886c4..47ee8fa 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -936,6 +936,8 @@
     "trace_event/memory_dump_session_state.h",
     "trace_event/memory_infra_background_whitelist.cc",
     "trace_event/memory_infra_background_whitelist.h",
+    "trace_event/memory_usage_estimator.cc",
+    "trace_event/memory_usage_estimator.h",
     "trace_event/process_memory_dump.cc",
     "trace_event/process_memory_dump.h",
     "trace_event/process_memory_maps.cc",
@@ -1989,6 +1991,7 @@
     "trace_event/java_heap_dump_provider_android_unittest.cc",
     "trace_event/memory_allocator_dump_unittest.cc",
     "trace_event/memory_dump_manager_unittest.cc",
+    "trace_event/memory_usage_estimator_unittest.cc",
     "trace_event/process_memory_dump_unittest.cc",
     "trace_event/trace_category_unittest.cc",
     "trace_event/trace_config_unittest.cc",
diff --git a/base/files/file_path.cc b/base/files/file_path.cc
index 0b06493..cff862ae 100644
--- a/base/files/file_path.cc
+++ b/base/files/file_path.cc
@@ -560,6 +560,12 @@
 }
 
 bool FilePath::ReferencesParent() const {
+  if (path_.find(kParentDirectory) == StringType::npos) {
+    // GetComponents is quite expensive, so avoid calling it in the majority
+    // of cases where there isn't a kParentDirectory anywhere in the path.
+    return false;
+  }
+
   std::vector<StringType> components;
   GetComponents(&components);
 
diff --git a/base/trace_event/memory_usage_estimator.cc b/base/trace_event/memory_usage_estimator.cc
new file mode 100644
index 0000000..c769d5b
--- /dev/null
+++ b/base/trace_event/memory_usage_estimator.cc
@@ -0,0 +1,14 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/memory_usage_estimator.h"
+
+namespace base {
+namespace trace_event {
+
+template size_t EstimateMemoryUsage(const std::string&);
+template size_t EstimateMemoryUsage(const string16&);
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/memory_usage_estimator.h b/base/trace_event/memory_usage_estimator.h
new file mode 100644
index 0000000..c089b0e
--- /dev/null
+++ b/base/trace_event/memory_usage_estimator.h
@@ -0,0 +1,418 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
+#define BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
+
+#include <stdint.h>
+
+#include <array>
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/strings/string16.h"
+#include "base/template_util.h"
+
+// Composable memory usage estimators.
+//
+// This file defines set of EstimateMemoryUsage(object) functions that return
+// approximate memory usage of their argument.
+//
+// The ultimate goal is to make memory usage estimation for a class simply a
+// matter of aggregating EstimateMemoryUsage() results over all fields.
+//
+// That is achieved via composability: if EstimateMemoryUsage() is defined
+// for T then EstimateMemoryUsage() is also defined for any combination of
+// containers holding T (e.g. std::map<int, std::vector<T>>).
+//
+// There are two ways of defining EstimateMemoryUsage() for a type:
+//
+// 1. As a global function 'size_t EstimateMemoryUsage(T)' in
+//    in base::trace_event namespace.
+//
+// 2. As 'size_t T::EstimateMemoryUsage() const' method. In this case
+//    EstimateMemoryUsage(T) function in base::trace_event namespace is
+//    provided automatically.
+//
+// Here is an example implementation:
+//
+// size_t foo::bar::MyClass::EstimateMemoryUsage() const {
+//   return base::trace_event::EstimateMemoryUsage(name_) +
+//          base::trace_event::EstimateMemoryUsage(id_) +
+//          base::trace_event::EstimateMemoryUsage(items_);
+// }
+//
+// The approach is simple: first call EstimateMemoryUsage() on all members,
+// then recursively fix compilation errors that are caused by types not
+// implementing EstimateMemoryUsage().
+
+namespace base {
+namespace trace_event {
+
+// Declarations
+
+// If T declares 'EstimateMemoryUsage() const' member function, then
+// global function EstimateMemoryUsage(T) is available, and just calls
+// the member function.
+template <class T>
+auto EstimateMemoryUsage(const T& object)
+    -> decltype(object.EstimateMemoryUsage());
+
+// String
+
+template <class C, class T, class A>
+size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string);
+
+// Arrays
+
+template <class T, size_t N>
+size_t EstimateMemoryUsage(const std::array<T, N>& array);
+
+template <class T, size_t N>
+size_t EstimateMemoryUsage(T (&array)[N]);
+
+template <class T>
+size_t EstimateMemoryUsage(const T* array, size_t array_length);
+
+// std::unique_ptr
+
+template <class T, class D>
+size_t EstimateMemoryUsage(const std::unique_ptr<T, D>& ptr);
+
+template <class T, class D>
+size_t EstimateMemoryUsage(const std::unique_ptr<T[], D>& array,
+                           size_t array_length);
+
+// Containers
+
+template <class F, class S>
+size_t EstimateMemoryUsage(const std::pair<F, S>& pair);
+
+template <class T, class A>
+size_t EstimateMemoryUsage(const std::vector<T, A>& vector);
+
+template <class T, class A>
+size_t EstimateMemoryUsage(const std::list<T, A>& list);
+
+template <class T, class C, class A>
+size_t EstimateMemoryUsage(const std::set<T, C, A>& set);
+
+template <class T, class C, class A>
+size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set);
+
+template <class K, class V, class C, class A>
+size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map);
+
+template <class K, class V, class C, class A>
+size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map);
+
+template <class T, class H, class KE, class A>
+size_t EstimateMemoryUsage(const std::unordered_set<T, H, KE, A>& set);
+
+template <class T, class H, class KE, class A>
+size_t EstimateMemoryUsage(const std::unordered_multiset<T, H, KE, A>& set);
+
+template <class K, class V, class H, class KE, class A>
+size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map);
+
+template <class K, class V, class H, class KE, class A>
+size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map);
+
+// TODO(dskiba):
+//   std::forward_list
+//   std::deque
+//   std::queue
+//   std::stack
+//   std::queue
+//   std::priority_queue
+
+// Definitions
+
+namespace internal {
+
+// HasEMU<T>::value is true iff EstimateMemoryUsage(T) is available.
+// (This is the default version, which is false.)
+template <class T, class X = void>
+struct HasEMU : std::false_type {};
+
+// This HasEMU specialization is only picked up if there exists function
+// EstimateMemoryUsage(const T&) that returns size_t. Simpler ways to
+// achieve this don't work on MSVC.
+template <class T>
+struct HasEMU<
+    T,
+    typename std::enable_if<std::is_same<
+        size_t,
+        decltype(EstimateMemoryUsage(std::declval<const T&>()))>::value>::type>
+    : std::true_type {};
+
+// EMUCaller<T> does three things:
+// 1. Defines Call() method that calls EstimateMemoryUsage(T) if it's
+//    available.
+// 2. If EstimateMemoryUsage(T) is not available, but T has trivial dtor
+//    (i.e. it's POD, integer, pointer, enum, etc.) then it defines Call()
+//    method that returns 0. This is useful for containers, which allocate
+//    memory regardless of T (also for cases like std::map<int, MyClass>).
+// 3. Finally, if EstimateMemoryUsage(T) is not available, then it triggers
+//    a static_assert with a helpful message. That cuts numbers of errors
+//    considerably - if you just call EstimateMemoryUsage(T) but it's not
+//    available for T, then compiler will helpfully list *all* possible
+//    variants of it, with an explanation for each.
+template <class T, class X = void>
+struct EMUCaller {
+  // std::is_same<> below makes static_assert depend on T, in order to
+  // prevent it from asserting regardless instantiation.
+  static_assert(std::is_same<T, std::false_type>::value,
+                "Neither global function 'size_t EstimateMemoryUsage(T)' "
+                "nor member function 'size_t T::EstimateMemoryUsage() const' "
+                "is defined for the type.");
+
+  static size_t Call(const T&) { return 0; }
+};
+
+template <class T>
+struct EMUCaller<T, typename std::enable_if<HasEMU<T>::value>::type> {
+  static size_t Call(const T& value) { return EstimateMemoryUsage(value); }
+};
+
+template <class T>
+struct EMUCaller<
+    T,
+    typename std::enable_if<!HasEMU<T>::value &&
+                            is_trivially_destructible<T>::value>::type> {
+  static size_t Call(const T& value) { return 0; }
+};
+
+}  // namespace internal
+
+// Proxy that deducts T and calls EMUCaller<T>.
+// To be used by EstimateMemoryUsage() implementations for containers.
+template <class T>
+size_t EstimateItemMemoryUsage(const T& value) {
+  return internal::EMUCaller<T>::Call(value);
+}
+
+template <class I>
+size_t EstimateIterableMemoryUsage(const I& iterable) {
+  size_t memory_usage = 0;
+  for (const auto& item : iterable) {
+    memory_usage += EstimateItemMemoryUsage(item);
+  }
+  return memory_usage;
+}
+
+// Global EstimateMemoryUsage(T) that just calls T::EstimateMemoryUsage().
+template <class T>
+auto EstimateMemoryUsage(const T& object)
+    -> decltype(object.EstimateMemoryUsage()) {
+  static_assert(
+      std::is_same<decltype(object.EstimateMemoryUsage()), size_t>::value,
+      "'T::EstimateMemoryUsage() const' must return size_t.");
+  return object.EstimateMemoryUsage();
+}
+
+// String
+
+template <class C, class T, class A>
+size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string) {
+  using string_type = std::basic_string<C, T, A>;
+  using value_type = typename string_type::value_type;
+  // C++11 doesn't leave much room for implementors - std::string can
+  // use short string optimization, but that's about it. We detect SSO
+  // by checking that c_str() points inside |string|.
+  const uint8_t* cstr = reinterpret_cast<const uint8_t*>(string.c_str());
+  const uint8_t* inline_cstr = reinterpret_cast<const uint8_t*>(&string);
+  if (cstr >= inline_cstr && cstr < inline_cstr + sizeof(string)) {
+    // SSO string
+    return 0;
+  }
+  return (string.capacity() + 1) * sizeof(value_type);
+}
+
+// Use explicit instantiations from the .cc file (reduces bloat).
+extern template BASE_EXPORT size_t EstimateMemoryUsage(const std::string&);
+extern template BASE_EXPORT size_t EstimateMemoryUsage(const string16&);
+
+// Arrays
+
+template <class T, size_t N>
+size_t EstimateMemoryUsage(const std::array<T, N>& array) {
+  return EstimateIterableMemoryUsage(array);
+}
+
+template <class T, size_t N>
+size_t EstimateMemoryUsage(T (&array)[N]) {
+  return EstimateIterableMemoryUsage(array);
+}
+
+template <class T>
+size_t EstimateMemoryUsage(const T* array, size_t array_length) {
+  size_t memory_usage = sizeof(T) * array_length;
+  for (size_t i = 0; i != array_length; ++i) {
+    memory_usage += EstimateItemMemoryUsage(array[i]);
+  }
+  return memory_usage;
+}
+
+// std::unique_ptr
+
+template <class T, class D>
+size_t EstimateMemoryUsage(const std::unique_ptr<T, D>& ptr) {
+  return ptr ? (sizeof(T) + EstimateItemMemoryUsage(*ptr)) : 0;
+}
+
+template <class T, class D>
+size_t EstimateMemoryUsage(const std::unique_ptr<T[], D>& array,
+                           size_t array_length) {
+  return EstimateMemoryUsage(array.get(), array_length);
+}
+
+// std::pair
+
+template <class F, class S>
+size_t EstimateMemoryUsage(const std::pair<F, S>& pair) {
+  return EstimateItemMemoryUsage(pair.first) +
+         EstimateItemMemoryUsage(pair.second);
+}
+
+// std::vector
+
+template <class T, class A>
+size_t EstimateMemoryUsage(const std::vector<T, A>& vector) {
+  return sizeof(T) * vector.capacity() + EstimateIterableMemoryUsage(vector);
+}
+
+// std::list
+
+template <class T, class A>
+size_t EstimateMemoryUsage(const std::list<T, A>& list) {
+  using value_type = typename std::list<T, A>::value_type;
+  struct Node {
+    Node* prev;
+    Node* next;
+    value_type value;
+  };
+  return sizeof(Node) * list.size() +
+         EstimateIterableMemoryUsage(list);
+}
+
+// Tree containers
+
+template <class V>
+size_t EstimateTreeMemoryUsage(size_t size) {
+  // Tree containers are modeled after libc++
+  // (__tree_node from include/__tree)
+  struct Node {
+    Node* left;
+    Node* right;
+    Node* parent;
+    bool is_black;
+    V value;
+  };
+  return sizeof(Node) * size;
+}
+
+template <class T, class C, class A>
+size_t EstimateMemoryUsage(const std::set<T, C, A>& set) {
+  using value_type = typename std::set<T, C, A>::value_type;
+  return EstimateTreeMemoryUsage<value_type>(set.size()) +
+         EstimateIterableMemoryUsage(set);
+}
+
+template <class T, class C, class A>
+size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set) {
+  using value_type = typename std::multiset<T, C, A>::value_type;
+  return EstimateTreeMemoryUsage<value_type>(set.size()) +
+         EstimateIterableMemoryUsage(set);
+}
+
+template <class K, class V, class C, class A>
+size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map) {
+  using value_type = typename std::map<K, V, C, A>::value_type;
+  return EstimateTreeMemoryUsage<value_type>(map.size()) +
+         EstimateIterableMemoryUsage(map);
+}
+
+template <class K, class V, class C, class A>
+size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map) {
+  using value_type = typename std::multimap<K, V, C, A>::value_type;
+  return EstimateTreeMemoryUsage<value_type>(map.size()) +
+         EstimateIterableMemoryUsage(map);
+}
+
+// HashMap containers
+
+namespace internal {
+
+// While hashtable containers model doesn't depend on STL implementation, one
+// detail still crept in: bucket_count. It's used in size estimation, but its
+// value after inserting N items is not predictable.
+// This function is specialized by unittests to return constant value, thus
+// excluding bucket_count from testing.
+template <class V>
+size_t HashMapBucketCountForTesting(size_t bucket_count) {
+  return bucket_count;
+}
+
+}  // namespace internal
+
+template <class V>
+size_t EstimateHashMapMemoryUsage(size_t bucket_count, size_t size) {
+  // Hashtable containers are modeled after libc++
+  // (__hash_node from include/__hash_table)
+  struct Node {
+    void* next;
+    size_t hash;
+    V value;
+  };
+  using Bucket = void*;
+  bucket_count = internal::HashMapBucketCountForTesting<V>(bucket_count);
+  return sizeof(Bucket) * bucket_count + sizeof(Node) * size;
+}
+
+template <class K, class H, class KE, class A>
+size_t EstimateMemoryUsage(const std::unordered_set<K, H, KE, A>& set) {
+  using value_type = typename std::unordered_set<K, H, KE, A>::value_type;
+  return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
+                                                set.size()) +
+         EstimateIterableMemoryUsage(set);
+}
+
+template <class K, class H, class KE, class A>
+size_t EstimateMemoryUsage(const std::unordered_multiset<K, H, KE, A>& set) {
+  using value_type = typename std::unordered_multiset<K, H, KE, A>::value_type;
+  return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
+                                                set.size()) +
+         EstimateIterableMemoryUsage(set);
+}
+
+template <class K, class V, class H, class KE, class A>
+size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map) {
+  using value_type = typename std::unordered_map<K, V, H, KE, A>::value_type;
+  return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
+                                                map.size()) +
+         EstimateIterableMemoryUsage(map);
+}
+
+template <class K, class V, class H, class KE, class A>
+size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map) {
+  using value_type =
+      typename std::unordered_multimap<K, V, H, KE, A>::value_type;
+  return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
+                                                map.size()) +
+         EstimateIterableMemoryUsage(map);
+}
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
diff --git a/base/trace_event/memory_usage_estimator_unittest.cc b/base/trace_event/memory_usage_estimator_unittest.cc
new file mode 100644
index 0000000..b77d5fd
--- /dev/null
+++ b/base/trace_event/memory_usage_estimator_unittest.cc
@@ -0,0 +1,229 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/memory_usage_estimator.h"
+
+#include <stdlib.h>
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/string16.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(ARCH_CPU_64_BITS)
+#define EXPECT_EQ_32_64(_, e, a) EXPECT_EQ(e, a)
+#else
+#define EXPECT_EQ_32_64(e, _, a) EXPECT_EQ(e, a)
+#endif
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+// Test class with predictable memory usage.
+class Data {
+ public:
+  explicit Data(size_t size = 17): size_(size) {
+  }
+
+  size_t size() const { return size_; }
+
+  size_t EstimateMemoryUsage() const {
+    return size_;
+  }
+
+  bool operator < (const Data& other) const {
+    return size_ < other.size_;
+  }
+  bool operator == (const Data& other) const {
+    return size_ == other.size_;
+  }
+
+  struct Hasher {
+    size_t operator () (const Data& data) const {
+      return data.size();
+    }
+  };
+
+ private:
+  size_t size_;
+};
+
+}  // namespace
+
+namespace internal {
+
+// This kills variance of bucket_count across STL implementations.
+template <>
+size_t HashMapBucketCountForTesting<Data>(size_t) {
+  return 10;
+}
+template <>
+size_t HashMapBucketCountForTesting<std::pair<const Data, short>>(size_t) {
+  return 10;
+}
+
+}  // namespace internal
+
+TEST(EstimateMemoryUsageTest, String) {
+  std::string string(777, 'a');
+  EXPECT_EQ(string.capacity() + 1, EstimateMemoryUsage(string));
+}
+
+TEST(EstimateMemoryUsageTest, String16) {
+  string16 string(777, 'a');
+  EXPECT_EQ(sizeof(char16) * (string.capacity() + 1),
+            EstimateMemoryUsage(string));
+}
+
+TEST(EstimateMemoryUsageTest, Arrays) {
+  // std::array
+  {
+    std::array<Data, 10> array;
+    EXPECT_EQ(170u, EstimateMemoryUsage(array));
+  }
+
+  // T[N]
+  {
+    Data array[10];
+    EXPECT_EQ(170u, EstimateMemoryUsage(array));
+  }
+
+  // C array
+  {
+    struct Item {
+      char payload[10];
+    };
+    Item* array = new Item[7];
+    EXPECT_EQ(70u, EstimateMemoryUsage(array, 7));
+    delete[] array;
+  }
+}
+
+TEST(EstimateMemoryUsageTest, UniquePtr) {
+  // Empty
+  {
+    std::unique_ptr<Data> ptr;
+    EXPECT_EQ(0u, EstimateMemoryUsage(ptr));
+  }
+
+  // Not empty
+  {
+    std::unique_ptr<Data> ptr(new Data());
+    EXPECT_EQ_32_64(21u, 25u, EstimateMemoryUsage(ptr));
+  }
+
+  // With a pointer
+  {
+    std::unique_ptr<Data*> ptr(new Data*());
+    EXPECT_EQ(sizeof(void*), EstimateMemoryUsage(ptr));
+  }
+
+  // With an array
+  {
+    struct Item {
+      uint32_t payload[10];
+    };
+    std::unique_ptr<Item[]> ptr(new Item[7]);
+    EXPECT_EQ(280u, EstimateMemoryUsage(ptr, 7));
+  }
+}
+
+TEST(EstimateMemoryUsageTest, Vector) {
+  std::vector<Data> vector;
+  vector.reserve(1000);
+
+  // For an empty vector we should return memory usage of its buffer
+  size_t capacity = vector.capacity();
+  size_t expected_size = capacity * sizeof(Data);
+  EXPECT_EQ(expected_size, EstimateMemoryUsage(vector));
+
+  // If vector is not empty, its size should also include memory usages
+  // of all elements.
+  for (size_t i = 0; i != capacity / 2; ++i) {
+    vector.push_back(Data(i));
+    expected_size += EstimateMemoryUsage(vector.back());
+  }
+  EXPECT_EQ(expected_size, EstimateMemoryUsage(vector));
+}
+
+TEST(EstimateMemoryUsageTest, List) {
+  struct POD {
+    short data;
+  };
+  std::list<POD> list;
+  for (int i = 0; i != 1000; ++i) {
+    list.push_back(POD());
+  }
+  EXPECT_EQ_32_64(12000u, 24000u, EstimateMemoryUsage(list));
+}
+
+TEST(EstimateMemoryUsageTest, Set) {
+  std::set<std::pair<int, Data>> set;
+  for (int i = 0; i != 1000; ++i) {
+    set.insert({i, Data(i)});
+  }
+  EXPECT_EQ_32_64(523500u, 547500u, EstimateMemoryUsage(set));
+}
+
+TEST(EstimateMemoryUsageTest, MultiSet) {
+  std::multiset<bool> set;
+  for (int i = 0; i != 1000; ++i) {
+    set.insert((i & 1) != 0);
+  }
+  EXPECT_EQ_32_64(16000u, 32000u, EstimateMemoryUsage(set));
+}
+
+TEST(EstimateMemoryUsageTest, Map) {
+  std::map<Data, int> map;
+  for (int i = 0; i != 1000; ++i) {
+    map.insert({Data(i), i});
+  }
+  EXPECT_EQ_32_64(523500u, 547500u, EstimateMemoryUsage(map));
+}
+
+TEST(EstimateMemoryUsageTest, MultiMap) {
+  std::multimap<char, Data> map;
+  for (int i = 0; i != 1000; ++i) {
+    map.insert({static_cast<char>(i), Data(i)});
+  }
+  EXPECT_EQ_32_64(523500u, 547500u, EstimateMemoryUsage(map));
+}
+
+TEST(EstimateMemoryUsageTest, UnorderedSet) {
+  std::unordered_set<Data, Data::Hasher> set;
+  for (int i = 0; i != 1000; ++i) {
+    set.insert(Data(i));
+  }
+  EXPECT_EQ_32_64(511540u, 523580u, EstimateMemoryUsage(set));
+}
+
+TEST(EstimateMemoryUsageTest, UnorderedMultiSet) {
+  std::unordered_multiset<Data, Data::Hasher> set;
+  for (int i = 0; i != 500; ++i) {
+    set.insert(Data(i));
+    set.insert(Data(i));
+  }
+  EXPECT_EQ_32_64(261540u, 273580u, EstimateMemoryUsage(set));
+}
+
+TEST(EstimateMemoryUsageTest, UnorderedMap) {
+  std::unordered_map<Data, short, Data::Hasher> map;
+  for (int i = 0; i != 1000; ++i) {
+    map.insert({Data(i), static_cast<short>(i)});
+  }
+  EXPECT_EQ_32_64(515540u, 531580u, EstimateMemoryUsage(map));
+}
+
+TEST(EstimateMemoryUsageTest, UnorderedMultiMap) {
+  std::unordered_multimap<Data, short, Data::Hasher> map;
+  for (int i = 0; i != 1000; ++i) {
+    map.insert({Data(i), static_cast<short>(i)});
+  }
+  EXPECT_EQ_32_64(515540u, 531580u, EstimateMemoryUsage(map));
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/blimp/tools/client_engine_integration.py b/blimp/tools/client_engine_integration.py
index 0357a77..e28c641 100755
--- a/blimp/tools/client_engine_integration.py
+++ b/blimp/tools/client_engine_integration.py
@@ -191,8 +191,8 @@
   else:
     try:
       port_pairs = [(port_number, port_number)]
-      forwarder.Forwarder.Map(port_pairs, device)
       SetCommandFlag(device, args.engine_ip, port_number)
+      forwarder.Forwarder.Map(port_pairs, device)
       print "Blimp engine started"
       return engine_process
 
diff --git a/build/android/pylib/base/environment_factory.py b/build/android/pylib/base/environment_factory.py
index d72bbeb2..29de483 100644
--- a/build/android/pylib/base/environment_factory.py
+++ b/build/android/pylib/base/environment_factory.py
@@ -4,12 +4,14 @@
 
 from pylib import constants
 from pylib.local.device import local_device_environment
+from pylib.local.machine import local_machine_environment
 
 def CreateEnvironment(args, error_func):
 
   if args.environment == 'local':
     if args.command not in constants.LOCAL_MACHINE_TESTS:
       return local_device_environment.LocalDeviceEnvironment(args, error_func)
-    # TODO(jbudorick) Add local machine environment.
+    else:
+      return local_machine_environment.LocalMachineEnvironment(args, error_func)
 
   error_func('Unable to create %s environment.' % args.environment)
diff --git a/build/android/pylib/base/test_instance_factory.py b/build/android/pylib/base/test_instance_factory.py
index 2d0d83f..129bd7f 100644
--- a/build/android/pylib/base/test_instance_factory.py
+++ b/build/android/pylib/base/test_instance_factory.py
@@ -4,6 +4,7 @@
 
 from pylib.gtest import gtest_test_instance
 from pylib.instrumentation import instrumentation_test_instance
+from pylib.junit import junit_test_instance
 from pylib.perf import perf_test_instance
 from pylib.utils import isolator
 
@@ -16,6 +17,8 @@
   elif args.command == 'instrumentation':
     return instrumentation_test_instance.InstrumentationTestInstance(
         args, isolator.Isolator(), error_func)
+  elif args.command == 'junit':
+    return junit_test_instance.JunitTestInstance(args, error_func)
   elif args.command == 'perf':
     return perf_test_instance.PerfTestInstance(args, error_func)
 
diff --git a/build/android/pylib/base/test_run.py b/build/android/pylib/base/test_run.py
index 7380e78..9b16f89 100644
--- a/build/android/pylib/base/test_run.py
+++ b/build/android/pylib/base/test_run.py
@@ -25,6 +25,11 @@
     raise NotImplementedError
 
   def RunTests(self):
+    """Runs Tests and returns test results.
+
+    Returns:
+      Should return list of |base_test_result.TestRunResults| objects.
+    """
     raise NotImplementedError
 
   def TearDown(self):
diff --git a/build/android/pylib/base/test_run_factory.py b/build/android/pylib/base/test_run_factory.py
index 04a8f3c..d706fd4 100644
--- a/build/android/pylib/base/test_run_factory.py
+++ b/build/android/pylib/base/test_run_factory.py
@@ -4,10 +4,13 @@
 
 from pylib.gtest import gtest_test_instance
 from pylib.instrumentation import instrumentation_test_instance
+from pylib.junit import junit_test_instance
 from pylib.local.device import local_device_environment
 from pylib.local.device import local_device_gtest_run
 from pylib.local.device import local_device_instrumentation_test_run
 from pylib.local.device import local_device_perf_test_run
+from pylib.local.machine import local_machine_environment
+from pylib.local.machine import local_machine_junit_test_run
 from pylib.perf import perf_test_instance
 
 
@@ -34,6 +37,11 @@
                   perf_test_instance.PerfTestInstance):
       return _CreatePerfTestRun(args, env, test_instance)
 
+  if isinstance(env, local_machine_environment.LocalMachineEnvironment):
+    if isinstance(test_instance, junit_test_instance.JunitTestInstance):
+      return (local_machine_junit_test_run
+              .LocalMachineJunitTestRun(env, test_instance))
+
   error_func('Unable to create test run for %s tests in %s environment'
              % (str(test_instance), str(env)))
 
diff --git a/build/android/pylib/device/commands/BUILD.gn b/build/android/pylib/device/commands/BUILD.gn
index 34247ee4..fe17276 100644
--- a/build/android/pylib/device/commands/BUILD.gn
+++ b/build/android/pylib/device/commands/BUILD.gn
@@ -6,11 +6,14 @@
 
 group("commands") {
   data_deps = [
-    ":chromium_commands",
+    ":chromium_commands_java",
   ]
 }
 
-android_library("chromium_commands") {
+android_library("chromium_commands_java") {
   java_files = [ "java/src/org/chromium/android/commands/unzip/Unzip.java" ]
   dex_path = "$root_build_dir/lib.java/chromium_commands.dex.jar"
+  data = [
+    dex_path,
+  ]
 }
diff --git a/build/android/pylib/junit/junit_test_instance.py b/build/android/pylib/junit/junit_test_instance.py
new file mode 100644
index 0000000..5fd6af9
--- /dev/null
+++ b/build/android/pylib/junit/junit_test_instance.py
@@ -0,0 +1,49 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from pylib.base import test_instance
+
+
+class JunitTestInstance(test_instance.TestInstance):
+
+  def __init__(self, args, _):
+    super(JunitTestInstance, self).__init__()
+
+    self._coverage_dir = args.coverage_dir
+    self._package_filter = args.package_filter
+    self._runner_filter = args.runner_filter
+    self._test_filter = args.test_filter
+    self._test_suite = args.test_suite
+
+  #override
+  def TestType(self):
+    return 'junit'
+
+  #override
+  def SetUp(self):
+    pass
+
+  #override
+  def TearDown(self):
+    pass
+
+  @property
+  def coverage_dir(self):
+    return self._coverage_dir
+
+  @property
+  def package_filter(self):
+    return self._package_filter
+
+  @property
+  def runner_filter(self):
+    return self._runner_filter
+
+  @property
+  def test_filter(self):
+    return self._test_filter
+
+  @property
+  def suite(self):
+    return self._test_suite
diff --git a/build/android/pylib/junit/test_runner.py b/build/android/pylib/junit/test_runner.py
index 5066c204..17e8afa 100644
--- a/build/android/pylib/junit/test_runner.py
+++ b/build/android/pylib/junit/test_runner.py
@@ -17,7 +17,7 @@
     self._coverage_dir = args.coverage_dir
     self._package_filter = args.package_filter
     self._runner_filter = args.runner_filter
-    self._sdk_version = args.sdk_version
+
     self._test_filter = args.test_filter
     self._test_suite = args.test_suite
 
@@ -40,8 +40,6 @@
         jar_args.extend(['-package-filter', self._package_filter])
       if self._runner_filter:
         jar_args.extend(['-runner-filter', self._runner_filter])
-      if self._sdk_version:
-        jar_args.extend(['-sdk-version', self._sdk_version])
       command.extend(['--jar-args', '"%s"' % ' '.join(jar_args)])
 
       # Add JVM arguments.
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
index 6c00b70..59d43a9 100644
--- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py
+++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -62,6 +62,7 @@
   def TestPackage(self):
     return self._test_instance.suite
 
+  #override
   def SetUp(self):
     def substitute_device_root(d, device_root):
       if not d:
@@ -153,6 +154,7 @@
         individual_device_set_up,
         self._test_instance.GetDataDependencies())
 
+  #override
   def TearDown(self):
     @local_device_environment.handle_shard_failures_with(
         self._env.BlacklistDevice)
diff --git a/build/android/pylib/local/device/local_device_perf_test_run.py b/build/android/pylib/local/device/local_device_perf_test_run.py
index 823e903..3398a0b 100644
--- a/build/android/pylib/local/device/local_device_perf_test_run.py
+++ b/build/android/pylib/local/device/local_device_perf_test_run.py
@@ -335,11 +335,13 @@
     self._test_instance = test_instance
     self._timeout = None if test_instance.no_timeout else self._DEFAULT_TIMEOUT
 
+  #override
   def SetUp(self):
     if os.path.exists(constants.PERF_OUTPUT_DIR):
       shutil.rmtree(constants.PERF_OUTPUT_DIR)
     os.makedirs(constants.PERF_OUTPUT_DIR)
 
+  #override
   def TearDown(self):
     pass
 
diff --git a/build/android/pylib/local/machine/__init__.py b/build/android/pylib/local/machine/__init__.py
new file mode 100644
index 0000000..ca3e206f
--- /dev/null
+++ b/build/android/pylib/local/machine/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/build/android/pylib/local/machine/local_machine_environment.py b/build/android/pylib/local/machine/local_machine_environment.py
new file mode 100644
index 0000000..b9f6acad
--- /dev/null
+++ b/build/android/pylib/local/machine/local_machine_environment.py
@@ -0,0 +1,19 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from pylib.base import environment
+
+
+class LocalMachineEnvironment(environment.Environment):
+
+  def __init__(self, _args, _error_func):
+    super(LocalMachineEnvironment, self).__init__()
+
+  #override
+  def SetUp(self):
+    pass
+
+  #override
+  def TearDown(self):
+    pass
diff --git a/build/android/pylib/local/machine/local_machine_junit_test_run.py b/build/android/pylib/local/machine/local_machine_junit_test_run.py
new file mode 100644
index 0000000..ef8bef4
--- /dev/null
+++ b/build/android/pylib/local/machine/local_machine_junit_test_run.py
@@ -0,0 +1,78 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import os
+import tempfile
+
+from devil.utils import cmd_helper
+from pylib import constants
+from pylib.base import base_test_result
+from pylib.base import test_run
+from pylib.results import json_results
+
+
+class LocalMachineJunitTestRun(test_run.TestRun):
+  def __init__(self, env, test_instance):
+    super(LocalMachineJunitTestRun, self).__init__(env, test_instance)
+
+  #override
+  def TestPackage(self):
+    return self._test_instance.suite
+
+  #override
+  def SetUp(self):
+    pass
+
+  #override
+  def RunTests(self):
+    with tempfile.NamedTemporaryFile() as json_file:
+      java_script = os.path.join(
+          constants.GetOutDirectory(), 'bin', 'helper',
+          self._test_instance.suite)
+      command = [java_script]
+
+      # Add Jar arguments.
+      jar_args = ['-test-jars', self._test_instance.suite + '.jar',
+                  '-json-results-file', json_file.name]
+      if self._test_instance.test_filter:
+        jar_args.extend(['-gtest-filter', self._test_instance.test_filter])
+      if self._test_instance.package_filter:
+        jar_args.extend(['-package-filter',
+                         self._test_instance.package_filter])
+      if self._test_instance.runner_filter:
+        jar_args.extend(['-runner-filter', self._test_instance.runner_filter])
+      command.extend(['--jar-args', '"%s"' % ' '.join(jar_args)])
+
+      # Add JVM arguments.
+      jvm_args = []
+      # TODO(mikecase): Add a --robolectric-dep-dir arg to test runner.
+      # Have this arg set by GN in the generated test runner scripts.
+      jvm_args += [
+          '-Drobolectric.dependency.dir=%s' %
+          os.path.join(constants.GetOutDirectory(),
+              'lib.java', 'third_party', 'robolectric')]
+      if self._test_instance.coverage_dir:
+        if not os.path.exists(self._test_instance.coverage_dir):
+          os.makedirs(self._test_instance.coverage_dir)
+        elif not os.path.isdir(self._test_instance.coverage_dir):
+          raise Exception('--coverage-dir takes a directory, not file path.')
+        jvm_args.append('-Demma.coverage.out.file=%s' % os.path.join(
+            self._test_instance.coverage_dir,
+            '%s.ec' % self._test_instance.suite))
+      if jvm_args:
+        command.extend(['--jvm-args', '"%s"' % ' '.join(jvm_args)])
+
+      cmd_helper.RunCmd(command)
+      results_list = json_results.ParseResultsFromJson(
+          json.loads(json_file.read()))
+
+      test_run_results = base_test_result.TestRunResults()
+      test_run_results.AddResults(results_list)
+
+      return [test_run_results]
+
+  #override
+  def TearDown(self):
+    pass
diff --git a/build/android/pylib/perf/perf_test_instance.py b/build/android/pylib/perf/perf_test_instance.py
index 426ffaf..fbb7962d 100644
--- a/build/android/pylib/perf/perf_test_instance.py
+++ b/build/android/pylib/perf/perf_test_instance.py
@@ -81,9 +81,11 @@
     self._test_filter = args.test_filter
     self._write_buildbot_json = args.write_buildbot_json
 
+  #override
   def SetUp(self):
     pass
 
+  #override
   def TearDown(self):
     pass
 
diff --git a/build/android/test_runner.py b/build/android/test_runner.py
index 6509d1a2b66..42429d3 100755
--- a/build/android/test_runner.py
+++ b/build/android/test_runner.py
@@ -400,13 +400,19 @@
   group.add_argument(
       '--package-filter', dest='package_filter',
       help='Filters tests by package.')
+  # TODO(mikecase): Add --repeat and --break-on-failure to common options.
+  # These options are required for platform-mode support.
+  group.add_argument(
+      '--repeat', dest='repeat', type=int, default=0,
+      help='Number of times to repeat the specified set of tests.')
+  group.add_argument(
+      '--break-on-failure', '--break_on_failure',
+      dest='break_on_failure', action='store_true',
+      help='Whether to break on failure.')
   group.add_argument(
       '--runner-filter', dest='runner_filter',
       help='Filters tests by runner class. Must be fully qualified.')
   group.add_argument(
-      '--sdk-version', dest='sdk_version', type=int,
-      help='The Android SDK version.')
-  group.add_argument(
       '--coverage-dir', dest='coverage_dir', type=os.path.realpath,
       help='Directory to store coverage info.')
   AddCommonOptions(parser)
@@ -721,6 +727,7 @@
   # TODO(jbudorick): Add support for more test types.
   'gtest',
   'instrumentation',
+  'junit',
   'perf',
 ]
 
diff --git a/build/android/test_runner.pydeps b/build/android/test_runner.pydeps
index 554181d..8bc5786 100644
--- a/build/android/test_runner.pydeps
+++ b/build/android/test_runner.pydeps
@@ -93,6 +93,7 @@
 pylib/instrumentation/instrumentation_test_instance.py
 pylib/instrumentation/test_result.py
 pylib/junit/__init__.py
+pylib/junit/junit_test_instance.py
 pylib/junit/setup.py
 pylib/junit/test_dispatcher.py
 pylib/junit/test_runner.py
@@ -108,6 +109,9 @@
 pylib/local/device/local_device_perf_test_run.py
 pylib/local/device/local_device_test_run.py
 pylib/local/local_test_server_spawner.py
+pylib/local/machine/__init__.py
+pylib/local/machine/local_machine_environment.py
+pylib/local/machine/local_machine_junit_test_run.py
 pylib/monkey/__init__.py
 pylib/monkey/setup.py
 pylib/monkey/test_options.py
diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn
index fd05dfd9..122f254 100644
--- a/build/config/BUILD.gn
+++ b/build/config/BUILD.gn
@@ -161,9 +161,6 @@
   if (!enable_nacl) {
     defines += [ "DISABLE_NACL" ]
   }
-  if (enable_extensions) {
-    defines += [ "ENABLE_EXTENSIONS=1" ]
-  }
   if (enable_task_manager) {
     defines += [ "ENABLE_TASK_MANAGER=1" ]
   }
@@ -173,9 +170,6 @@
   if (enable_rlz) {
     defines += [ "ENABLE_RLZ" ]
   }
-  if (enable_image_loader_extension) {
-    defines += [ "IMAGE_LOADER_EXTENSION=1" ]
-  }
   if (enable_wayland_server) {
     defines += [ "ENABLE_WAYLAND_SERVER=1" ]
   }
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index f228bac..4bc405d 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -31,7 +31,6 @@
 
   # TODO(agrieve): Rename targets below to match above patterns.
   "*android_webview/glue:glue",
-  "//build/android/pylib/device/commands:chromium_commands",
   "//build/android/rezip:rezip",
   "//chrome/test/android/cast_emulator:cast_emulator",
   "//mojo/public/java:bindings",
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index fbe7cff..6649a67f 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -2314,7 +2314,7 @@
       install_script_name = _install_script_name
       deps += [ "//testing/android/broker:broker_java" ]
       data_deps += [
-        "//testing/android/driver:driver_apk",
+        "//build/android/pylib/device/commands",
         "//tools/android/forwarder2",
         "//tools/android/md5sum",
       ]
@@ -2446,7 +2446,10 @@
         "//testing/android/appurify_support:appurify_support_java",
         "//testing/android/reporter:reporter_java",
       ]
-      data_deps += [ "//tools/android/md5sum" ]
+      data_deps += [
+        "//build/android/pylib/device/commands",
+        "//tools/android/md5sum",
+      ]
       if (host_os == "linux") {
         data_deps += [ "//tools/android/forwarder2" ]
       }
diff --git a/build/config/features.gni b/build/config/features.gni
index 738a1b9..b44b0770 100644
--- a/build/config/features.gni
+++ b/build/config/features.gni
@@ -21,7 +21,6 @@
 }
 
 declare_args() {
-  enable_extensions = !is_android && !is_ios
   enable_plugins = (!is_android && !is_ios) || is_chromecast
   enable_pdf = !is_android && !is_ios && !is_chromecast
 
@@ -81,12 +80,10 @@
 
   # Enable WebVR support by default on Android
   # Still requires command line flag to access API
-  # TODO(bshe): Remove is_component_build once we can statically link gvr
-  # library. Currently gvr library only has a prebuild so file which causes
-  # linking issue for non component build.
+  # TODO(bshe): Enable for other architecture too. Currently we only support arm
+  # and arm64.
   enable_webvr = is_android && is_component_build &&
-                 (current_cpu == "x86" || current_cpu == "x64" ||
-                  current_cpu == "arm" || current_cpu == "arm64")
+                 (current_cpu == "arm" || current_cpu == "arm64")
 }
 
 # Additional dependent variables -----------------------------------------------
@@ -97,9 +94,6 @@
 # Use the operating system's spellchecker rather than hunspell.
 use_browser_spellchecker = is_android || is_mac
 
-# Use Minikin hyphenation engine.
-use_minikin_hyphenation = is_android
-
 # The seccomp-bpf sandbox is only supported on five architectures
 # currently.
 # Do not disable seccomp_bpf anywhere without talking to
@@ -123,16 +117,10 @@
 enable_rlz_support = is_win || is_mac || is_ios || is_chromeos
 enable_rlz = is_chrome_branded && enable_rlz_support
 
-# Image loader extension is enabled on ChromeOS only.
-enable_image_loader_extension = is_chromeos
-
 # Chrome OS: whether to also build the upcoming version of
 # ChromeVox, which can then be enabled via a command-line switch.
 enable_chromevox_next = false
 
-# Use brlapi from brltty for braille display support.
-use_brlapi = is_chromeos
-
 enable_configuration_policy = !is_ios
 
 enable_mac_keystone = is_mac && is_chrome_branded && is_official_build
diff --git a/build/linux/BUILD.gn b/build/linux/BUILD.gn
index c81172e..410f832a 100644
--- a/build/linux/BUILD.gn
+++ b/build/linux/BUILD.gn
@@ -5,12 +5,6 @@
 import("//build/config/features.gni")
 import("//build/config/linux/pkg_config.gni")
 
-# If brlapi isn't needed, don't require it to be installed.
-if (use_brlapi) {
-  deps = [
-    "//build/linux/libbrlapi",
-  ]
-}
 if (use_gio) {
   pkg_config("gio_config") {
     packages = [ "gio-2.0" ]
diff --git a/build/linux/libbrlapi/BUILD.gn b/build/linux/libbrlapi/BUILD.gn
index fa99540..4ee395045 100644
--- a/build/linux/libbrlapi/BUILD.gn
+++ b/build/linux/libbrlapi/BUILD.gn
@@ -4,17 +4,11 @@
 
 import("//tools/generate_library_loader/generate_library_loader.gni")
 
-config("brlapi_config") {
-  defines = [ "USE_BRLAPI" ]
-}
-
-# TODO(GYP) linux_link_brlapi support. Is this needed?
 generate_library_loader("libbrlapi") {
   name = "LibBrlapiLoader"
   output_h = "libbrlapi.h"
   output_cc = "libbrlapi_loader.cc"
   header = "<brlapi.h>"
-  config = ":brlapi_config"
 
   functions = [
     "brlapi_getHandleSize",
diff --git a/build/symlink.gni b/build/symlink.gni
index 95a498d..4da5a57 100644
--- a/build/symlink.gni
+++ b/build/symlink.gni
@@ -10,6 +10,7 @@
   action(target_name) {
     forward_variables_from(invoker,
                            [
+                             "data_deps",
                              "deps",
                              "testonly",
                              "visibility",
@@ -60,6 +61,12 @@
     deps = [
       invoker.binary_label,
     ]
+    data_deps = [
+      invoker.binary_label,
+    ]
+    if (defined(invoker.data_deps)) {
+      data_deps += invoker.data_deps
+    }
 
     _out_dir = get_label_info(invoker.binary_label, "root_out_dir")
     if (defined(invoker.binary_output_name)) {
diff --git a/cc/resources/scoped_ui_resource.h b/cc/resources/scoped_ui_resource.h
index 7f3ea4b..a0fb5d6d 100644
--- a/cc/resources/scoped_ui_resource.h
+++ b/cc/resources/scoped_ui_resource.h
@@ -33,9 +33,7 @@
   UIResourceId id() { return id_; }
 
   // Returns the memory usage of the bitmap.
-  size_t GetAllocatedSizeInBytes() const {
-    return bitmap_.GetAllocatedSizeInBytes();
-  }
+  size_t EstimateMemoryUsage() const { return bitmap_.EstimateMemoryUsage(); }
 
  protected:
   ScopedUIResource(UIResourceManager* ui_resource_manager,
diff --git a/cc/resources/ui_resource_bitmap.h b/cc/resources/ui_resource_bitmap.h
index 6f2c780..0c7931b 100644
--- a/cc/resources/ui_resource_bitmap.h
+++ b/cc/resources/ui_resource_bitmap.h
@@ -52,7 +52,7 @@
   ~UIResourceBitmap();
 
   // Returns the memory usage of the bitmap.
-  size_t GetAllocatedSizeInBytes() const {
+  size_t EstimateMemoryUsage() const {
     return pixel_ref_ ? pixel_ref_->rowBytes() * size_.height() : 0;
   }
 
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 750b1d709..84b911b 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -15,6 +15,7 @@
 import("//chrome/chrome_paks.gni")
 import("//chrome/common/features.gni")
 import("//chrome/process_version_rc_template.gni")
+import("//extensions/features/features.gni")
 import("//ppapi/features/features.gni")
 import("//third_party/WebKit/public/public_features.gni")
 import("//third_party/widevine/cdm/widevine.gni")
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 5b027f5..50ccd56 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -266,10 +266,7 @@
 
   if (enable_vr_shell || enable_webvr) {
     java_files += chrome_vr_java_sources
-    deps += [
-      "//third_party/gvr-android-sdk:gvr_base_java",
-      "//third_party/gvr-android-sdk:gvr_common_java",
-    ]
+    deps += [ "//third_party/gvr-android-sdk:gvr_common_java" ]
   }
 }
 
@@ -461,10 +458,7 @@
 
   if (enable_vr_shell) {
     java_files += [ "javatests/src/org/chromium/chrome/browser/vr_shell/VrShellTest.java" ]
-    deps += [
-      "//third_party/gvr-android-sdk:gvr_base_java",
-      "//third_party/gvr-android-sdk:gvr_common_java",
-    ]
+    deps += [ "//third_party/gvr-android-sdk:gvr_common_java" ]
   }
 }
 
diff --git a/chrome/android/java/res/drawable-hdpi/offline_pin_round.png b/chrome/android/java/res/drawable-hdpi/offline_pin_round.png
new file mode 100644
index 0000000..61cab7c
--- /dev/null
+++ b/chrome/android/java/res/drawable-hdpi/offline_pin_round.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/offline_pin_round.png b/chrome/android/java/res/drawable-mdpi/offline_pin_round.png
new file mode 100644
index 0000000..6c4bd2b5
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/offline_pin_round.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/offline_pin_round.png b/chrome/android/java/res/drawable-xhdpi/offline_pin_round.png
new file mode 100644
index 0000000..2067c3e
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/offline_pin_round.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/offline_pin_round.png b/chrome/android/java/res/drawable-xxhdpi/offline_pin_round.png
new file mode 100644
index 0000000..a52e1bc
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/offline_pin_round.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/offline_pin_round.png b/chrome/android/java/res/drawable-xxxhdpi/offline_pin_round.png
new file mode 100644
index 0000000..3e1cdc4
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxxhdpi/offline_pin_round.png
Binary files differ
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActionModeCallback.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActionModeCallback.java
index 42b9ca0..2e3dbf1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActionModeCallback.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActionModeCallback.java
@@ -36,14 +36,14 @@
 
     @Override
     public boolean onCreateActionMode(ActionMode mode, Menu menu) {
-        mTab.notifyContextualActionBarVisibilityChanged(true);
+        notifyContextualActionBarVisibilityChanged(true);
         mHelper.onCreateActionMode(mode, menu);
         return true;
     }
 
     @Override
     public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
-        mTab.notifyContextualActionBarVisibilityChanged(true);
+        notifyContextualActionBarVisibilityChanged(true);
         return mHelper.onPrepareActionMode(mode, menu);
     }
 
@@ -63,7 +63,13 @@
     @Override
     public void onDestroyActionMode(ActionMode mode) {
         mHelper.onDestroyActionMode();
-        mTab.notifyContextualActionBarVisibilityChanged(false);
+        notifyContextualActionBarVisibilityChanged(false);
+    }
+
+    private void notifyContextualActionBarVisibilityChanged(boolean show) {
+        if (!mHelper.supportsFloatingActionMode()) {
+            mTab.notifyContextualActionBarVisibilityChanged(show);
+        }
     }
 
     private void search() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 3a5772e..d1f63c7f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -1685,7 +1685,8 @@
     protected ChromeFullscreenManager createFullscreenManager() {
         return new ChromeFullscreenManager(this,
                 (ToolbarControlContainer) findViewById(R.id.control_container),
-                getTabModelSelector(), getControlContainerHeightResource(), true);
+                getTabModelSelector(), getControlContainerHeightResource(), true,
+                FeatureUtilities.isChromeHomeEnabled());
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
index 009f2bd..ff5df03 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
@@ -437,7 +437,7 @@
     public void onStart() {
         if (mFullscreenManager != null) {
             mLastContentOffset = mFullscreenManager.getContentOffset();
-            mLastVisibleContentOffset = mFullscreenManager.getVisibleContentOffset();
+            mLastVisibleContentOffset = mFullscreenManager.getTopVisibleContentOffset();
             mFullscreenManager.addListener(this);
         }
         requestRender();
@@ -495,9 +495,17 @@
     }
 
     private void propagateViewportToLayouts(int contentWidth, int contentHeight) {
-        int heightMinusBrowserControls = contentHeight - getBrowserControlsHeightPixels();
-        mCacheViewport.set(0, (int) mLastContentOffset, contentWidth, contentHeight);
-        mCacheVisibleViewport.set(0, (int) mLastVisibleContentOffset, contentWidth, contentHeight);
+        int heightMinusBrowserControls = contentHeight
+                - (getTopControlsHeightPixels() + getBottomControlsHeightPixels());
+        int bottomControlOffset = mFullscreenManager != null
+                ? (int) mFullscreenManager.getBottomControlOffset() : 0;
+        int viewportBottom =
+                contentHeight - (getBottomControlsHeightPixels() - bottomControlOffset);
+
+        // The only time that mCacheViewport and mCacheVisibleViewport are different is when the
+        // browser has manipulated the browser controls offset.
+        mCacheViewport.set(0, (int) mLastContentOffset, contentWidth, viewportBottom);
+        mCacheVisibleViewport.set(0, (int) mLastVisibleContentOffset, contentWidth, viewportBottom);
         // TODO(changwan): check if this can be merged with setContentMotionEventOffsets.
         if (mTabVisible != null && mTabVisible.getContentViewCore() != null) {
             mTabVisible.getContentViewCore().setSmartClipOffsets(
@@ -633,7 +641,7 @@
         mFullscreenManager = fullscreen;
         if (mFullscreenManager != null) {
             mLastContentOffset = mFullscreenManager.getContentOffset();
-            mLastVisibleContentOffset = mFullscreenManager.getVisibleContentOffset();
+            mLastVisibleContentOffset = mFullscreenManager.getTopVisibleContentOffset();
             mFullscreenManager.addListener(this);
         }
         propagateViewportToLayouts(getWidth(), getHeight());
@@ -658,14 +666,21 @@
     }
 
     @Override
-    public int getBrowserControlsHeightPixels() {
-        return mFullscreenManager != null ? mFullscreenManager.getBrowserControlsHeight() : 0;
+    public int getTopControlsHeightPixels() {
+        return mFullscreenManager != null ? mFullscreenManager.getTopControlsHeight() : 0;
+    }
+
+    /**
+     * @return The height of the bottom conrols in pixels.
+     */
+    public int getBottomControlsHeightPixels() {
+        return mFullscreenManager != null ? mFullscreenManager.getBottomControlsHeight() : 0;
     }
 
     @Override
     public int getOverlayTranslateY() {
         return areBrowserControlsPermanentlyHidden()
-                ? getBrowserControlsHeightPixels()
+                ? getTopControlsHeightPixels()
                 : mCacheVisibleViewport.top;
     }
 
@@ -878,8 +893,9 @@
      */
     private void initializeContentViewCore(ContentViewCore contentViewCore) {
         contentViewCore.setCurrentTouchEventOffsets(0.f, 0.f);
-        contentViewCore.setTopControlsHeight(getBrowserControlsHeightPixels(),
+        contentViewCore.setTopControlsHeight(getTopControlsHeightPixels(),
                 contentViewCore.doBrowserControlsShrinkBlinkSize());
+        contentViewCore.setBottomControlsHeight(getBottomControlsHeightPixels());
 
         adjustPhysicalBackingSize(contentViewCore,
                 mCompositorView.getWidth(), mCompositorView.getHeight());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
index 2fde2a9..41f64f4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
@@ -292,7 +292,7 @@
      */
     protected float getBrowserControlsOffsetDp() {
         if (mActivity == null || mActivity.getFullscreenManager() == null) return 0.0f;
-        return -mActivity.getFullscreenManager().getControlOffset() * mPxToDp;
+        return -mActivity.getFullscreenManager().getTopControlOffset() * mPxToDp;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java
index bcd0412..aadea2eb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java
@@ -1139,7 +1139,7 @@
         updateSceneLayer(viewport, contentViewport, layerTitleCache, tabContentManager,
                 resourceManager, fullscreenManager);
 
-        float offsetPx = fullscreenManager != null ? fullscreenManager.getControlOffset() : 0.f;
+        float offsetPx = fullscreenManager != null ? fullscreenManager.getTopControlOffset() : 0.f;
         float dpToPx = getContext().getResources().getDisplayMetrics().density;
         float offsetDp = offsetPx / dpToPx;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutRenderHost.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutRenderHost.java
index ddb8256d..31990ce 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutRenderHost.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutRenderHost.java
@@ -73,9 +73,9 @@
     boolean areBrowserControlsPermanentlyHidden();
 
     /**
-     * @return The height of the browser controls in pixels.
+     * @return The height of the top controls in pixels.
      */
-    int getBrowserControlsHeightPixels();
+    int getTopControlsHeightPixels();
 
     /**
      * @return The {@link ResourceManager}.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
index 98c7f41..2c7ea787 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
@@ -356,6 +356,6 @@
                 resourceManager, fullscreenManager);
         assert mSceneLayer != null;
         mSceneLayer.pushLayers(getContext(), viewport, contentViewport, this, layerTitleCache,
-                tabContentManager, resourceManager);
+                tabContentManager, resourceManager, fullscreenManager);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/SimpleAnimationLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/SimpleAnimationLayout.java
index 19e907d..c5c08b9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/SimpleAnimationLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/SimpleAnimationLayout.java
@@ -419,6 +419,6 @@
                 resourceManager, fullscreenManager);
         assert mSceneLayer != null;
         mSceneLayer.pushLayers(getContext(), viewport, contentViewport, this, layerTitleCache,
-                tabContentManager, resourceManager);
+                tabContentManager, resourceManager, fullscreenManager);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java
index ba37b6c..9651c2f6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java
@@ -1220,6 +1220,6 @@
                 resourceManager, fullscreenManager);
         assert mSceneLayer != null;
         mSceneLayer.pushLayers(getContext(), viewport, contentViewport, this, layerTitleCache,
-                tabContentManager, resourceManager);
+                tabContentManager, resourceManager, fullscreenManager);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java
index 1e6b9c4..b2c83bf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java
@@ -16,6 +16,7 @@
 import org.chromium.chrome.browser.compositor.layouts.Layout.Orientation;
 import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
+import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.ui.resources.ResourceManager;
 
@@ -33,11 +34,17 @@
      * disabled ScheduleComposite calls as this will change the tree and could subsequently cause
      * unnecessary follow up renders.
      * @param context         The {@link Context} to use to query device information.
+     * @param viewport        The viewport for the screen.
+     * @param contentViewport The visible viewport.
      * @param layout          The {@link Layout} to push to the screen.
+     * @param layerTitleCache An object for accessing tab layer titles.
+     * @param tabContentManager An object for accessing tab content.
+     * @param resourceManager An object for accessing static and dynamic resources.
+     * @param fullscreenManager The fullscreen manager for browser controls information.
      */
     public void pushLayers(Context context, Rect viewport, Rect contentViewport, Layout layout,
             LayerTitleCache layerTitleCache, TabContentManager tabContentManager,
-            ResourceManager resourceManager) {
+            ResourceManager resourceManager, ChromeFullscreenManager fullscreenManager) {
         if (mNativePtr == 0) return;
 
         Resources res = context.getResources();
@@ -73,7 +80,8 @@
                     R.drawable.tabswitcher_border_frame_decoration, R.drawable.logo_card_back,
                     R.drawable.tabswitcher_border_frame,
                     R.drawable.tabswitcher_border_frame_inner_shadow,
-                    t.canUseLiveTexture(), t.getBackgroundColor(),
+                    t.canUseLiveTexture(), fullscreenManager.areBrowserControlsAtBottom(),
+                    t.getBackgroundColor(),
                     ApiCompatibilityUtils.getColor(res, borderColorResource), t.isIncognito(),
                     layout.getOrientation() == Orientation.PORTRAIT, t.getRenderX() * dpToPx,
                     t.getRenderY() * dpToPx, t.getScaledContentWidth() * dpToPx,
@@ -91,7 +99,7 @@
                     defaultThemeColor, t.getToolbarBackgroundColor(), closeButtonColor,
                     t.anonymizeToolbar(), R.drawable.textbox, t.getTextBoxBackgroundColor(),
                     t.getTextBoxAlpha(), t.getToolbarAlpha(), t.getToolbarYOffset() * dpToPx,
-                    t.getSideBorderScale(), true, t.insetBorderVertical());
+                    t.getSideBorderScale(), t.insetBorderVertical());
         }
         nativeFinishBuildingFrame(mNativePtr);
     }
@@ -131,7 +139,8 @@
     private native void nativePutTabLayer(long nativeTabListSceneLayer, int id,
             int toolbarResourceId, int closeButtonResourceId, int shadowResourceId,
             int contourResourceId, int backLogoResourceId, int borderResourceId,
-            int borderInnerShadowResourceId, boolean canUseLiveLayer, int tabBackgroundColor,
+            int borderInnerShadowResourceId, boolean canUseLiveLayer,
+            boolean browserControlsAtBottom, int tabBackgroundColor,
             int backLogoColor, boolean incognito, boolean isPortrait, float x, float y, float width,
             float height, float contentWidth, float contentHeight, float visibleContentHeight,
             float shadowX, float shadowY, float shadowWidth, float shadowHeight, float pivotX,
@@ -142,5 +151,5 @@
             int toolbarBackgroundColor, int closeButtonColor, boolean anonymizeToolbar,
             int toolbarTextBoxResource, int toolbarTextBoxBackgroundColor,
             float toolbarTextBoxAlpha, float toolbarAlpha, float toolbarYOffset,
-            float sideBorderScale, boolean attachContent, boolean insetVerticalBorder);
+            float sideBorderScale, boolean insetVerticalBorder);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ToolbarSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ToolbarSceneLayer.java
index 94c6fff..52554bddf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ToolbarSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ToolbarSceneLayer.java
@@ -88,8 +88,9 @@
 
         mLayoutProvider.getViewportPixel(mViewport);
 
-        float offset = fullscreenManager.getControlOffset();
-        boolean useTexture = fullscreenManager.drawControlsAsTexture() || offset == 0
+        // Texture is always used unless it is completely off-screen.
+        boolean useTexture = !fullscreenManager.areBrowserControlsOffScreen();
+        boolean showShadow = fullscreenManager.drawControlsAsTexture()
                 || forceHideAndroidBrowserControls;
 
         fullscreenManager.setHideBrowserControlsAndroidView(forceHideAndroidBrowserControls);
@@ -100,9 +101,13 @@
             useTexture = false;
         }
 
+        // Note that the bottom controls offset is not passed here. Conveniently, the viewport
+        // size changes will push the controls off screen when they are at the bottom; see
+        // mViewport.height().
         nativeUpdateToolbarLayer(mNativePtr, resourceManager, R.id.control_container,
                 browserControlsBackgroundColor, R.drawable.textbox, browserControlsUrlBarAlpha,
-                offset, mViewport.height(), useTexture, forceHideAndroidBrowserControls);
+                fullscreenManager.getTopControlOffset(), mViewport.height(), useTexture,
+                showShadow, fullscreenManager.areBrowserControlsAtBottom());
 
         if (mProgressBarDrawingInfo == null) return;
         nativeUpdateProgressBar(mNativePtr,
@@ -247,7 +252,8 @@
             float topOffset,
             float viewHeight,
             boolean visible,
-            boolean showShadow);
+            boolean showShadow,
+            boolean browserControlsAtBottom);
     private native void nativeUpdateProgressBar(
             long nativeToolbarSceneLayer,
             int progressBarX,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/BarOverlapTapSuppression.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/BarOverlapTapSuppression.java
index 981cdd3..0e58598 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/BarOverlapTapSuppression.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/BarOverlapTapSuppression.java
@@ -73,10 +73,14 @@
         ChromeFullscreenManager fullscreenManager = mActivity.getFullscreenManager();
         if (fullscreenManager == null || currentTab == null) return 0.f;
 
-        float controlsOffset = fullscreenManager.getControlOffset();
-        float controlsHeight = fullscreenManager.getBrowserControlsHeight();
+        float topControlsOffset = fullscreenManager.getTopControlOffset();
+        float topControlsHeight = fullscreenManager.getTopControlsHeight();
+        float bottomControlsOffset = fullscreenManager.getBottomControlOffset();
+        float bottomControlsHeight = fullscreenManager.getBottomControlsHeight();
+
         float tabHeight = currentTab.getHeight();
-        return tabHeight - (controlsHeight + controlsOffset);
+        return (tabHeight - (topControlsHeight + topControlsOffset))
+                - (bottomControlsHeight - bottomControlsOffset);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index 7499d5f..b133dd5d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -914,7 +914,7 @@
     protected ChromeFullscreenManager createFullscreenManager() {
         return new ChromeFullscreenManager(this,
                 (ToolbarControlContainer) findViewById(R.id.control_container),
-                getTabModelSelector(), getControlContainerHeightResource(), true);
+                getTabModelSelector(), getControlContainerHeightResource(), true, false);
     }
 
     /** Sets the initial background color for the Tab, shown before the page content is ready. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java
index 16bfa081..267d700 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java
@@ -21,6 +21,7 @@
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.chrome.browser.instantapps.InstantAppsHandler;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabRedirectHandler;
 import org.chromium.chrome.browser.util.IntentUtils;
@@ -443,9 +444,20 @@
             }
         }
 
-        boolean shouldProxyForInstantApps = isExternalProtocol
-                && SUPERVISOR_PKG.equals(intent.getPackage())
+        boolean isDirectInstantAppsIntent = isExternalProtocol
+                && SUPERVISOR_PKG.equals(intent.getPackage());
+        boolean shouldProxyForInstantApps = isDirectInstantAppsIntent
                 && mDelegate.isSerpReferrer(params.getReferrerUrl(), params.getTab());
+        if (shouldProxyForInstantApps) {
+            intent.putExtra(InstantAppsHandler.IS_GOOGLE_SEARCH_REFERRER, true);
+        } else if (isDirectInstantAppsIntent) {
+            // For security reasons, we disable all intent:// URLs to Instant Apps that are
+            // not coming from SERP.
+            return OverrideUrlLoadingResult.NO_OVERRIDE;
+        } else {
+            // Make sure this extra is not sent unless we've done the verification.
+            intent.removeExtra(InstantAppsHandler.IS_GOOGLE_SEARCH_REFERRER);
+        }
 
         try {
             if (params.isIncognito() && !mDelegate.willChromeHandleIntent(intent)) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java
index 67c59bc..a49e8736 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java
@@ -35,6 +35,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver;
+import org.chromium.chrome.browser.util.ViewUtils;
 import org.chromium.chrome.browser.widget.ControlContainer;
 import org.chromium.content.browser.ContentVideoView;
 import org.chromium.content.browser.ContentViewCore;
@@ -62,7 +63,8 @@
     private final Activity mActivity;
     private final Window mWindow;
     private final Handler mHandler;
-    private final int mControlContainerHeight;
+    private final int mTopControlContainerHeight;
+    private final int mBottomControlContainerHeight;
 
     private final TabModelSelector mTabModelSelector;
     private final TabModelSelectorTabModelObserver mTabModelObserver;
@@ -100,6 +102,8 @@
     private boolean mBrowserControlsAndroidViewHidden;
     private final boolean mSupportsBrowserOverride;
 
+    private boolean mIsBottomControls;
+
     private final ArrayList<FullscreenListener> mListeners = new ArrayList<FullscreenListener>();
 
     /**
@@ -188,25 +192,18 @@
     }
 
     /**
-     * @return The ratio that the browser controls are off screen; this will be a number [0,1]
-     *         where 1 is completely hidden and 0 is completely shown.
-     */
-    private float getBrowserControlHiddenRatio() {
-        return mControlOffsetRatio;
-    }
-
-    /**
      * Creates an instance of the fullscreen mode manager.
      * @param activity The activity that supports fullscreen.
      * @param controlContainer Container holding the controls (Toolbar).
      * @param modelSelector The tab model selector that will be monitored for tab changes.
      * @param resControlContainerHeight The dimension resource ID for the control container height.
      * @param supportsBrowserOverride Whether we want to disable the token system used by the
-                                      browser.
+     *                                browser.
+     * @param isBottomControls Whether or not the browser controls are at the bottom of the screen.
      */
     public ChromeFullscreenManager(Activity activity, ControlContainer controlContainer,
             TabModelSelector modelSelector, int resControlContainerHeight,
-            boolean supportsBrowserOverride) {
+            boolean supportsBrowserOverride, boolean isBottomControls) {
         super(activity.getWindow());
 
         mActivity = activity;
@@ -242,12 +239,29 @@
         assert controlContainer != null;
         mControlContainer = controlContainer;
         Resources resources = mWindow.getContext().getResources();
-        mControlContainerHeight = resources.getDimensionPixelSize(resControlContainerHeight);
-        mRendererTopContentOffset = mControlContainerHeight;
+        mIsBottomControls = isBottomControls;
+
+        int controlContainerHeight = resources.getDimensionPixelSize(resControlContainerHeight);
+        if (mIsBottomControls) {
+            mTopControlContainerHeight = 0;
+            mBottomControlContainerHeight = controlContainerHeight;
+        } else {
+            mTopControlContainerHeight = controlContainerHeight;
+            mBottomControlContainerHeight = 0;
+        }
+
+        mRendererTopContentOffset = mTopControlContainerHeight;
         mSupportsBrowserOverride = supportsBrowserOverride;
         updateControlOffset();
     }
 
+    /**
+     * @return Whether or not the browser controls are attached to the bottom of the screen.
+     */
+    public boolean areBrowserControlsAtBottom() {
+        return mIsBottomControls;
+    }
+
     @Override
     public void onActivityStateChange(Activity activity, int newState) {
         if (newState == ActivityState.STOPPED) {
@@ -286,7 +300,7 @@
             @Override
             public void onEnterFullscreen() {
                 Tab tab = getTab();
-                if (getControlOffset() == -mControlContainerHeight) {
+                if (areBrowserControlsOffScreen()) {
                     // The browser controls are currently hidden.
                     getHtmlApiHandler().enterFullscreen(tab);
                 } else {
@@ -378,6 +392,21 @@
     }
 
     /**
+     * @return The ratio that the browser controls are off screen; this will be a number [0,1]
+     *         where 1 is completely hidden and 0 is completely shown.
+     */
+    private float getBrowserControlHiddenRatio() {
+        return mControlOffsetRatio;
+    }
+
+    /**
+     * @return True if the browser controls are completely off screen.
+     */
+    public boolean areBrowserControlsOffScreen() {
+        return getBrowserControlHiddenRatio() == 1.0f;
+    }
+
+    /**
      * @param remove Whether or not to forcefully remove the toolbar.
      */
     public void setBrowserControlsPermamentlyHidden(boolean remove) {
@@ -401,24 +430,41 @@
     }
 
     @Override
-    public int getBrowserControlsHeight() {
-        return mControlContainerHeight;
+    public int getTopControlsHeight() {
+        return mTopControlContainerHeight;
+    }
+
+    /**
+     * @return The height of the bottom controls in pixels.
+     */
+    public int getBottomControlsHeight() {
+        return mBottomControlContainerHeight;
     }
 
     @Override
     public float getContentOffset() {
         if (mBrowserControlsPermanentlyHidden) return 0;
-        return rendererContentOffset();
+        return mRendererTopContentOffset;
     }
 
     /**
      * @return The offset of the controls from the top of the screen.
      */
-    public float getControlOffset() {
-        if (mBrowserControlsPermanentlyHidden) return -getBrowserControlsHeight();
+    public float getTopControlOffset() {
+        if (mBrowserControlsPermanentlyHidden) return -getTopControlsHeight();
         // This is to avoid a problem with -0f in tests.
         if (mControlOffsetRatio == 0f) return 0f;
-        return mControlOffsetRatio * -getBrowserControlsHeight();
+        return mControlOffsetRatio * -getTopControlsHeight();
+    }
+
+    /**
+     * @return The offset of the controls from the bottom of the screen.
+     */
+    public float getBottomControlOffset() {
+        if (mBrowserControlsPermanentlyHidden) return getBottomControlsHeight();
+        if (mControlOffsetRatio == 0f) return 0f;
+        return mControlOffsetRatio * getBottomControlsHeight();
+
     }
 
     /**
@@ -431,10 +477,15 @@
     private void updateControlOffset() {
         float topOffsetRatio = 0;
 
-        // Inline Float.isNan with "x != x":
         final boolean isNaNBrowserControlOffset = Float.isNaN(mBrowserControlShownRatio);
-        final float rendererControlOffset =
-                Math.abs(rendererControlOffset() / controlContainerHeight());
+        float rendererControlOffset;
+        if (mIsBottomControls) {
+            rendererControlOffset =
+                    Math.abs(mRendererBottomControlOffset / getBottomControlsHeight());
+        } else {
+            rendererControlOffset = Math.abs(mRendererTopControlOffset / getTopControlsHeight());
+        }
+
         final boolean isNaNRendererControlOffset = Float.isNaN(rendererControlOffset);
         if (!isNaNBrowserControlOffset || !isNaNRendererControlOffset) {
             topOffsetRatio = Math.min(
@@ -463,25 +514,10 @@
     }
 
     /**
-     * Returns how tall the opaque portion of the control container is.
-     */
-    public float controlContainerHeight() {
-        return mControlContainerHeight;
-    }
-
-    private float rendererContentOffset() {
-        return mRendererTopContentOffset;
-    }
-
-    private float rendererControlOffset() {
-        return mRendererTopControlOffset;
-    }
-
-    /**
      * @return The visible offset of the content from the top of the screen.
      */
-    public float getVisibleContentOffset() {
-        return mControlContainerHeight + getControlOffset();
+    public float getTopVisibleContentOffset() {
+        return getTopControlsHeight() + getTopControlOffset();
     }
 
     /**
@@ -509,9 +545,9 @@
         if (mInGesture || mContentViewScrolling) return;
 
         // Update content viewport size only when the browser controls are not animating.
-        int contentOffset = (int) rendererContentOffset();
-        if (contentOffset != 0 && contentOffset != mControlContainerHeight) return;
-        viewCore.setTopControlsHeight(mControlContainerHeight, contentOffset > 0);
+        int contentOffset = (int) mRendererTopContentOffset;
+        if (contentOffset != 0 && contentOffset != getTopControlsHeight()) return;
+        viewCore.setTopControlsHeight(getTopControlsHeight(), contentOffset > 0);
     }
 
     @Override
@@ -520,7 +556,7 @@
         if (contentViewCore == null) return;
         ViewGroup view = contentViewCore.getContainerView();
 
-        float topViewsTranslation = (getControlOffset() + mControlContainerHeight);
+        float topViewsTranslation = getTopVisibleContentOffset();
         applyTranslationToTopChildViews(view, topViewsTranslation);
         applyMarginToFullChildViews(view, topViewsTranslation);
         updateContentViewViewportSize(contentViewCore);
@@ -540,13 +576,16 @@
     private void updateVisuals() {
         TraceEvent.begin("FullscreenManager:updateVisuals");
 
-        float offset = getControlOffset();
+        // Use bottom controls height if top controls have no height.
+        float offset = getTopControlOffset();
+        if (mIsBottomControls) offset = getBottomControlOffset();
+
         if (Float.compare(mPreviousControlOffset, offset) != 0) {
             mPreviousControlOffset = offset;
 
             scheduleVisibilityUpdate();
             if (shouldShowAndroidControls()) {
-                mControlContainer.getView().setTranslationY(getControlOffset());
+                mControlContainer.getView().setTranslationY(offset);
             }
 
             // Whether we need the compositor to draw again to update our animation.
@@ -554,13 +593,16 @@
             // scrolling.
             boolean needsAnimate = mControlAnimation != null || shouldShowAndroidControls();
             for (int i = 0; i < mListeners.size(); i++) {
+                // Since, in the case of bottom controls, the view is never translated, we don't
+                // need to change the information passed into this method.
+                // getTopVisibleContentOffset will return 0 which is the expected result.
                 mListeners.get(i).onVisibleContentOffsetChanged(
-                        getVisibleContentOffset(), needsAnimate);
+                        getTopVisibleContentOffset(), needsAnimate);
             }
         }
 
         final Tab tab = getTab();
-        if (tab != null && offset == -mControlContainerHeight && mIsEnteringPersistentModeState) {
+        if (tab != null && areBrowserControlsOffScreen() && mIsEnteringPersistentModeState) {
             getHtmlApiHandler().enterFullscreen(tab);
             mIsEnteringPersistentModeState = false;
         }
@@ -653,9 +695,9 @@
     public void setPositionsForTabToNonFullscreen() {
         Tab tab = getTab();
         if (tab == null || tab.isShowingBrowserControlsEnabled()) {
-            setPositionsForTab(0, 0, mControlContainerHeight);
+            setPositionsForTab(0, 0, getTopControlsHeight());
         } else {
-            setPositionsForTab(-mControlContainerHeight, 0, 0);
+            setPositionsForTab(-getTopControlsHeight(), getBottomControlsHeight(), 0);
         }
     }
 
@@ -670,12 +712,12 @@
         }
 
         float rendererTopControlOffset =
-                Math.round(Math.max(topControlsOffset, -mControlContainerHeight));
+                Math.round(Math.max(topControlsOffset, -getTopControlsHeight()));
         float rendererBottomControlOffset =
-                Math.round(Math.min(bottomControlsOffset, mControlContainerHeight));
+                Math.round(Math.min(bottomControlsOffset, getBottomControlsHeight()));
 
         float rendererTopContentOffset = Math.min(
-                Math.round(topContentOffset), rendererTopControlOffset + mControlContainerHeight);
+                Math.round(topContentOffset), rendererTopControlOffset + getTopControlsHeight());
 
         if (Float.compare(rendererTopControlOffset, mRendererTopControlOffset) == 0
                 && Float.compare(rendererBottomControlOffset, mRendererBottomControlOffset) == 0
@@ -698,7 +740,24 @@
      *         consumed.
      */
     public boolean onInterceptMotionEvent(MotionEvent e) {
-        return e.getY() < getControlOffset() + mControlContainerHeight
+        int bottomPosition;
+        int topPosition = 0;
+        float offset;
+
+        if (mIsBottomControls) {
+            int[] position = new int[2];
+            ViewUtils.getRelativeLayoutPosition(mControlContainer.getView().getRootView(),
+                    mControlContainer.getView(), position);
+
+            topPosition = position[1];
+            bottomPosition = topPosition + getBottomControlsHeight();
+            offset = getBottomControlOffset();
+        } else {
+            bottomPosition = getTopControlsHeight();
+            offset = getTopControlOffset();
+        }
+
+        return e.getY() < topPosition + offset && e.getY() > bottomPosition + offset
                 && !mBrowserControlsAndroidViewHidden;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenManager.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenManager.java
index 7e9c35c..b86776b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenManager.java
@@ -75,9 +75,9 @@
     public abstract void hideControlsPersistent(int token);
 
     /**
-     * @return The height of the browser controls in pixels.
+     * @return The height of the top controls in pixels.
      */
-    public abstract int getBrowserControlsHeight();
+    public abstract int getTopControlsHeight();
 
     /**
      * @return The offset of the content from the top of the screen.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/instantapps/InstantAppsHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/instantapps/InstantAppsHandler.java
index e512bed..203d39b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/instantapps/InstantAppsHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/instantapps/InstantAppsHandler.java
@@ -52,6 +52,9 @@
     protected static final String TRUSTED_REFERRER_PKG_EXTRA =
             "com.google.android.gms.instantapps.TRUSTED_REFERRER_PKG";
 
+    public static final String IS_GOOGLE_SEARCH_REFERRER =
+            "com.google.android.gms.instantapps.IS_GOOGLE_SEARCH_REFERRER";
+
     /** Finch experiment name. */
     private static final String INSTANT_APPS_EXPERIMENT_NAME = "InstantApps";
 
@@ -90,7 +93,7 @@
      * @param context The application context.
      * @return Whether the feature is enabled.
      */
-    private boolean isEnabled(Context context) {
+    protected boolean isEnabled(Context context) {
         // Will go away once the feature is enabled for everyone by default.
         StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
         try {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index cb26e7d..df79197 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -1274,7 +1274,7 @@
             int securityLevel, boolean isSmallDevice, boolean isOfflinePage) {
         // Both conditions should be met, because isOfflinePage might take longer to be cleared.
         if (securityLevel == ConnectionSecurityLevel.NONE && isOfflinePage) {
-            return R.drawable.offline_pin;
+            return R.drawable.offline_pin_round;
         }
         switch (securityLevel) {
             case ConnectionSecurityLevel.NONE:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
index 049f3a3..bcf321a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -2759,7 +2759,7 @@
                         LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
             }
 
-            lp.topMargin = mFullscreenManager.getBrowserControlsHeight();
+            lp.topMargin = mFullscreenManager.getTopControlsHeight();
             blimpView.setLayoutParams(lp);
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrCoreVersionChecker.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrCoreVersionChecker.java
new file mode 100644
index 0000000..2757b61
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrCoreVersionChecker.java
@@ -0,0 +1,16 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.vr_shell;
+
+/**
+ * Abstracts away the VrCoreVersionCheckerImpl class, which may or may not be present at runtime
+ * depending on compile flags.
+ */
+public interface VrCoreVersionChecker {
+    /**
+     * Test if VrCore version installed is compatible with Chromium.
+     */
+    boolean isVrCoreCompatible();
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrCoreVersionCheckerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrCoreVersionCheckerImpl.java
new file mode 100644
index 0000000..4715015
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrCoreVersionCheckerImpl.java
@@ -0,0 +1,44 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.vr_shell;
+
+import com.google.vr.ndk.base.Version;
+import com.google.vr.vrcore.base.api.VrCoreNotAvailableException;
+import com.google.vr.vrcore.base.api.VrCoreUtils;
+import com.google.vr.vrcore.base.api.VrCoreUtils.ConnectionResult;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.Log;
+import org.chromium.base.annotations.UsedByReflection;
+
+/**
+ * Helper class to check if VrCore version is compatible with Chromium.
+ */
+@UsedByReflection("VrShellDelegate.java")
+public class VrCoreVersionCheckerImpl implements VrCoreVersionChecker {
+    private static final String TAG = "VrCoreVersionChecker";
+
+    @UsedByReflection("VrShellDelegate.java")
+    public VrCoreVersionCheckerImpl() {
+    }
+
+    @Override
+    public boolean isVrCoreCompatible() {
+        try {
+            String vrCoreSdkLibraryVersionString = VrCoreUtils.getVrCoreSdkLibraryVersion(
+                    ContextUtils.getApplicationContext());
+            Version vrCoreSdkLibraryVersion = Version.parse(vrCoreSdkLibraryVersionString);
+            Version targetSdkLibraryVersion =
+                    Version.parse(com.google.vr.ndk.base.BuildConstants.VERSION);
+            if (!vrCoreSdkLibraryVersion.isAtLeast(targetSdkLibraryVersion)) {
+                throw new VrCoreNotAvailableException(ConnectionResult.SERVICE_OBSOLETE);
+            }
+            return true;
+        } catch (VrCoreNotAvailableException e) {
+            Log.e(TAG, "Unable to find a compatible VrCore", e);
+            return false;
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
index 317fa4e3..6ce2544 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
@@ -46,33 +46,27 @@
     private Class<? extends VrShell> mVrShellClass;
     private Class<? extends NonPresentingGvrContext> mNonPresentingGvrContextClass;
     private Class<? extends VrDaydreamApi> mVrDaydreamApiClass;
+    private Class<? extends VrCoreVersionChecker> mVrCoreVersionCheckerClass;
     private VrShell mVrShell;
     private NonPresentingGvrContext mNonPresentingGvrContext;
     private VrDaydreamApi mVrDaydreamApi;
+    private VrCoreVersionChecker mVrCoreVersionChecker;
     private boolean mInVr;
     private int mRestoreSystemUiVisibilityFlag = -1;
     private int mRestoreOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-    private String mVrExtra;
     private long mNativeVrShellDelegate;
     private Tab mTab;
 
+    // TODO(bshe): This should be replaced by string provided by NDK. Currently, it only available
+    // in SDK and we don't want add dependency to SDK just to get this string. So hard code it here.
+    private static final String DAYDREAM_VR_EXTRA = "android.intent.extra.VR_LAUNCH";
     private static final String DAYDREAM_DON_AUTO_TRANSITION =
             "org.chromium.chrome.browser.vr_shell.DAYDREAM_DON_AUTO_TRANSITION";
 
     public VrShellDelegate(ChromeTabbedActivity activity) {
         mActivity = activity;
-
-        mVrAvailable = maybeFindVrClasses();
-        if (mVrAvailable) {
-            try {
-                mVrExtra = (String) mVrShellClass.getField("VR_EXTRA").get(null);
-            } catch (IllegalAccessException | IllegalArgumentException | NoSuchFieldException e) {
-                Log.e(TAG, "Unable to read VR_EXTRA field", e);
-                mVrAvailable = false;
-            }
-            createVrDaydreamApi();
-        }
-
+        mVrAvailable = maybeFindVrClasses() && isVrCoreCompatible();
+        createVrDaydreamApi();
         mTabObserver = new EmptyTabObserver() {
             @Override
             public void onContentChanged(Tab tab) {
@@ -111,11 +105,14 @@
                             "org.chromium.chrome.browser.vr_shell.NonPresentingGvrContextImpl");
             mVrDaydreamApiClass = (Class<? extends VrDaydreamApi>) Class.forName(
                     "org.chromium.chrome.browser.vr_shell.VrDaydreamApiImpl");
+            mVrCoreVersionCheckerClass = (Class<? extends VrCoreVersionChecker>) Class.forName(
+                    "org.chromium.chrome.browser.vr_shell.VrCoreVersionCheckerImpl");
             return true;
         } catch (ClassNotFoundException e) {
             mVrShellClass = null;
             mNonPresentingGvrContextClass = null;
             mVrDaydreamApiClass = null;
+            mVrCoreVersionCheckerClass = null;
             return false;
         }
     }
@@ -323,6 +320,28 @@
         mTab.updateBrowserControlsState(BrowserControlsState.SHOWN, true);
     }
 
+    private boolean isVrCoreCompatible() {
+        if (mVrCoreVersionChecker != null) {
+            return mVrCoreVersionChecker.isVrCoreCompatible();
+        }
+
+        if (mVrCoreVersionCheckerClass == null) {
+            return false;
+        }
+
+        try {
+            Constructor<?> mVrCoreVersionCheckerConstructor =
+                    mVrCoreVersionCheckerClass.getConstructor();
+            mVrCoreVersionChecker =
+                    (VrCoreVersionChecker) mVrCoreVersionCheckerConstructor.newInstance();
+        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
+                | InvocationTargetException | NoSuchMethodException e) {
+            Log.e(TAG, "Unable to instantiate VrCoreVersionChecker", e);
+            return false;
+        }
+        return mVrCoreVersionChecker.isVrCoreCompatible();
+    }
+
     private boolean createVrDaydreamApi() {
         if (!mVrAvailable) return false;
 
@@ -406,7 +425,7 @@
     public boolean isVrIntent(Intent intent) {
         if (intent == null) return false;
         return intent.getBooleanExtra(DAYDREAM_DON_AUTO_TRANSITION, false)
-                || intent.getBooleanExtra(mVrExtra, false);
+                || intent.getBooleanExtra(DAYDREAM_VR_EXTRA, false);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
index 1ed2b95..7b82f344 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
@@ -54,7 +54,7 @@
     private static final String TAG = "VrShellImpl";
 
     @UsedByReflection("VrShellDelegate.java")
-    public static final String VR_EXTRA = com.google.vr.sdk.base.Constants.EXTRA_VR_LAUNCH;
+    public static final String VR_EXTRA = "android.intent.extra.VR_LAUNCH";
 
     private Activity mActivity;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/FullScreenActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/FullScreenActivity.java
index fc893d1..d6accdf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/FullScreenActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/FullScreenActivity.java
@@ -212,6 +212,6 @@
     protected ChromeFullscreenManager createFullscreenManager() {
         return new ChromeFullscreenManager(this,
                 (ControlContainer) findViewById(R.id.control_container),
-                getTabModelSelector(), getControlContainerHeightResource(), true);
+                getTabModelSelector(), getControlContainerHeightResource(), true, false);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
index d378ae3e..1a9c547 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
@@ -575,7 +575,7 @@
         return new ChromeFullscreenManager(this,
                 (ControlContainer) findViewById(R.id.control_container),
                 getTabModelSelector(), getControlContainerHeightResource(),
-                false /* supportsBrowserOverride */);
+                false /* supportsBrowserOverride */, false /* isBottomControls */);
     }
 
     @Override
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 7533079..60b89eb 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -997,6 +997,7 @@
   "java/src/org/chromium/chrome/browser/util/UrlUtilities.java",
   "java/src/org/chromium/chrome/browser/util/ViewUtils.java",
   "java/src/org/chromium/chrome/browser/vr_shell/NonPresentingGvrContext.java",
+  "java/src/org/chromium/chrome/browser/vr_shell/VrCoreVersionChecker.java",
   "java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApi.java",
   "java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java",
   "java/src/org/chromium/chrome/browser/vr_shell/VrShell.java",
@@ -1106,6 +1107,7 @@
 
 chrome_vr_java_sources = [
   "java/src/org/chromium/chrome/browser/vr_shell/NonPresentingGvrContextImpl.java",
+  "java/src/org/chromium/chrome/browser/vr_shell/VrCoreVersionCheckerImpl.java",
   "java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApiImpl.java",
   "java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java",
   "java/src/org/chromium/chrome/browser/vr_shell/VrWindowAndroid.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/MockLayoutHost.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/MockLayoutHost.java
index 702e8f1..d4cfc6b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/MockLayoutHost.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/MockLayoutHost.java
@@ -112,7 +112,7 @@
     }
 
     @Override
-    public int getBrowserControlsHeightPixels() {
+    public int getTopControlsHeightPixels() {
         return 0;
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java
index 38415c3..27d2c01 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java
@@ -152,7 +152,7 @@
 
         ChromeFullscreenManager fullscreenManager = getActivity().getFullscreenManager();
         fullscreenManager.setAnimationDurationsForTest(1, 1);
-        int browserControlsHeight = fullscreenManager.getBrowserControlsHeight();
+        int browserControlsHeight = fullscreenManager.getTopControlsHeight();
 
         Tab tab = getActivity().getActivityTab();
         View view = tab.getView();
@@ -182,7 +182,7 @@
         final ChromeFullscreenManager fullscreenManager = getActivity().getFullscreenManager();
         disableBrowserOverrides();
 
-        assertEquals(fullscreenManager.getControlOffset(), 0f);
+        assertEquals(fullscreenManager.getTopControlOffset(), 0f);
 
         waitForBrowserControlsToBeMoveable(getActivity().getActivityTab());
 
@@ -200,7 +200,7 @@
         final ChromeFullscreenManager fullscreenManager = getActivity().getFullscreenManager();
         disableBrowserOverrides();
 
-        assertEquals(fullscreenManager.getControlOffset(), 0f);
+        assertEquals(fullscreenManager.getTopControlOffset(), 0f);
 
         // Detect layouts. Note this doesn't actually need to be atomic (just final).
         final AtomicInteger layoutCount = new AtomicInteger();
@@ -251,9 +251,9 @@
 
         final ChromeFullscreenManager fullscreenManager = getActivity().getFullscreenManager();
         disableBrowserOverrides();
-        int browserControlsHeight = fullscreenManager.getBrowserControlsHeight();
+        int browserControlsHeight = fullscreenManager.getTopControlsHeight();
 
-        assertEquals(fullscreenManager.getControlOffset(), 0f);
+        assertEquals(fullscreenManager.getTopControlOffset(), 0f);
 
         float dragX = 50f;
         float dragStartY = browserControlsHeight * 2;
@@ -275,7 +275,7 @@
         ChromeFullscreenManager fullscreenManager = getActivity().getFullscreenManager();
         fullscreenManager.setAnimationDurationsForTest(1, 1);
         waitForNoBrowserBrowserControlsOffset();
-        assertEquals(fullscreenManager.getControlOffset(), 0f);
+        assertEquals(fullscreenManager.getTopControlOffset(), 0f);
 
         scrollBrowserControls(false);
 
@@ -336,11 +336,11 @@
         startMainActivityWithURL(LONG_HTML_WITH_AUTO_FOCUS_INPUT_TEST_PAGE);
 
         ChromeFullscreenManager fullscreenManager = getActivity().getFullscreenManager();
-        assertEquals(fullscreenManager.getControlOffset(), 0f);
+        assertEquals(fullscreenManager.getTopControlOffset(), 0f);
 
         fullscreenManager.setAnimationDurationsForTest(1, 1);
 
-        int browserControlsHeight = fullscreenManager.getBrowserControlsHeight();
+        int browserControlsHeight = fullscreenManager.getTopControlsHeight();
         float dragX = 50f;
         float dragStartY = browserControlsHeight * 3;
         float dragEndY = dragStartY - browserControlsHeight * 2;
@@ -349,7 +349,7 @@
         dragTo(dragX, dragX, dragStartY, dragEndY, 100, downTime);
         dragEnd(dragX, dragEndY, downTime);
         waitForNoBrowserBrowserControlsOffset();
-        assertEquals(fullscreenManager.getControlOffset(), 0f);
+        assertEquals(fullscreenManager.getTopControlOffset(), 0f);
 
         Tab tab = getActivity().getActivityTab();
         singleClickView(tab.getView());
@@ -362,7 +362,7 @@
 
     private void scrollBrowserControls(boolean show) throws InterruptedException {
         ChromeFullscreenManager fullscreenManager = getActivity().getFullscreenManager();
-        int browserControlsHeight = fullscreenManager.getBrowserControlsHeight();
+        int browserControlsHeight = fullscreenManager.getTopControlsHeight();
 
         waitForPageToBeScrollable(getActivity().getActivityTab());
 
@@ -390,7 +390,7 @@
         CriteriaHelper.pollUiThread(Criteria.equals(position, new Callable<Float>() {
             @Override
             public Float call() {
-                return fullscreenManager.getControlOffset();
+                return fullscreenManager.getTopControlOffset();
             }
         }));
     }
@@ -428,7 +428,7 @@
 
         final CallbackHelper contentMovedCallback = new CallbackHelper();
         final ChromeFullscreenManager fullscreenManager = getActivity().getFullscreenManager();
-        final float initialVisibleContentOffset = fullscreenManager.getVisibleContentOffset();
+        final float initialVisibleContentOffset = fullscreenManager.getTopVisibleContentOffset();
 
         fullscreenManager.addListener(new FullscreenListener() {
             @Override
@@ -452,7 +452,7 @@
         float dragStartY = tab.getView().getHeight() - 50f;
 
         for (int i = 0; i < 10; i++) {
-            float dragEndY = dragStartY - fullscreenManager.getBrowserControlsHeight();
+            float dragEndY = dragStartY - fullscreenManager.getTopControlsHeight();
 
             long downTime = SystemClock.uptimeMillis();
             dragStart(dragX, dragStartY, downTime);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageSavePageLaterEvaluationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageSavePageLaterEvaluationTest.java
index 84152a1..550ee62 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageSavePageLaterEvaluationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageSavePageLaterEvaluationTest.java
@@ -77,6 +77,7 @@
     private static final String NEW_LINE = System.getProperty("line.separator");
     private static final String DELIMITER = ";";
     private static final String CONFIG_FILE_PATH = "paquete/test_config";
+    private static final String SAVED_PAGES_EXTERNAL_PATH = "paquete/archives";
     private static final String INPUT_FILE_PATH = "paquete/offline_eval_urls.txt";
     private static final String LOG_OUTPUT_FILE_PATH = "paquete/offline_eval_logs.txt";
     private static final String RESULT_OUTPUT_FILE_PATH = "paquete/offline_eval_results.txt";
@@ -165,6 +166,34 @@
     }
 
     /**
+     * Get the directory on external storage for storing saved pages.
+     */
+    private File getExternalArchiveDir() {
+        File externalArchiveDir =
+                new File(Environment.getExternalStorageDirectory(), SAVED_PAGES_EXTERNAL_PATH);
+        try {
+            // Clear the old archive folder.
+            if (externalArchiveDir.exists()) {
+                String[] files = externalArchiveDir.list();
+                if (files != null) {
+                    for (String file : files) {
+                        File currentFile = new File(externalArchiveDir.getPath(), file);
+                        if (!currentFile.delete()) {
+                            logError(file + " cannot be deleted when clearing previous archives.");
+                        }
+                    }
+                }
+            }
+            if (!externalArchiveDir.mkdir()) {
+                logError("Cannot create directory on external storage to store saved pages.");
+            }
+        } catch (SecurityException e) {
+            logError("Failed to delete or create external archive folder!");
+        }
+        return externalArchiveDir;
+    }
+
+    /**
      * Logs error in both console and output file.
      */
     private void logError(String error) {
@@ -172,6 +201,7 @@
         if (mLogOutput != null) {
             try {
                 mLogOutput.write(error + NEW_LINE);
+                mLogOutput.flush();
             } catch (Exception e) {
                 Log.e(TAG, e.getMessage(), e);
             }
@@ -404,6 +434,7 @@
             if (!completed) {
                 logError("Test terminated before all requests completed.");
             }
+            File externalArchiveDir = getExternalArchiveDir();
             for (int i = 0; i < mRequestMetadata.size(); i++) {
                 RequestMetadata metadata = mRequestMetadata.valueAt(i);
                 long requestId = metadata.mId;
@@ -420,6 +451,12 @@
                 output.write(metadata.mUrl + DELIMITER + statusToString(status) + DELIMITER
                         + page.getFileSize() / 1000 + " KB" + DELIMITER
                         + metadata.mTimeDelta.getTimeDelta() + NEW_LINE);
+                // Move the page to external storage if external archive exists.
+                File originalPage = new File(page.getFilePath());
+                File externalPage = new File(externalArchiveDir, originalPage.getName());
+                if (!OfflinePageUtils.copyToShareableLocation(originalPage, externalPage)) {
+                    logError("Saved page for url " + page.getUrl() + " cannot be moved.");
+                }
             }
             output.write(String.format(
                     "Total requested URLs: %d, Completed: %d, Failed: %d, Failure Rate: %.2f%%"
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 9376ef7e..68514623 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -6393,6 +6393,18 @@
   <message name="IDS_ARC_OPT_IN_DIALOG_BACKUP_RESTORE" desc="Message in the opt-in dialog for users to enable Backup and Restore for Android apps.">
     Automatically back up and restore Play app data to Google Drive. <ph name="BEGIN_LINK1">&lt;a href="#" id="learn-more-link-backup-restore"&gt;</ph>Learn More<ph name="END_LINK1">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph>
   </message>
+  <message name="IDS_ARC_OOBE_TERMS_DIALOG_METRICS_MANAGED_ENABLED" desc="Message in the Arc Terms OOBE dialog in case metrics are managed on the device and on.">
+    This device currently sends diagnostic and usage data to Google. This setting is enforced by the owner. <ph name="BEGIN_LINK1">&lt;a href="#" id="learn-more-link-metrics"&gt;</ph>Learn More<ph name="END_LINK1">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph>
+  </message>
+  <message name="IDS_ARC_OOBE_TERMS_DIALOG_METRICS_MANAGED_DISABLED" desc="Message in the Arc Terms OOBE dialog in case metrics are enabled on the device and off.">
+    The owner may choose to send diagnostic and usage data for this device to Google. <ph name="BEGIN_LINK1">&lt;a href="#" id="learn-more-link-metrics"&gt;</ph>Learn More<ph name="END_LINK1">&lt;a&gt;<ex>&lt;/a&gt;</ex></ph>
+  </message>
+  <message name="IDS_ARC_OOBE_TERMS_DIALOG_METRICS_ENABLED" desc="Message in the Arc Terms OOBE dialog in case metrics are already enabled on the device. User has no way to deactivate them using this dialog.">
+    This device currently sends diagnostic and usage data to Google. You can change this at any time in your device settings. <ph name="BEGIN_LINK1">&lt;a href="#" id="learn-more-link-metrics"&gt;</ph>Learn More<ph name="END_LINK1">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph>
+  </message>
+  <message name="IDS_ARC_OOBE_TERMS_DIALOG_METRICS_DISABLED" desc="Message in the Arc Terms OOBE dialog in case metrics are disabled on the device. User has an option to active them using this dialog">
+    Automatically send diagnostic and usage data to Google. You can change this at any time in your device settings. <ph name="BEGIN_LINK1">&lt;a href="#" id="learn-more-link-metrics"&gt;</ph>Learn More<ph name="END_LINK1">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph>
+  </message>
   <message name="IDS_ARC_OPT_IN_LOCATION_SETTING" desc="Message in the opt-in dialog for Android apps for the user to turn on Google location services">
     Let Google’s location service help apps find your location quickly and accurately, which can reduce battery consumption. Anonymous location data will be sent to Google, even when no apps are running. <ph name="BEGIN_LINK1">&lt;a href="#" id="learn-more-link-location-service"&gt;</ph>Learn More<ph name="END_LINK1">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph>
   </message>
@@ -6465,6 +6477,27 @@
   <message name="IDS_ARC_CRITICALLY_LOW_DISK_NOTIFICATION_MESSAGE" desc="Notification text warning users that they are critically low on disk space and Android apps can not be launched.">
     Free up disk space to launch Android apps.
   </message>
+  <message name="IDS_ARC_OOBE_TERMS_HEADING" desc="Heading of the Arc Terms OOBE dialog.">
+    Google Play Terms of Service
+  </message>
+  <message name="IDS_ARC_OOBE_TERMS_LOADING" desc="Loading message of the Arc Terms OOBE dialog.">
+    Loading...
+  </message>
+  <message name="IDS_ARC_OOBE_TERMS_LOAD_ERROR" desc="Load error message of the Arc Terms OOBE dialog.">
+    Google Play Terms of Service cannot be loaded. Please retry.
+  </message>
+  <message name="IDS_ARC_OOBE_TERMS_BUTTON_ACCEPT" desc="Accept button of the Arc Terms OOBE dialog.">
+    Agree
+  </message>
+  <message name="IDS_ARC_OOBE_TERMS_BUTTON_SKIP" desc="Skip button of the Arc Terms OOBE dialog.">
+    Skip
+  </message>
+  <message name="IDS_ARC_OOBE_TERMS_BUTTON_RETRY" desc="Retry button of the Arc Terms OOBE dialog that reloads Play Store terms.">
+    Retry
+  </message>
+  <message name="IDS_ARC_OOBE_TERMS_POPUP_HELP_CLOSE_BUTTON" desc="Close button of the popup local help of Arc Play Store Terms OOBE dialog.">
+    Close
+  </message>
   <message name="IDS_FLAGS_ARC_BOOT_COMPLETED" desc="Name of the flag for blocking the ACTION_BOOT_COMPLETED broadcast for third-party apps on ARC.">
     Load Android apps automatically
   </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 52e761c..6a95507 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -9,6 +9,7 @@
 import("//build/split_static_library.gni")
 import("//chrome/common/features.gni")
 import("//components/os_crypt/features.gni")
+import("//extensions/features/features.gni")
 import("//media/media_options.gni")
 import("//net/features.gni")
 import("//ppapi/features/features.gni")
@@ -606,8 +607,6 @@
     "metrics/time_ticks_experiment_win.h",
     "metrics/variations/chrome_variations_service_client.cc",
     "metrics/variations/chrome_variations_service_client.h",
-    "metrics/variations/variations_registry_syncer_win.cc",
-    "metrics/variations/variations_registry_syncer_win.h",
     "mod_pagespeed/mod_pagespeed_metrics.cc",
     "mod_pagespeed/mod_pagespeed_metrics.h",
     "native_window_notification_source.h",
@@ -1432,6 +1431,7 @@
     "//device/power_save_blocker",
     "//device/usb/mojo",
     "//device/usb/public/interfaces",
+    "//extensions/features",
     "//google_apis",
     "//gpu/config",
     "//media",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 6bc62ef..a368b31 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -24,6 +24,7 @@
   "+extensions/browser",
   "+extensions/common",
   "+extensions/components/javascript_dialog_extensions_client",
+  "+extensions/features",
   "+extensions/grit",
   "+extensions/test",
   "+gin",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index e21926d..4b07b0d 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -68,6 +68,7 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/feature_h264_with_openh264_ffmpeg.h"
 #include "content/public/common/features.h"
+#include "extensions/features/features.h"
 #include "gin/public/gin_features.h"
 #include "media/audio/audio_features.h"
 #include "media/base/media_switches.h"
@@ -104,7 +105,7 @@
 #include "ui/app_list/app_list_switches.h"
 #endif  // ENABLE_APP_LIST
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/common/switches.h"
 #endif  // ENABLE_EXTENSIONS
 
@@ -721,7 +722,7 @@
      IDS_FLAGS_NACL_DEBUG_MASK_DESCRIPTION, kOsDesktop,
      MULTI_VALUE_TYPE(kNaClDebugMaskChoices)},
 #endif  // DISABLE_NACL
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     {"extension-apis", IDS_FLAGS_EXPERIMENTAL_EXTENSION_APIS_NAME,
      IDS_FLAGS_EXPERIMENTAL_EXTENSION_APIS_DESCRIPTION, kOsDesktop,
      SINGLE_VALUE_TYPE(extensions::switches::kEnableExperimentalExtensionApis)},
@@ -732,7 +733,7 @@
     {"enable-fast-unload", IDS_FLAGS_FAST_UNLOAD_NAME,
      IDS_FLAGS_FAST_UNLOAD_DESCRIPTION, kOsAll,
      SINGLE_VALUE_TYPE(switches::kEnableFastUnload)},
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     {"enable-app-window-controls", IDS_FLAGS_APP_WINDOW_CONTROLS_NAME,
      IDS_FLAGS_APP_WINDOW_CONTROLS_DESCRIPTION, kOsDesktop,
      SINGLE_VALUE_TYPE(extensions::switches::kEnableAppWindowControls)},
@@ -830,7 +831,7 @@
      IDS_FLAGS_EXPERIMENTAL_WEB_PLATFORM_FEATURES_NAME,
      IDS_FLAGS_EXPERIMENTAL_WEB_PLATFORM_FEATURES_DESCRIPTION, kOsAll,
      SINGLE_VALUE_TYPE(switches::kEnableExperimentalWebPlatformFeatures)},
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     {"enable-ble-advertising-in-apps",
      IDS_FLAGS_BLE_ADVERTISING_IN_EXTENSIONS_NAME,
      IDS_FLAGS_BLE_ADVERTISING_IN_EXTENSIONS_DESCRIPTION, kOsCrOS,
@@ -1310,7 +1311,7 @@
      SINGLE_DISABLE_VALUE_TYPE(
          views::switches::kDisableViewsRectBasedTargeting)},
 #endif  // TOOLKIT_VIEWS
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     {"enable-apps-show-on-first-paint", IDS_FLAGS_APPS_SHOW_ON_FIRST_PAINT_NAME,
      IDS_FLAGS_APPS_SHOW_ON_FIRST_PAINT_DESCRIPTION, kOsDesktop,
      SINGLE_VALUE_TYPE(extensions::switches::kEnableAppsShowOnFirstPaint)},
@@ -1381,7 +1382,7 @@
      IDS_FLAGS_EXTENSION_CONTENT_VERIFICATION_NAME,
      IDS_FLAGS_EXTENSION_CONTENT_VERIFICATION_DESCRIPTION, kOsDesktop,
      MULTI_VALUE_TYPE(kExtensionContentVerificationChoices)},
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     {"extension-active-script-permission",
      IDS_FLAGS_USER_CONSENT_FOR_EXTENSION_SCRIPTS_NAME,
      IDS_FLAGS_USER_CONSENT_FOR_EXTENSION_SCRIPTS_DESCRIPTION, kOsAll,
@@ -1397,7 +1398,7 @@
     {"enable-hotword-hardware", IDS_FLAGS_EXPERIMENTAL_HOTWORD_HARDWARE_NAME,
      IDS_FLAGS_EXPERIMENTAL_HOTWORD_HARDWARE_DESCRIPTION, kOsCrOS,
      SINGLE_VALUE_TYPE(switches::kEnableExperimentalHotwordHardware)},
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     {"enable-embedded-extension-options",
      IDS_FLAGS_EMBEDDED_EXTENSION_OPTIONS_NAME,
      IDS_FLAGS_EMBEDDED_EXTENSION_OPTIONS_DESCRIPTION, kOsDesktop,
@@ -1411,7 +1412,7 @@
     {"drop-sync-credential", IDS_FLAGS_DROP_SYNC_CREDENTIAL_NAME,
      IDS_FLAGS_DROP_SYNC_CREDENTIAL_DESCRIPTION, kOsAll,
      FEATURE_VALUE_TYPE(password_manager::features::kDropSyncCredential)},
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     {"enable-extension-action-redesign",
      IDS_FLAGS_EXTENSION_ACTION_REDESIGN_NAME,
      IDS_FLAGS_EXTENSION_ACTION_REDESIGN_DESCRIPTION, kOsDesktop,
@@ -1764,7 +1765,7 @@
     {"force-ui-direction", IDS_FLAGS_FORCE_UI_DIRECTION_NAME,
      IDS_FLAGS_FORCE_UI_DIRECTION_DESCRIPTION, kOsAll,
      MULTI_VALUE_TYPE(kForceUIDirectionChoices)},
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     {"enable-md-extensions", IDS_FLAGS_ENABLE_MATERIAL_DESIGN_EXTENSIONS_NAME,
      IDS_FLAGS_ENABLE_MATERIAL_DESIGN_EXTENSIONS_DESCRIPTION, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kMaterialDesignExtensions)},
@@ -1837,7 +1838,7 @@
      IDS_FLAG_DISABLE_AUDIO_FOR_DESKTOP_SHARE,
      IDS_FLAG_DISABLE_AUDIO_FOR_DESKTOP_SHARE_DESCRIPTION, kOsAll,
      SINGLE_VALUE_TYPE(switches::kDisableAudioSupportForDesktopShare)},
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     {"tab-for-desktop-share", IDS_FLAG_DISABLE_TAB_FOR_DESKTOP_SHARE,
      IDS_FLAG_DISABLE_TAB_FOR_DESKTOP_SHARE_DESCRIPTION, kOsAll,
      SINGLE_VALUE_TYPE(extensions::switches::kDisableTabForDesktopShare)},
diff --git a/chrome/browser/android/DEPS b/chrome/browser/android/DEPS
index 152f1d1..14ac2dc 100644
--- a/chrome/browser/android/DEPS
+++ b/chrome/browser/android/DEPS
@@ -11,6 +11,7 @@
   "+components/toolbar",
   "+components/web_contents_delegate_android",
   "+sandbox/linux/seccomp-bpf/sandbox_bpf.h",
+  "+third_party/gvr-android-sdk",
 ]
 
 specific_include_rules = {
diff --git a/chrome/browser/android/chrome_jni_registrar.cc b/chrome/browser/android/chrome_jni_registrar.cc
index 7d0ba84d..015b8afc 100644
--- a/chrome/browser/android/chrome_jni_registrar.cc
+++ b/chrome/browser/android/chrome_jni_registrar.cc
@@ -182,6 +182,9 @@
 #if defined(ENABLE_VR_SHELL) || defined(ENABLE_WEBVR)
 #include "chrome/browser/android/vr_shell/vr_shell.h"
 #include "chrome/browser/android/vr_shell/vr_shell_delegate.h"
+#include "third_party/gvr-android-sdk/display_synchronizer_jni.h"
+#include "third_party/gvr-android-sdk/gvr_api_jni.h"
+#include "third_party/gvr-android-sdk/native_callbacks_jni.h"
 #endif
 
 #if !defined(OFFICIAL_BUILD)
@@ -399,6 +402,11 @@
 #if defined(ENABLE_VR_SHELL) || defined(ENABLE_WEBVR)
     {"VrShell", vr_shell::RegisterVrShell},
     {"VrShellDelegate", vr_shell::RegisterVrShellDelegate},
+    {"DisplaySynchronizer",
+     DisplaySynchronizer::RegisterDisplaySynchronizerNatives},
+    {"GvrApi", GvrApi::RegisterGvrApiNatives},
+    {"NativeCallbacks",
+     NativeCallbacks::RegisterNativeCallbacksNatives},
 #endif
     {"WarmupManager", RegisterWarmupManager},
     {"WebApkInstaller", WebApkInstaller::Register},
diff --git a/chrome/browser/android/compositor/layer/tab_layer.cc b/chrome/browser/android/compositor/layer/tab_layer.cc
index 47aaf8a0..578a0b83 100644
--- a/chrome/browser/android/compositor/layer/tab_layer.cc
+++ b/chrome/browser/android/compositor/layer/tab_layer.cc
@@ -93,6 +93,7 @@
 
 void TabLayer::SetProperties(int id,
                              bool can_use_live_layer,
+                             bool browser_controls_at_bottom,
                              int toolbar_resource_id,
                              int close_button_resource_id,
                              int shadow_resource_id,
@@ -141,7 +142,6 @@
                              float toolbar_alpha,
                              float toolbar_y_offset,
                              float side_border_scale,
-                             bool attach_content,
                              bool inset_border) {
   if (alpha <= 0) {
     layer_->SetHideLayerAndSubtree(true);
@@ -236,7 +236,8 @@
                                toolbar_textbox_alpha,
                                view_height,
                                false,
-                               false);
+                               false,
+                               browser_controls_at_bottom);
   toolbar_layer_->UpdateProgressBar(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
 
   float toolbar_impact_height = 0;
@@ -522,7 +523,7 @@
     close_button_->SetOpacity(close_alpha * close_alpha * border_alpha);
   }
 
-  if (content_visible && attach_content) {
+  if (content_visible) {
     {
       // content_ and back_logo_ Transforms
       gfx::Transform transform;
diff --git a/chrome/browser/android/compositor/layer/tab_layer.h b/chrome/browser/android/compositor/layer/tab_layer.h
index 0c078f29..a57862ac 100644
--- a/chrome/browser/android/compositor/layer/tab_layer.h
+++ b/chrome/browser/android/compositor/layer/tab_layer.h
@@ -47,6 +47,7 @@
 
   void SetProperties(int id,
                      bool can_use_live_layer,
+                     bool browser_controls_at_bottom,
                      int toolbar_resource_id,
                      int close_button_resource_id,
                      int shadow_resource_id,
@@ -95,7 +96,6 @@
                      float toolbar_alpha,
                      float toolbar_y_offset,
                      float side_border_scale,
-                     bool attach_content,
                      bool inset_border);
 
   bool is_incognito() const { return incognito_; }
diff --git a/chrome/browser/android/compositor/layer/toolbar_layer.cc b/chrome/browser/android/compositor/layer/toolbar_layer.cc
index 2c92657..f74d676 100644
--- a/chrome/browser/android/compositor/layer/toolbar_layer.cc
+++ b/chrome/browser/android/compositor/layer/toolbar_layer.cc
@@ -4,12 +4,10 @@
 
 #include "chrome/browser/android/compositor/layer/toolbar_layer.h"
 
-#include "base/feature_list.h"
 #include "cc/layers/nine_patch_layer.h"
 #include "cc/layers/solid_color_layer.h"
 #include "cc/layers/ui_resource_layer.h"
 #include "cc/resources/scoped_ui_resource.h"
-#include "chrome/browser/android/chrome_feature_list.h"
 #include "content/public/browser/android/compositor.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/android/resources/resource_manager.h"
@@ -35,7 +33,8 @@
     float url_bar_alpha,
     float view_height,
     bool show_debug,
-    bool clip_shadow) {
+    bool clip_shadow,
+    bool browser_controls_at_bottom) {
   ui::ResourceManager::Resource* resource =
       resource_manager_->GetResource(ui::ANDROID_RESOURCE_TYPE_DYNAMIC,
                                      toolbar_resource_id);
@@ -60,7 +59,7 @@
   toolbar_root_->SetBounds(resource->padding.size());
 
   gfx::PointF background_position(resource->padding.origin());
-  if (is_chrome_home_enabled_) {
+  if (browser_controls_at_bottom) {
     float layer_offset =
         resource->size.height() - resource->padding.size().height();
     layer_->SetPosition(gfx::PointF(0, view_height));
@@ -154,9 +153,7 @@
 }
 
 ToolbarLayer::ToolbarLayer(ui::ResourceManager* resource_manager)
-    : is_chrome_home_enabled_(
-          base::FeatureList::IsEnabled(chrome::android::kChromeHomeFeature)),
-      resource_manager_(resource_manager),
+    : resource_manager_(resource_manager),
       layer_(cc::Layer::Create()),
       toolbar_root_(cc::Layer::Create()),
       toolbar_background_layer_(cc::SolidColorLayer::Create()),
@@ -193,10 +190,6 @@
   debug_layer_->SetIsDrawable(true);
   debug_layer_->SetBackgroundColor(SK_ColorGREEN);
   debug_layer_->SetOpacity(0.5f);
-
-
-  is_chrome_home_enabled_ =
-      base::FeatureList::IsEnabled(chrome::android::kChromeHomeFeature);
 }
 
 ToolbarLayer::~ToolbarLayer() {
diff --git a/chrome/browser/android/compositor/layer/toolbar_layer.h b/chrome/browser/android/compositor/layer/toolbar_layer.h
index 7ad33431..8ae38add 100644
--- a/chrome/browser/android/compositor/layer/toolbar_layer.h
+++ b/chrome/browser/android/compositor/layer/toolbar_layer.h
@@ -37,7 +37,8 @@
                     float url_bar_alpha,
                     float view_height,
                     bool show_debug,
-                    bool clip_shadow);
+                    bool clip_shadow,
+                    bool browser_controls_at_bottom);
 
   void UpdateProgressBar(int progress_bar_x,
                          int progress_bar_y,
@@ -55,8 +56,6 @@
   ~ToolbarLayer() override;
 
  private:
-  bool is_chrome_home_enabled_;
-
   ui::ResourceManager* resource_manager_;
 
   scoped_refptr<cc::Layer> layer_;
diff --git a/chrome/browser/android/compositor/scene_layer/tab_list_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/tab_list_scene_layer.cc
index 8907651..7f19d2f5 100644
--- a/chrome/browser/android/compositor/scene_layer/tab_list_scene_layer.cc
+++ b/chrome/browser/android/compositor/scene_layer/tab_list_scene_layer.cc
@@ -98,6 +98,7 @@
     jint border_resource_id,
     jint border_inner_shadow_resource_id,
     jboolean can_use_live_layer,
+    jboolean browser_controls_at_bottom,
     jint tab_background_color,
     jint back_logo_color,
     jboolean incognito,
@@ -139,7 +140,6 @@
     jfloat toolbar_alpha,
     jfloat toolbar_y_offset,
     jfloat side_border_scale,
-    jboolean attach_content,
     jboolean inset_border) {
   scoped_refptr<TabLayer> layer;
   auto iter = tab_map_.find(id);
@@ -161,7 +161,8 @@
   DCHECK(layer);
   if (layer) {
     layer->SetProperties(
-        id, can_use_live_layer, toolbar_resource_id, close_button_resource_id,
+        id, can_use_live_layer, browser_controls_at_bottom,
+        toolbar_resource_id, close_button_resource_id,
         shadow_resource_id, contour_resource_id, back_logo_resource_id,
         border_resource_id, border_inner_shadow_resource_id,
         tab_background_color, back_logo_color, is_portrait, x, y, width, height,
@@ -173,15 +174,13 @@
         default_theme_color, toolbar_background_color,
         close_button_color, anonymize_toolbar, toolbar_textbox_resource_id,
         toolbar_textbox_background_color, toolbar_textbox_alpha, toolbar_alpha,
-        toolbar_y_offset, side_border_scale, attach_content, inset_border);
+        toolbar_y_offset, side_border_scale, inset_border);
   }
 
-  if (attach_content) {
-    gfx::RectF self(own_tree_->position(), gfx::SizeF(own_tree_->bounds()));
-    gfx::RectF content(x, y, width, height);
+  gfx::RectF self(own_tree_->position(), gfx::SizeF(own_tree_->bounds()));
+  gfx::RectF content(x, y, width, height);
 
-    content_obscures_self_ |= content.Contains(self);
-  }
+  content_obscures_self_ |= content.Contains(self);
 }
 
 base::android::ScopedJavaLocalRef<jobject> TabListSceneLayer::GetJavaObject(
diff --git a/chrome/browser/android/compositor/scene_layer/tab_list_scene_layer.h b/chrome/browser/android/compositor/scene_layer/tab_list_scene_layer.h
index 3be252de..028bd56 100644
--- a/chrome/browser/android/compositor/scene_layer/tab_list_scene_layer.h
+++ b/chrome/browser/android/compositor/scene_layer/tab_list_scene_layer.h
@@ -62,6 +62,7 @@
       jint border_resource_id,
       jint border_inner_shadow_resource_id,
       jboolean can_use_live_layer,
+      jboolean browser_controls_at_bottom,
       jint tab_background_color,
       jint back_logo_color,
       jboolean incognito,
@@ -103,7 +104,6 @@
       jfloat toolbar_alpha,
       jfloat toolbar_y_offset,
       jfloat side_border_scale,
-      jboolean attach_content,
       jboolean inset_border);
 
   base::android::ScopedJavaLocalRef<jobject> GetJavaObject(JNIEnv* env);
diff --git a/chrome/browser/android/compositor/scene_layer/toolbar_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/toolbar_scene_layer.cc
index efd98a0..1f0c806 100644
--- a/chrome/browser/android/compositor/scene_layer/toolbar_scene_layer.cc
+++ b/chrome/browser/android/compositor/scene_layer/toolbar_scene_layer.cc
@@ -41,7 +41,8 @@
     jfloat top_offset,
     jfloat view_height,
     bool visible,
-    bool show_shadow) {
+    bool show_shadow,
+    bool browser_controls_at_bottom) {
   // If the toolbar layer has not been created yet, create it.
   if (!toolbar_layer_) {
     ui::ResourceManager* resource_manager =
@@ -54,12 +55,10 @@
   toolbar_layer_->layer()->SetHideLayerAndSubtree(!visible);
   if (visible) {
     toolbar_layer_->layer()->SetPosition(gfx::PointF(0, top_offset));
-    // If we're at rest, hide the shadow.  The Android view should be drawing.
-    bool clip_shadow = top_offset >= 0.f && !show_shadow;
     toolbar_layer_->PushResource(toolbar_resource_id, toolbar_background_color,
                                  false, SK_ColorWHITE, url_bar_resource_id,
                                  url_bar_alpha, view_height, false,
-                                 clip_shadow);
+                                 !show_shadow, browser_controls_at_bottom);
   }
 }
 
diff --git a/chrome/browser/android/compositor/scene_layer/toolbar_scene_layer.h b/chrome/browser/android/compositor/scene_layer/toolbar_scene_layer.h
index d021b68..99c69b2 100644
--- a/chrome/browser/android/compositor/scene_layer/toolbar_scene_layer.h
+++ b/chrome/browser/android/compositor/scene_layer/toolbar_scene_layer.h
@@ -41,7 +41,8 @@
       jfloat top_offset,
       jfloat view_height,
       bool visible,
-      bool show_shadow);
+      bool show_shadow,
+      bool browser_controls_at_bottom);
 
   // Update the progress bar.
   void UpdateProgressBar(
diff --git a/chrome/browser/android/offline_pages/evaluation/run_offline_page_evaluation_test.py b/chrome/browser/android/offline_pages/evaluation/run_offline_page_evaluation_test.py
index 340ecf90..8ecd3306 100755
--- a/chrome/browser/android/offline_pages/evaluation/run_offline_page_evaluation_test.py
+++ b/chrome/browser/android/offline_pages/evaluation/run_offline_page_evaluation_test.py
@@ -13,7 +13,7 @@
 # Example Steps:
 # 1. Build chrome_public_test_apk
 # 2. Prepare a list of urls.
-# 3. Run the script
+# 3. Run the script (use -d when you have more than one device connected.)
 #   run_offline_page_evaluation_test.py --output-directory
 #   ~/offline_eval_short_output/ --url-timeout 150 --user-requested=true
 #   --use-test-scheduler=true $CHROME_SRC/out/Default ~/offline_eval_urls.txt
@@ -75,6 +75,12 @@
       dest='verbose',
       action='store_true',
       help='Make test runner verbose.')
+  parser.add_argument(
+      '-d',
+      '--device',
+      type=str,
+      dest='device_id',
+      help='Specify which device to be used. See \'adb devices\'.')
   parser.add_argument('build_output_dir', help='Path to build directory.')
   parser.add_argument(
       'test_urls_file', help='Path to input file with urls to be tested.')
@@ -85,6 +91,11 @@
       user_test_scheduler=DEFAULT_USE_TEST_SCHEDULER,
       verbose=DEFAULT_VERBOSE)
 
+  def get_adb_command(args):
+    if options.device_id != None:
+      return ['adb', '-s', options.device_id] + args
+    return ['adb'] + args
+
   # Get the arguments and several paths.
   options, extra_args = parser.parse_known_args(args)
 
@@ -99,7 +110,7 @@
                                   'bin/run_chrome_public_test_apk')
   config_output_path = os.path.join(options.output_dir, CONFIG_FILENAME)
   external_dir = subprocess.check_output(
-      ['adb', 'shell', 'echo', '$EXTERNAL_STORAGE']).strip()
+      get_adb_command(['shell', 'echo', '$EXTERNAL_STORAGE'])).strip()
 
   # Create the output directory for results, and have a copy of test config
   # there.
@@ -114,13 +125,14 @@
             use_test_scheduler=options.use_test_scheduler))
 
   print 'Uploading config file and input file onto the device.'
-  subprocess.call([
-      'adb', 'push', config_output_path, external_dir + '/paquete/test_config'
-  ])
-  subprocess.call([
-      'adb', 'push', options.test_urls_file,
-      '/sdcard/paquete/offline_eval_urls.txt'
-  ])
+  subprocess.call(
+      get_adb_command(
+          ['push', config_output_path, external_dir + '/paquete/test_config']))
+  subprocess.call(
+      get_adb_command([
+          'push', options.test_urls_file,
+          '/sdcard/paquete/offline_eval_urls.txt'
+      ]))
   print 'Start running test...'
 
   # Run test
@@ -136,19 +148,19 @@
   archive_dir = os.path.join(options.output_dir, 'archives/')
   if os.path.exists(archive_dir):
     shutil.rmtree(archive_dir)
-  subprocess.call(['adb', 'root'])
-  subprocess.call([
-      'adb', 'pull', '/data/data/org.chromium.chrome/app_chrome/'
-      'Default/Offline Pages/archives', archive_dir
-  ])
-  subprocess.call([
-      'adb', 'pull', external_dir + '/paquete/offline_eval_results.txt',
-      options.output_dir
-  ])
-  subprocess.call([
-      'adb', 'pull', external_dir + '/paquete/offline_eval_logs.txt',
-      options.output_dir
-  ])
+  subprocess.call(
+      get_adb_command(
+          ['pull', external_dir + '/paquete/archives', archive_dir]))
+  subprocess.call(
+      get_adb_command([
+          'pull', external_dir + '/paquete/offline_eval_results.txt',
+          options.output_dir
+      ]))
+  subprocess.call(
+      get_adb_command([
+          'pull', external_dir + '/paquete/offline_eval_logs.txt',
+          options.output_dir
+      ]))
   print 'Test finished!'
 
 
diff --git a/chrome/browser/android/offline_pages/offline_page_bridge.cc b/chrome/browser/android/offline_pages/offline_page_bridge.cc
index f585cf6..ed2b0d0 100644
--- a/chrome/browser/android/offline_pages/offline_page_bridge.cc
+++ b/chrome/browser/android/offline_pages/offline_page_bridge.cc
@@ -23,7 +23,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
 #include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/background/request_queue.h"
+#include "components/offline_pages/background/request_queue_results.h"
 #include "components/offline_pages/background/save_page_request.h"
 #include "components/offline_pages/offline_page_feature.h"
 #include "components/offline_pages/offline_page_item.h"
@@ -167,20 +167,19 @@
   base::android::RunCallbackAndroid(j_callback_obj, j_result_obj);
 }
 
-RequestQueue::UpdateRequestResult ToUpdateRequestResult(
-    ItemActionStatus status) {
+UpdateRequestResult ToUpdateRequestResult(ItemActionStatus status) {
   switch (status) {
     case ItemActionStatus::SUCCESS:
-      return RequestQueue::UpdateRequestResult::SUCCESS;
+      return UpdateRequestResult::SUCCESS;
     case ItemActionStatus::NOT_FOUND:
-      return RequestQueue::UpdateRequestResult::REQUEST_DOES_NOT_EXIST;
+      return UpdateRequestResult::REQUEST_DOES_NOT_EXIST;
     case ItemActionStatus::STORE_ERROR:
-      return RequestQueue::UpdateRequestResult::STORE_FAILURE;
+      return UpdateRequestResult::STORE_FAILURE;
     case ItemActionStatus::ALREADY_EXISTS:
     default:
       NOTREACHED();
   }
-  return RequestQueue::UpdateRequestResult::STORE_FAILURE;
+  return UpdateRequestResult::STORE_FAILURE;
 }
 
 void OnRemoveRequestsDone(const ScopedJavaGlobalRef<jobject>& j_callback_obj,
diff --git a/chrome/browser/android/url_utilities.cc b/chrome/browser/android/url_utilities.cc
index cc7d3d1..584759a 100644
--- a/chrome/browser/android/url_utilities.cc
+++ b/chrome/browser/android/url_utilities.cc
@@ -26,6 +26,44 @@
 static const char* const g_fallback_valid_schemes[] = {
     "http", "https", nullptr };
 
+// TODO(mariakhomenko): figure out how to keep this list updated.
+static const char* const g_google_tld_list[] = {"ac", "ad", "ae", "af", "ag",
+    "al", "am", "as", "at", "aw", "az", "ba", "be", "bf", "bg", "bi", "biz",
+    "bj", "bm", "bn", "bo", "bs", "bt", "by", "bz", "ca", "cat", "cc", "cd",
+    "cf", "cg", "ch", "ci", "cl", "cm", "cn", "co", "co.ao", "co.at", "co.ba",
+    "co.bi", "co.bw", "co.ci", "co.ck", "co.cr", "co.gg", "co.gl", "co.gy",
+    "co.hu", "co.id", "co.il", "co.im", "co.in", "co.it", "co.je", "co.jp",
+    "co.ke", "co.kr", "co.ls", "co.ma", "co.mu", "co.mw", "co.mz", "co.nz",
+    "co.pn", "co.rs",  "co.th", "co.tt", "co.tz", "co.ua", "co.ug", "co.uk",
+    "co.uz", "co.ve", "co.vi", "co.za", "co.zm", "co.zw", "com", "com.af",
+    "com.ag", "com.ai", "com.ar", "com.au", "com.az", "com.bd", "com.bh",
+    "com.bi", "com.bn", "com.bo", "com.br", "com.bs", "com.by", "com.bz",
+    "com.cn", "com.co", "com.cu", "com.cy", "com.do", "com.dz", "com.ec",
+    "com.eg", "com.er", "com.et", "com.fj", "com.ge", "com.gh", "com.gi",
+    "com.gl", "com.gp", "com.gr", "com.gt", "com.gy", "com.hk", "com.hn",
+    "com.hr", "com.ht", "com.iq", "com.jm", "com.jo", "com.kg", "com.kh",
+    "com.ki", "com.kw", "com.kz", "com.lb", "com.lc", "com.lk", "com.lv",
+    "com.ly", "com.mk", "com.mm", "com.mt", "com.mu", "com.mw", "com.mx",
+    "com.my", "com.na", "com.nc", "com.nf", "com.ng", "com.ni", "com.np",
+    "com.nr", "com.om", "com.pa", "com.pe", "com.pg", "com.ph", "com.pk",
+    "com.pl", "com.pr", "com.ps", "com.pt", "com.py", "com.qa", "com.ru",
+    "com.sa", "com.sb", "com.sc", "com.sg", "com.sl", "com.sv", "com.tj",
+    "com.tm", "com.tn", "com.tr", "com.tt", "com.tw", "com.ua", "com.uy",
+    "com.uz", "com.vc", "com.ve", "com.vi", "com.vn", "com.ws", "cv", "cx",
+    "cz", "de", "dj", "dk", "dm", "do", "dz", "ec", "ee", "es", "eu", "fi",
+    "fm", "fr", "ga", "gd", "ge", "gf", "gg", "gl", "gm", "gp", "gr", "gw",
+    "gy", "hk", "hn", "hr", "ht", "hu", "ie", "im", "in", "info", "in.rs", "io",
+    "iq", "is", "it", "it.ao", "je", "jo", "jobs", "jp", "kg", "ki", "kids.us",
+    "km", "kn", "kr", "kz", "la", "li", "lk", "lt", "lu", "lv", "ma", "md",
+    "me", "mg", "mh", "mk", "ml", "mn", "mobi", "mr", "ms", "mu", "mv", "mw",
+    "mx", "name", "ne", "ne.jp", "net", "net.in", "net.nz", "nf", "ng", "nl",
+    "no", "nom.es", "nr", "nu", "off.ai", "org", "org.af", "org.es", "org.in",
+    "org.nz", "org.uk", "pf", "ph", "pk", "pl", "pn", "pr", "pro", "ps", "pt",
+    "qa", "re", "ro", "rs", "ru", "rw", "sc", "se", "sg", "sh", "si", "sk",
+    "sl", "sm", "sn", "so", "sr", "st", "sz", "td", "tel", "tg", "tk", "tl",
+    "tm", "tn", "to", "tt", "tv", "tw", "ua", "ug", "us", "uz", "vc", "vg",
+    "vn", "vu", "ws", "yt"};
+
 GURL ConvertJavaStringToGURL(JNIEnv* env, jstring url) {
   return url ? GURL(ConvertJavaStringToUTF8(env, url)) : GURL();
 }
@@ -103,7 +141,26 @@
   GURL gurl = ConvertJavaStringToGURL(env, url);
   if (gurl.is_empty())
     return false;
-  return google_util::IsGoogleSearchUrl(gurl);
+  bool is_search = google_util::IsGoogleSearchUrl(gurl);
+  if (!is_search)
+    return is_search;
+
+  size_t length = net::registry_controlled_domains::GetRegistryLength(
+      gurl,
+      net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES,
+      net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
+  if ((length == 0) || (length == std::string::npos))
+    return false;
+
+  std::string tld(gurl.host().substr(gurl.host().length() - length,
+                                     std::string::npos));
+  // TODO(mariakhomenko): binary search instead?
+  for (size_t i = 0; g_google_tld_list[i]; i++) {
+    if (g_google_tld_list[i] == tld) {
+      return true;
+    }
+  }
+  return false;
 }
 
 static jboolean IsGoogleHomePageUrl(JNIEnv* env,
diff --git a/chrome/browser/android/vr_shell/BUILD.gn b/chrome/browser/android/vr_shell/BUILD.gn
index c9e16628..0b10738 100644
--- a/chrome/browser/android/vr_shell/BUILD.gn
+++ b/chrome/browser/android/vr_shell/BUILD.gn
@@ -8,70 +8,73 @@
 
 assert(enable_vr_shell || enable_webvr)
 
-static_library("vr_common") {
-  defines = []
+if (current_cpu == "arm" || current_cpu == "arm64") {
+  static_library("vr_common") {
+    defines = []
 
-  sources = [
-    "animation.cc",
-    "animation.h",
-    "easing.cc",
-    "easing.h",
-    "ui_elements.cc",
-    "ui_elements.h",
-    "ui_interface.cc",
-    "ui_interface.h",
-    "ui_scene.cc",
-    "ui_scene.h",
-    "vr_compositor.cc",
-    "vr_compositor.h",
-    "vr_controller.cc",
-    "vr_controller.h",
-    "vr_gesture.h",
-    "vr_gl_util.cc",
-    "vr_gl_util.h",
-    "vr_input_manager.cc",
-    "vr_input_manager.h",
-    "vr_math.cc",
-    "vr_math.h",
-    "vr_shell.cc",
-    "vr_shell.h",
-    "vr_shell_delegate.cc",
-    "vr_shell_delegate.h",
-    "vr_shell_renderer.cc",
-    "vr_shell_renderer.h",
-    "vr_web_contents_observer.cc",
-    "vr_web_contents_observer.h",
-  ]
+    sources = [
+      "animation.cc",
+      "animation.h",
+      "easing.cc",
+      "easing.h",
+      "ui_elements.cc",
+      "ui_elements.h",
+      "ui_interface.cc",
+      "ui_interface.h",
+      "ui_scene.cc",
+      "ui_scene.h",
+      "vr_compositor.cc",
+      "vr_compositor.h",
+      "vr_controller.cc",
+      "vr_controller.h",
+      "vr_gesture.h",
+      "vr_gl_util.cc",
+      "vr_gl_util.h",
+      "vr_input_manager.cc",
+      "vr_input_manager.h",
+      "vr_math.cc",
+      "vr_math.h",
+      "vr_shell.cc",
+      "vr_shell.h",
+      "vr_shell_delegate.cc",
+      "vr_shell_delegate.h",
+      "vr_shell_renderer.cc",
+      "vr_shell_renderer.h",
+      "vr_web_contents_observer.cc",
+      "vr_web_contents_observer.h",
+    ]
 
-  if (enable_vr_shell_ui_dev) {
-    assert(enable_vr_shell)
-    defines += [ "ENABLE_VR_SHELL_UI_DEV" ]
+    if (enable_vr_shell_ui_dev) {
+      assert(enable_vr_shell)
+      defines += [ "ENABLE_VR_SHELL_UI_DEV" ]
+    }
+
+    deps = [
+      ":vr_shell_jni_headers",
+      "//base",
+      "//cc",
+      "//content/public/browser",
+      "//content/public/common",
+      "//device/vr",
+      "//ui/android",
+      "//ui/base",
+      "//ui/display",
+      "//ui/gl",
+      "//ui/gl/init",
+    ]
+
+    libs =
+        [ "//third_party/gvr-android-sdk/libgvr_shim_static_${current_cpu}.a" ]
+    configs += [ "//third_party/gvr-android-sdk:libgvr_config" ]
   }
 
-  deps = [
-    ":vr_shell_jni_headers",
-    "//base",
-    "//cc",
-    "//content/public/browser",
-    "//content/public/common",
-    "//device/vr",
-    "//third_party/gvr-android-sdk:libgvr",
-    "//ui/android",
-    "//ui/base",
-    "//ui/display",
-    "//ui/gl",
-    "//ui/gl/init",
-  ]
-
-  configs += [ "//third_party/gvr-android-sdk:libgvr_config" ]
-}
-
-generate_jni("vr_shell_jni_headers") {
-  sources = [
-    "//chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java",
-    "//chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java",
-  ]
-  jni_package = "vr_shell"
+  generate_jni("vr_shell_jni_headers") {
+    sources = [
+      "//chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java",
+      "//chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java",
+    ]
+    jni_package = "vr_shell"
+  }
 }
 
 if (enable_vr_shell) {
diff --git a/chrome/browser/autocomplete/autocomplete_classifier_factory.cc b/chrome/browser/autocomplete/autocomplete_classifier_factory.cc
index 0112537e..cf53be8a 100644
--- a/chrome/browser/autocomplete/autocomplete_classifier_factory.cc
+++ b/chrome/browser/autocomplete/autocomplete_classifier_factory.cc
@@ -15,8 +15,9 @@
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/omnibox/browser/autocomplete_classifier.h"
 #include "components/omnibox/browser/autocomplete_controller.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/extension_system_provider.h"
 #include "extensions/browser/extensions_browser_client.h"
 #endif
@@ -49,7 +50,7 @@
     : BrowserContextKeyedServiceFactory(
         "AutocompleteClassifier",
         BrowserContextDependencyManager::GetInstance()) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   DependsOn(
       extensions::ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
 #endif
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
index b997043..af917eb 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
@@ -32,8 +32,9 @@
 #include "components/prefs/pref_service.h"
 #include "components/sync/driver/sync_service_utils.h"
 #include "content/public/browser/notification_service.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/autocomplete/keyword_extensions_delegate_impl.h"
 #endif
 
@@ -142,7 +143,7 @@
 std::unique_ptr<KeywordExtensionsDelegate>
 ChromeAutocompleteProviderClient::GetKeywordExtensionsDelegate(
     KeywordProvider* keyword_provider) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return base::MakeUnique<KeywordExtensionsDelegateImpl>(profile_,
                                                          keyword_provider);
 #else
diff --git a/chrome/browser/autocomplete/keyword_extensions_delegate_impl.h b/chrome/browser/autocomplete/keyword_extensions_delegate_impl.h
index 405cb05e..0b4c03c 100644
--- a/chrome/browser/autocomplete/keyword_extensions_delegate_impl.h
+++ b/chrome/browser/autocomplete/keyword_extensions_delegate_impl.h
@@ -19,8 +19,9 @@
 #include "components/omnibox/browser/keyword_provider.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
+#include "extensions/features/features.h"
 
-#if !defined(ENABLE_EXTENSIONS)
+#if !BUILDFLAG(ENABLE_EXTENSIONS)
 #error "Should not be included when extensions are disabled"
 #endif
 
diff --git a/chrome/browser/autocomplete/shortcuts_backend_factory.cc b/chrome/browser/autocomplete/shortcuts_backend_factory.cc
index 6f3e42d..2fb5a4b 100644
--- a/chrome/browser/autocomplete/shortcuts_backend_factory.cc
+++ b/chrome/browser/autocomplete/shortcuts_backend_factory.cc
@@ -17,9 +17,10 @@
 #include "components/omnibox/browser/shortcuts_constants.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_thread.h"
+#include "extensions/features/features.h"
 
 namespace {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 const char kShortcutsExtensionsManagerKey[] = "ShortcutsExtensionsManager";
 #endif
 }
@@ -79,7 +80,7 @@
 
 void ShortcutsBackendFactory::BrowserContextShutdown(
     content::BrowserContext* context) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   context->RemoveUserData(kShortcutsExtensionsManagerKey);
 #endif
 
@@ -98,7 +99,7 @@
       content::BrowserThread::GetTaskRunnerForThread(
           content::BrowserThread::DB),
       profile->GetPath().Append(kShortcutsDatabaseName), suppress_db));
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   ShortcutsExtensionsManager* extensions_manager =
       new ShortcutsExtensionsManager(profile);
   profile->SetUserData(kShortcutsExtensionsManagerKey, extensions_manager);
diff --git a/chrome/browser/autocomplete/shortcuts_extensions_manager.cc b/chrome/browser/autocomplete/shortcuts_extensions_manager.cc
index aebc8d77..a623a1f 100644
--- a/chrome/browser/autocomplete/shortcuts_extensions_manager.cc
+++ b/chrome/browser/autocomplete/shortcuts_extensions_manager.cc
@@ -10,8 +10,9 @@
 #include "components/omnibox/browser/shortcuts_backend.h"
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_source.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/notification_types.h"
 #include "extensions/common/extension.h"
 #endif
@@ -19,7 +20,7 @@
 ShortcutsExtensionsManager::ShortcutsExtensionsManager(Profile* profile)
     : profile_(profile) {
   DCHECK(profile_);
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   notification_registrar_.Add(
       this, extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
       content::Source<Profile>(profile_));
@@ -32,7 +33,7 @@
     int type,
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   DCHECK_EQ(extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED, type);
   scoped_refptr<ShortcutsBackend> shortcuts_backend =
       ShortcutsBackendFactory::GetForProfileIfExists(profile_);
diff --git a/chrome/browser/autocomplete/shortcuts_provider_extension_unittest.cc b/chrome/browser/autocomplete/shortcuts_provider_extension_unittest.cc
index efd0c33..a66214ee 100644
--- a/chrome/browser/autocomplete/shortcuts_provider_extension_unittest.cc
+++ b/chrome/browser/autocomplete/shortcuts_provider_extension_unittest.cc
@@ -25,9 +25,10 @@
 #include "components/omnibox/browser/shortcuts_provider_test_util.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/test/test_browser_thread.h"
+#include "extensions/features/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/notification_types.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_builder.h"
@@ -92,7 +93,7 @@
 
 // Actual tests ---------------------------------------------------------------
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 TEST_F(ShortcutsProviderExtensionTest, Extension) {
   // Try an input string that matches an extension URL.
   base::string16 text(base::ASCIIToUTF16("echo"));
diff --git a/chrome/browser/browser_about_handler.cc b/chrome/browser/browser_about_handler.cc
index 30899a53a..93e0d6a1 100644
--- a/chrome/browser/browser_about_handler.cc
+++ b/chrome/browser/browser_about_handler.cc
@@ -20,6 +20,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/url_constants.h"
 #include "components/url_formatter/url_fixer.h"
+#include "extensions/features/features.h"
 
 #if !defined(OS_ANDROID)
 #include "chrome/browser/ui/webui/md_history_ui.h"
@@ -61,7 +62,7 @@
   } else if (host == chrome::kChromeUISyncHost) {
     host = chrome::kChromeUISyncInternalsHost;
   // Redirect chrome://extensions.
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   } else if (host == chrome::kChromeUIExtensionsHost) {
     // If the material design extensions page is enabled, it gets its own host.
     // Otherwise, it's handled by the uber settings page.
@@ -77,7 +78,7 @@
       url->path() == std::string("/") + chrome::kExtensionsSubPage) {
     host = chrome::kChromeUIUberHost;
     path = chrome::kChromeUIExtensionsHost;
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
   // Redirect chrome://history.
   } else if (host == chrome::kChromeUIHistoryHost) {
 #if defined(OS_ANDROID)
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index 673377e..e455394 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -117,6 +117,7 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/content_switches.h"
 #include "extensions/common/constants.h"
+#include "extensions/features/features.h"
 #include "net/socket/client_socket_pool_manager.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "printing/features/features.h"
@@ -143,7 +144,7 @@
 #include "chrome/browser/background/background_mode_manager.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/chrome_extensions_browser_client.h"
 #include "chrome/browser/extensions/event_router_forwarder.h"
 #include "chrome/browser/media_galleries/media_file_system_registry.h"
@@ -236,7 +237,7 @@
 
   device_client_.reset(new ChromeDeviceClient);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Athena sets its own instance during Athena's init process.
   extensions::AppWindowClient::Set(ChromeAppWindowClient::GetInstance());
 
@@ -261,7 +262,7 @@
 }
 
 BrowserProcessImpl::~BrowserProcessImpl() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::ExtensionsBrowserClient::Set(nullptr);
 #endif
 
@@ -324,7 +325,7 @@
 
   child_process_watcher_.reset();
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   media_file_system_registry_.reset();
   // Remove the global instance of the Storage Monitor now. Otherwise the
   // FILE thread would be gone when we try to release it in the dtor and
@@ -572,7 +573,7 @@
 
 extensions::EventRouterForwarder*
 BrowserProcessImpl::extension_event_router_forwarder() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return extension_event_router_forwarder_.get();
 #else
   return NULL;
@@ -717,7 +718,7 @@
 
 void BrowserProcessImpl::SetApplicationLocale(const std::string& locale) {
   locale_ = locale;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extension_l10n_util::SetProcessLocale(locale);
 #endif
   ChromeContentBrowserClient::SetApplicationLocale(locale);
@@ -730,7 +731,7 @@
 }
 
 MediaFileSystemRegistry* BrowserProcessImpl::media_file_system_registry() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (!media_file_system_registry_)
     media_file_system_registry_.reset(new MediaFileSystemRegistry());
   return media_file_system_registry_.get();
@@ -1036,7 +1037,7 @@
 }
 
 void BrowserProcessImpl::PreCreateThreads() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Register the chrome-extension scheme to reflect the extension process
   // model. Controlled by a field trial, so we can't do this earlier.
   base::FieldTrialList::FindFullName("SiteIsolationExtensions");
diff --git a/chrome/browser/browser_process_impl.h b/chrome/browser/browser_process_impl.h
index c5134c1..eb2a9cae 100644
--- a/chrome/browser/browser_process_impl.h
+++ b/chrome/browser/browser_process_impl.h
@@ -25,6 +25,7 @@
 #include "chrome/browser/lifetime/keep_alive_state_observer.h"
 #include "chrome/common/features.h"
 #include "components/prefs/pref_change_registrar.h"
+#include "extensions/features/features.h"
 #include "printing/features/features.h"
 
 class ChromeChildProcessWatcher;
@@ -218,7 +219,7 @@
 
   std::unique_ptr<GpuModeManager> gpu_mode_manager_;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   std::unique_ptr<extensions::ExtensionsBrowserClient>
       extensions_browser_client_;
 
diff --git a/chrome/browser/browsing_data/browsing_data_counter_factory.cc b/chrome/browser/browsing_data/browsing_data_counter_factory.cc
index a9952c58..e9d0be0a 100644
--- a/chrome/browser/browsing_data/browsing_data_counter_factory.cc
+++ b/chrome/browser/browsing_data/browsing_data_counter_factory.cc
@@ -25,8 +25,9 @@
 #include "components/browsing_data/core/pref_names.h"
 #include "components/history/core/browser/web_history_service.h"
 #include "components/password_manager/core/browser/password_store.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/browsing_data/hosted_apps_counter.h"
 #endif
 
@@ -75,7 +76,7 @@
   if (pref_name == browsing_data::prefs::kDeleteMediaLicenses)
     return base::MakeUnique<MediaLicensesCounter>(profile);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (pref_name == browsing_data::prefs::kDeleteHostedAppsData)
     return base::MakeUnique<HostedAppsCounter>(profile);
 #endif
diff --git a/chrome/browser/browsing_data/browsing_data_counter_utils.cc b/chrome/browser/browsing_data/browsing_data_counter_utils.cc
index eb7d126..6aa25a91 100644
--- a/chrome/browser/browsing_data/browsing_data_counter_utils.cc
+++ b/chrome/browser/browsing_data/browsing_data_counter_utils.cc
@@ -15,10 +15,11 @@
 #include "components/browsing_data/core/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/strings/grit/components_strings.h"
+#include "extensions/features/features.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/text/bytes_formatting.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_util.h"
 #include "chrome/browser/browsing_data/hosted_apps_counter.h"
@@ -97,7 +98,7 @@
         IDS_DEL_MEDIA_LICENSES_COUNTER_GENERAL_COMMENT);
   }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (pref_name == browsing_data::prefs::kDeleteHostedAppsData) {
     // Hosted apps counter.
     const HostedAppsCounter::HostedAppsResult* hosted_apps_result =
diff --git a/chrome/browser/browsing_data/browsing_data_counter_utils_unittest.cc b/chrome/browser/browsing_data/browsing_data_counter_utils_unittest.cc
index c465720..dc5e7d1 100644
--- a/chrome/browser/browsing_data/browsing_data_counter_utils_unittest.cc
+++ b/chrome/browser/browsing_data/browsing_data_counter_utils_unittest.cc
@@ -13,9 +13,10 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "extensions/features/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "base/strings/string_split.h"
 #include "chrome/browser/browsing_data/hosted_apps_counter.h"
 #endif
@@ -32,7 +33,7 @@
   TestingProfile profile_;
 };
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 // Tests the complex output of the hosted apps counter.
 TEST_F(BrowsingDataCounterUtilsTest, HostedAppsCounterResult) {
   HostedAppsCounter counter(GetProfile());
diff --git a/chrome/browser/browsing_data/browsing_data_file_system_helper.cc b/chrome/browser/browsing_data/browsing_data_file_system_helper.cc
index 85487b6..fc9e245 100644
--- a/chrome/browser/browsing_data/browsing_data_file_system_helper.cc
+++ b/chrome/browser/browsing_data/browsing_data_file_system_helper.cc
@@ -13,6 +13,7 @@
 #include "base/sequenced_task_runner.h"
 #include "chrome/browser/browsing_data/browsing_data_helper.h"
 #include "content/public/browser/browser_thread.h"
+#include "extensions/features/features.h"
 #include "storage/browser/fileapi/file_system_context.h"
 #include "storage/browser/fileapi/file_system_quota_util.h"
 #include "storage/common/fileapi/file_system_types.h"
@@ -99,7 +100,7 @@
   const storage::FileSystemType types[] = {
     storage::kFileSystemTypeTemporary,
     storage::kFileSystemTypePersistent,
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     storage::kFileSystemTypeSyncable,
 #endif
   };
diff --git a/chrome/browser/browsing_data/browsing_data_helper_unittest.cc b/chrome/browser/browsing_data/browsing_data_helper_unittest.cc
index 2d4eb08..77ee686f 100644
--- a/chrome/browser/browsing_data/browsing_data_helper_unittest.cc
+++ b/chrome/browser/browsing_data/browsing_data_helper_unittest.cc
@@ -8,11 +8,12 @@
 #include "base/strings/stringprintf.h"
 #include "chrome/common/url_constants.h"
 #include "extensions/common/constants.h"
+#include "extensions/features/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 #include "url/url_constants.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/mock_extension_special_storage_policy.h"
 #endif
 
@@ -104,7 +105,7 @@
   EXPECT_FALSE(IsExtensionScheme(content::kViewSourceScheme));
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 TEST_F(BrowsingDataHelperTest, TestMatches) {
   scoped_refptr<MockExtensionSpecialStoragePolicy> mock_policy =
       new MockExtensionSpecialStoragePolicy;
diff --git a/chrome/browser/browsing_data/browsing_data_remover.cc b/chrome/browser/browsing_data/browsing_data_remover.cc
index 8249f32..8fe00d5 100644
--- a/chrome/browser/browsing_data/browsing_data_remover.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover.cc
@@ -79,6 +79,7 @@
 #include "content/public/browser/ssl_host_state_delegate.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/user_metrics.h"
+#include "extensions/features/features.h"
 #include "net/base/net_errors.h"
 #include "net/cookies/cookie_store.h"
 #include "net/http/http_network_session.h"
@@ -100,7 +101,7 @@
 #include "components/precache/content/precache_manager.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/activity_log/activity_log.h"
 #include "extensions/browser/extension_prefs.h"
 #endif
@@ -534,7 +535,7 @@
                                                 filter);
     }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     // The extension activity log contains details of which websites extensions
     // were active on. It therefore indirectly stores details of websites a
     // user has visited so best clean from here as well.
diff --git a/chrome/browser/browsing_data/browsing_data_remover_factory.cc b/chrome/browser/browsing_data/browsing_data_remover_factory.cc
index e7f13b1..ccf45fc2 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_factory.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_factory.cc
@@ -21,13 +21,14 @@
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/power/origin_power_map_factory.h"
 #include "content/public/browser/browser_context.h"
+#include "extensions/features/features.h"
 
 #if BUILDFLAG(ANDROID_JAVA_UI)
 #include "chrome/browser/android/offline_pages/offline_page_model_factory.h"
 #include "chrome/browser/precache/precache_manager_factory.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/activity_log/activity_log.h"
 #include "extensions/browser/extension_prefs_factory.h"
 #endif
@@ -70,7 +71,7 @@
   DependsOn(precache::PrecacheManagerFactory::GetInstance());
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   DependsOn(extensions::ActivityLog::GetFactoryInstance());
   DependsOn(extensions::ExtensionPrefsFactory::GetInstance());
 #endif
diff --git a/chrome/browser/browsing_data/browsing_data_remover_unittest.cc b/chrome/browser/browsing_data/browsing_data_remover_unittest.cc
index 787b46f..74655b36 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_unittest.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_unittest.cc
@@ -80,6 +80,7 @@
 #include "content/public/test/test_browser_thread.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_utils.h"
+#include "extensions/features/features.h"
 #include "net/cookies/cookie_store.h"
 #include "net/http/http_network_session.h"
 #include "net/http/http_transaction_factory.h"
@@ -108,7 +109,7 @@
 #include "components/signin/core/account_id/account_id.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/mock_extension_special_storage_policy.h"
 #endif
 
@@ -1132,7 +1133,7 @@
   ~BrowsingDataRemoverTest() override {}
 
   void TearDown() override {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     mock_policy_ = nullptr;
 #endif
 
@@ -1213,7 +1214,7 @@
   }
 
   MockExtensionSpecialStoragePolicy* CreateMockPolicy() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     mock_policy_ = new MockExtensionSpecialStoragePolicy;
     return mock_policy_.get();
 #else
@@ -1223,7 +1224,7 @@
   }
 
   storage::SpecialStoragePolicy* mock_policy() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     return mock_policy_.get();
 #else
     return nullptr;
@@ -1233,7 +1234,7 @@
   // If |kOrigin1| is protected when extensions are enabled, the expected
   // result for tests where the OriginMatcherFunction result is variable.
   bool ShouldRemoveForProtectedOriginOne() const {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     return false;
 #else
     return true;
@@ -1253,7 +1254,7 @@
 
   StoragePartitionRemovalData storage_partition_removal_data_;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   scoped_refptr<MockExtensionSpecialStoragePolicy> mock_policy_;
 #endif
 
@@ -1447,7 +1448,7 @@
 }
 
 TEST_F(BrowsingDataRemoverTest, RemoveUnprotectedLocalStorageForever) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   MockExtensionSpecialStoragePolicy* policy = CreateMockPolicy();
   // Protect kOrigin1.
   policy->AddProtected(kOrigin1.GetOrigin());
@@ -1477,7 +1478,7 @@
 }
 
 TEST_F(BrowsingDataRemoverTest, RemoveProtectedLocalStorageForever) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Protect kOrigin1.
   MockExtensionSpecialStoragePolicy* policy = CreateMockPolicy();
   policy->AddProtected(kOrigin1.GetOrigin());
@@ -1508,7 +1509,7 @@
 }
 
 TEST_F(BrowsingDataRemoverTest, RemoveLocalStorageForLastWeek) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   CreateMockPolicy();
 #endif
 
@@ -1732,7 +1733,7 @@
 }
 
 TEST_F(BrowsingDataRemoverTest, RemoveQuotaManagedDataForeverOnlyTemporary) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   CreateMockPolicy();
 #endif
 
@@ -1776,7 +1777,7 @@
 }
 
 TEST_F(BrowsingDataRemoverTest, RemoveQuotaManagedDataForeverOnlyPersistent) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   CreateMockPolicy();
 #endif
 
@@ -1820,7 +1821,7 @@
 }
 
 TEST_F(BrowsingDataRemoverTest, RemoveQuotaManagedDataForeverNeither) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   CreateMockPolicy();
 #endif
 
@@ -1985,7 +1986,7 @@
 }
 
 TEST_F(BrowsingDataRemoverTest, RemoveQuotaManagedUnprotectedOrigins) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   MockExtensionSpecialStoragePolicy* policy = CreateMockPolicy();
   // Protect kOrigin1.
   policy->AddProtected(kOrigin1.GetOrigin());
@@ -2031,7 +2032,7 @@
 }
 
 TEST_F(BrowsingDataRemoverTest, RemoveQuotaManagedProtectedSpecificOrigin) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   MockExtensionSpecialStoragePolicy* policy = CreateMockPolicy();
   // Protect kOrigin1.
   policy->AddProtected(kOrigin1.GetOrigin());
@@ -2083,7 +2084,7 @@
 }
 
 TEST_F(BrowsingDataRemoverTest, RemoveQuotaManagedProtectedOrigins) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   MockExtensionSpecialStoragePolicy* policy = CreateMockPolicy();
   // Protect kOrigin1.
   policy->AddProtected(kOrigin1.GetOrigin());
@@ -2131,7 +2132,7 @@
 }
 
 TEST_F(BrowsingDataRemoverTest, RemoveQuotaManagedIgnoreExtensionsAndDevTools) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   CreateMockPolicy();
 #endif
 
diff --git a/chrome/browser/browsing_data/cookies_tree_model.cc b/chrome/browser/browsing_data/cookies_tree_model.cc
index 7eef2172..b321cde 100644
--- a/chrome/browser/browsing_data/cookies_tree_model.cc
+++ b/chrome/browser/browsing_data/cookies_tree_model.cc
@@ -23,6 +23,7 @@
 #include "chrome/grit/theme_resources.h"
 #include "components/content_settings/core/browser/cookie_settings.h"
 #include "content/public/common/url_constants.h"
+#include "extensions/features/features.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/cookies/canonical_cookie.h"
 #include "net/url_request/url_request_context.h"
@@ -33,7 +34,7 @@
 #include "url/gurl.h"
 #include "url/origin.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_special_storage_policy.h"
 #include "extensions/common/extension_set.h"
 #endif
@@ -128,7 +129,7 @@
   return retval;
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 bool TypeIsProtected(CookieTreeNode::DetailedInfo::NodeType type) {
   switch (type) {
     // Fall through each below cases to return true.
@@ -1027,7 +1028,7 @@
     ExtensionSpecialStoragePolicy* special_storage_policy)
     : ui::TreeNodeModel<CookieTreeNode>(
           base::MakeUnique<CookieTreeRootNode>(this)),
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
       special_storage_policy_(special_storage_policy),
 #endif
       data_container_(data_container) {
@@ -1136,7 +1137,7 @@
   PopulateCacheStorageUsageInfoWithFilter(data_container(), &notifier, filter);
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 const extensions::ExtensionSet* CookiesTreeModel::ExtensionsProtectingNode(
     const CookieTreeNode& cookie_node) {
   if (!special_storage_policy_.get())
diff --git a/chrome/browser/browsing_data/cookies_tree_model.h b/chrome/browser/browsing_data/cookies_tree_model.h
index 96205bb..f5e1f51 100644
--- a/chrome/browser/browsing_data/cookies_tree_model.h
+++ b/chrome/browser/browsing_data/cookies_tree_model.h
@@ -29,6 +29,7 @@
 #include "chrome/browser/browsing_data/browsing_data_service_worker_helper.h"
 #include "chrome/browser/browsing_data/local_data_container.h"
 #include "components/content_settings/core/common/content_settings.h"
+#include "extensions/features/features.h"
 #include "net/ssl/channel_id_store.h"
 #include "ui/base/models/tree_node_model.h"
 
@@ -793,7 +794,7 @@
   // Filter the origins to only display matched results.
   void UpdateSearchResults(const base::string16& filter);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Returns the set of extensions which protect the data item represented by
   // this node from deletion.
   // Returns nullptr if the node doesn't represent a protected data item or the
@@ -904,7 +905,7 @@
                                           ScopedBatchUpdateNotifier* notifier,
                                           const base::string16& filter);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // The extension special storage policy; see ExtensionsProtectingNode() above.
   scoped_refptr<ExtensionSpecialStoragePolicy> special_storage_policy_;
 #endif
diff --git a/chrome/browser/browsing_data/cookies_tree_model_unittest.cc b/chrome/browser/browsing_data/cookies_tree_model_unittest.cc
index 286862a..f8a9a29 100644
--- a/chrome/browser/browsing_data/cookies_tree_model_unittest.cc
+++ b/chrome/browser/browsing_data/cookies_tree_model_unittest.cc
@@ -32,11 +32,12 @@
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_types.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "extensions/features/features.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_special_storage_policy.h"
 #endif
 
@@ -49,7 +50,7 @@
  public:
   ~CookiesTreeModelTest() override {
     // Avoid memory leaks.
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     special_storage_policy_ = nullptr;
 #endif
     profile_.reset();
@@ -91,7 +92,7 @@
             HostContentSettingsMapFactory::GetForProfile(profile_.get()),
             profile_->GetPrefs(),
             kExtensionScheme);
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     special_storage_policy_ =
         new ExtensionSpecialStoragePolicy(cookie_settings.get());
 #endif
@@ -449,7 +450,7 @@
 
  protected:
   ExtensionSpecialStoragePolicy* special_storage_policy() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     return special_storage_policy_.get();
 #else
     return nullptr;
@@ -485,7 +486,7 @@
   scoped_refptr<MockBrowsingDataMediaLicenseHelper>
       mock_browsing_data_media_license_helper_;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   scoped_refptr<ExtensionSpecialStoragePolicy> special_storage_policy_;
 #endif
 };
diff --git a/chrome/browser/browsing_data/downloads_counter_browsertest.cc b/chrome/browser/browsing_data/downloads_counter_browsertest.cc
index 2993b46..ea865fbb 100644
--- a/chrome/browser/browsing_data/downloads_counter_browsertest.cc
+++ b/chrome/browser/browsing_data/downloads_counter_browsertest.cc
@@ -21,8 +21,9 @@
 #include "components/history/core/browser/download_row.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/download_manager.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/common/extension.h"
 #endif
 
@@ -78,7 +79,7 @@
         true);
   }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   std::string AddExtensionDownload() {
     // Extension downloads are not expected to be persisted. We don't need to
     // wait for a callback from them, so we don't add them to |guids_to_add_|.
@@ -331,7 +332,7 @@
 
   // Extension and user scripts download are not persisted.
   AddDownload();
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   AddUserScriptDownload();
   AddExtensionDownload();
 #endif
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 92d4da50..ef8d8cc 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -160,6 +160,7 @@
 #include "content/public/common/main_function_params.h"
 #include "device/geolocation/geolocation_delegate.h"
 #include "device/geolocation/geolocation_provider.h"
+#include "extensions/features/features.h"
 #include "media/base/media_resources.h"
 #include "net/base/net_module.h"
 #include "net/cookies/cookie_monster.h"
@@ -237,12 +238,12 @@
 #include "chrome/browser/background/background_mode_manager.h"
 #endif  // BUILDFLAG(ENABLE_BACKGROUND)
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/startup_helper.h"
 #include "extensions/browser/extension_protocols.h"
 #include "extensions/common/features/feature_provider.h"
 #include "extensions/components/javascript_dialog_extensions_client/javascript_dialog_extension_client_impl.h"
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW) && !defined(OFFICIAL_BUILD)
 #include "printing/printed_document.h"
@@ -1305,9 +1306,9 @@
   g_browser_process->profile_manager()->CleanUpEphemeralProfiles();
 #endif  // !defined(OS_ANDROID)
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   javascript_dialog_extensions_client::InstallClient();
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
   InstallChromeJavaScriptNativeDialogFactory();
 }
@@ -1450,7 +1451,7 @@
   IncognitoModePrefs::InitializePlatformParentalControls();
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (!variations::GetVariationParamValue(
       "LightSpeed", "EarlyInitStartup").empty()) {
     // Try to compute this early on another thread so that we don't spend time
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 47900fbf..ce9507de 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -175,6 +175,7 @@
 #include "device/bluetooth/public/interfaces/adapter.mojom.h"
 #include "device/usb/public/interfaces/chooser_service.mojom.h"
 #include "device/usb/public/interfaces/device_manager.mojom.h"
+#include "extensions/features/features.h"
 #include "gin/v8_initializer.h"
 #include "net/base/mime_util.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
@@ -289,7 +290,7 @@
 #include "components/nacl/common/nacl_switches.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/accessibility/animation_policy_prefs.h"
 #include "chrome/browser/extensions/chrome_content_browser_client_extensions_part.h"
 #include "chrome/browser/media/cast_transport_host_filter.h"
@@ -371,7 +372,7 @@
 using content::FileDescriptorInfo;
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 using extensions::APIPermission;
 using extensions::ChromeContentBrowserClientExtensionsPart;
 using extensions::Extension;
@@ -708,7 +709,7 @@
 }
 #endif  // defined(OS_ANDROID)
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 // By default, JavaScript, images and autoplay are enabled in guest content.
 void GetGuestViewDefaultContentSettingRules(
     bool incognito,
@@ -733,7 +734,7 @@
                                   std::string(),
                                   incognito));
 }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 void CreateUsbDeviceManager(
     RenderFrameHost* render_frame_host,
@@ -829,7 +830,7 @@
   TtsController::GetInstance()->SetTtsEngineDelegate(tts_extension_engine);
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extra_parts_.push_back(new ChromeContentBrowserClientExtensionsPart);
 #endif
 }
@@ -939,7 +940,7 @@
   // SiteInstance URL - "chrome-guest://app_id/persist?partition".
   if (site.SchemeIs(content::kGuestScheme))
     partition_id = site.spec();
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // The partition ID for extensions with isolated storage is treated similarly
   // to the above.
   else if (site.SchemeIs(extensions::kExtensionScheme) &&
@@ -974,7 +975,7 @@
   partition_name->clear();
   *in_memory = false;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   bool success = extensions::WebViewGuest::GetGuestPartitionConfigForSite(
       site, partition_domain, partition_name, in_memory);
 
@@ -1026,7 +1027,7 @@
 
   host->AddFilter(new ChromeRenderMessageFilter(
       id, profile, host->GetStoragePartition()->GetServiceWorkerContext()));
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   host->AddFilter(new cast::CastTransportHostFilter);
 #endif
 #if BUILDFLAG(ENABLE_PRINTING)
@@ -1076,7 +1077,7 @@
 
   RendererContentSettingRules rules;
   if (host->IsForGuestsOnly()) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     GetGuestViewDefaultContentSettingRules(profile->IsOffTheRecord(), &rules);
 #else
     NOTREACHED();
@@ -1099,7 +1100,7 @@
   if (search::ShouldAssignURLToInstantRenderer(url, profile))
     return search::GetEffectiveURLForInstant(url, profile);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return ChromeContentBrowserClientExtensionsPart::GetEffectiveURL(
       profile, url);
 #else
@@ -1121,7 +1122,7 @@
   if (search::ShouldUseProcessPerSiteForInstantURL(effective_url, profile))
     return true;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return ChromeContentBrowserClientExtensionsPart::ShouldUseProcessPerSite(
       profile, effective_url);
 #else
@@ -1132,7 +1133,7 @@
 bool ChromeContentBrowserClient::DoesSiteRequireDedicatedProcess(
     content::BrowserContext* browser_context,
     const GURL& effective_site_url) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (ChromeContentBrowserClientExtensionsPart::DoesSiteRequireDedicatedProcess(
           browser_context, effective_site_url)) {
     return true;
@@ -1154,7 +1155,7 @@
   if (effective_site_url.SchemeIs(chrome::kChromeSearchScheme))
     return false;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Disable origin lock if this is an extension/app that applies effective URL
   // mappings.
   if (!ChromeContentBrowserClientExtensionsPart::ShouldLockToOrigin(
@@ -1184,7 +1185,7 @@
 bool ChromeContentBrowserClient::CanCommitURL(
     content::RenderProcessHost* process_host,
     const GURL& url) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return ChromeContentBrowserClientExtensionsPart::CanCommitURL(
       process_host, url);
 #else
@@ -1194,7 +1195,7 @@
 
 bool ChromeContentBrowserClient::ShouldAllowOpenURL(
     content::SiteInstance* site_instance, const GURL& url) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   bool result;
   if (ChromeContentBrowserClientExtensionsPart::ShouldAllowOpenURL(
           site_instance, url, &result))
@@ -1239,7 +1240,7 @@
     *referrer = content::Referrer();
   }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   ChromeContentBrowserClientExtensionsPart::OverrideNavigationParams(
       site_instance, transition, is_renderer_initiated, referrer);
 #endif
@@ -1268,7 +1269,7 @@
       return is_instant_process && should_be_in_instant_process;
   }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return ChromeContentBrowserClientExtensionsPart::IsSuitableHost(
       profile, process_host, site_url);
 #else
@@ -1298,7 +1299,7 @@
   if (!url.is_valid())
     return false;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   Profile* profile = Profile::FromBrowserContext(browser_context);
   return ChromeContentBrowserClientExtensionsPart::
       ShouldTryToUseExistingProcessHost(
@@ -1344,7 +1345,7 @@
     SiteInstance* site_instance,
     const GURL& current_url,
     const GURL& new_url) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return ChromeContentBrowserClientExtensionsPart::
       ShouldSwapBrowsingInstancesForNavigation(
           site_instance, current_url, new_url);
@@ -1357,7 +1358,7 @@
     content::BrowserContext* browser_context,
     const GURL& current_url,
     const GURL& new_url) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return ChromeContentBrowserClientExtensionsPart::
       ShouldSwapProcessesForRedirect(browser_context, current_url, new_url);
 #else
@@ -1681,7 +1682,7 @@
       autofill::switches::kEnableSuggestionsWithSubstringMatch,
       autofill::switches::kIgnoreAutocompleteOffForAutofill,
       autofill::switches::kLocalHeuristicsOnlyForPasswordGeneration,
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
       extensions::switches::kAllowHTTPBackgroundPage,
       extensions::switches::kAllowLegacyExtensionManifests,
       extensions::switches::kEnableAppWindowControls,
@@ -1728,7 +1729,7 @@
     command_line->CopySwitchesFrom(browser_command_line, kSwitchNames,
                                    arraysize(kSwitchNames));
   } else if (process_type == switches::kUtilityProcess) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     static const char* const kSwitchNames[] = {
       extensions::switches::kAllowHTTPBackgroundPage,
       extensions::switches::kEnableExperimentalExtensionApis,
@@ -1818,7 +1819,7 @@
     int render_frame_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Check if this is an extension-related service worker, and, if so, if it's
   // allowed (this can return false if, e.g., the extension is disabled).
   // If it's not allowed, return immediately. We deliberately do *not* report
@@ -1927,14 +1928,14 @@
       io_data->GetCookieSettings();
   bool allow = cookie_settings->IsSettingCookieAllowed(url, url);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   GuestPermissionRequestHelper(url, render_frames, callback, allow);
 #else
   FileSystemAccessed(url, render_frames, callback, allow);
 #endif
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 void ChromeContentBrowserClient::GuestPermissionRequestHelper(
     const GURL& url,
     const std::vector<std::pair<int, int> >& render_frames,
@@ -2096,7 +2097,7 @@
 ChromeContentBrowserClient::OverrideRequestContextForURL(
     const GURL& url, content::ResourceContext* context) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (url.SchemeIs(extensions::kExtensionScheme)) {
     ProfileIOData* io_data = ProfileIOData::FromResourceContext(context);
     return io_data->extensions_request_context();
@@ -2259,7 +2260,7 @@
   // If the opener is trying to create a background window but doesn't have
   // the appropriate permission, fail the attempt.
   if (container_type == WINDOW_CONTAINER_TYPE_BACKGROUND) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     ProfileIOData* io_data = ProfileIOData::FromResourceContext(context);
     InfoMap* map = io_data->GetExtensionInfoMap();
     if (!map->SecurityOriginHasAPIPermission(
@@ -2284,7 +2285,7 @@
     return true;
   }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (extensions::WebViewRendererState::GetInstance()->IsGuest(
       render_process_id)) {
     return true;
@@ -2484,7 +2485,7 @@
   web_prefs->hyperlink_auditing_enabled =
       prefs->GetBoolean(prefs::kEnableHyperlinkAuditing);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   std::string image_animation_policy =
       prefs->GetString(prefs::kAnimationPolicy);
   if (image_animation_policy == kAnimationPolicyOnce)
@@ -2689,7 +2690,7 @@
     const GURL& url,
     bool private_api,
     const content::SocketPermissionRequest* params) {
-#if defined(ENABLE_PLUGINS) && defined(ENABLE_EXTENSIONS)
+#if defined(ENABLE_PLUGINS) && BUILDFLAG(ENABLE_EXTENSIONS)
   return ChromeContentBrowserClientPluginsPart::AllowPepperSocketAPI(
       browser_context, url, private_api, params, allowed_socket_origins_);
 #else
@@ -2700,7 +2701,7 @@
 bool ChromeContentBrowserClient::IsPepperVpnProviderAPIAllowed(
     content::BrowserContext* browser_context,
     const GURL& url) {
-#if defined(ENABLE_PLUGINS) && defined(ENABLE_EXTENSIONS)
+#if defined(ENABLE_PLUGINS) && BUILDFLAG(ENABLE_EXTENSIONS)
   return ChromeContentBrowserClientPluginsPart::IsPepperVpnProviderAPIAllowed(
       browser_context, url);
 #else
@@ -2711,7 +2712,7 @@
 std::unique_ptr<content::VpnServiceProxy>
 ChromeContentBrowserClient::GetVpnServiceProxy(
     content::BrowserContext* browser_context) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return ChromeContentBrowserClientExtensionsPart::GetVpnServiceProxy(
       browser_context);
 #else
@@ -2927,7 +2928,7 @@
     service_manager::InterfaceRegistry* registry,
     content::RenderFrameHost* render_frame_host) {
   if (base::FeatureList::IsEnabled(features::kWebUsb)
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
       &&
       !render_frame_host->GetSiteInstance()->GetSiteURL().SchemeIs(
           extensions::kExtensionScheme)
@@ -3149,7 +3150,7 @@
   }
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   throttles.push_back(new extensions::ExtensionNavigationThrottle(handle));
 #endif
 
@@ -3178,7 +3179,7 @@
 bool ChromeContentBrowserClient::IsPluginAllowedToCallRequestOSFileHandle(
     content::BrowserContext* browser_context,
     const GURL& url) {
-#if defined(ENABLE_PLUGINS) && defined(ENABLE_EXTENSIONS)
+#if defined(ENABLE_PLUGINS) && BUILDFLAG(ENABLE_EXTENSIONS)
   return ChromeContentBrowserClientPluginsPart::
       IsPluginAllowedToCallRequestOSFileHandle(browser_context, url,
                                                allowed_file_handle_origins_);
@@ -3190,7 +3191,7 @@
 bool ChromeContentBrowserClient::IsPluginAllowedToUseDevChannelAPIs(
     content::BrowserContext* browser_context,
     const GURL& url) {
-#if defined(ENABLE_PLUGINS) && defined(ENABLE_EXTENSIONS)
+#if defined(ENABLE_PLUGINS) && BUILDFLAG(ENABLE_EXTENSIONS)
   return ChromeContentBrowserClientPluginsPart::
       IsPluginAllowedToUseDevChannelAPIs(browser_context, url,
                                          allowed_dev_channel_origins_);
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index ce1c70e..26a0843 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -17,6 +17,7 @@
 #include "base/memory/weak_ptr.h"
 #include "build/build_config.h"
 #include "content/public/browser/content_browser_client.h"
+#include "extensions/features/features.h"
 
 class ChromeContentBrowserClientParts;
 
@@ -326,7 +327,7 @@
       base::Callback<void(bool)> callback,
       bool allow);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   void GuestPermissionRequestHelper(
       const GURL& url,
       const std::vector<std::pair<int, int> >& render_frames,
diff --git a/chrome/browser/chrome_notification_types.h b/chrome/browser/chrome_notification_types.h
index f485a3d..2e6b7fc 100644
--- a/chrome/browser/chrome_notification_types.h
+++ b/chrome/browser/chrome_notification_types.h
@@ -6,14 +6,15 @@
 #define CHROME_BROWSER_CHROME_NOTIFICATION_TYPES_H_
 
 #include "build/build_config.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/notification_types.h"
 #else
 #include "content/public/browser/notification_types.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #define PREVIOUS_END extensions::NOTIFICATION_EXTENSIONS_END
 #else
 #define PREVIOUS_END content::NOTIFICATION_CONTENT_END
@@ -127,7 +128,7 @@
   // Details<InfoBar::RemovedDetails>.
   NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // This notification is sent when extensions::TabHelper::SetExtensionApp is
   // invoked. The source is the extensions::TabHelper SetExtensionApp was
   // invoked on.
@@ -313,7 +314,7 @@
 
   // Cookies -----------------------------------------------------------------
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Sent when a cookie changes, for consumption by extensions. The source is a
   // Profile object, the details are a ChromeCookieDetails object.
   NOTIFICATION_COOKIE_CHANGED_FOR_EXTENSIONS,
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index dac98d2..d2e26af 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -4,6 +4,7 @@
 
 import("//build/config/features.gni")
 import("//build/config/ui.gni")
+import("//extensions/features/features.gni")
 import("//media/media_options.gni")
 import("//printing/features/features.gni")
 import("//third_party/protobuf/proto_library.gni")
@@ -702,6 +703,9 @@
     "login/saml/saml_offline_signin_limiter_factory.h",
     "login/screen_manager.cc",
     "login/screen_manager.h",
+    "login/screens/arc_terms_of_service_screen.cc",
+    "login/screens/arc_terms_of_service_screen.h",
+    "login/screens/arc_terms_of_service_screen_actor.h",
     "login/screens/base_screen.cc",
     "login/screens/base_screen.h",
     "login/screens/base_screen_delegate.h",
diff --git a/chrome/browser/chromeos/arc/arc_auth_service.cc b/chrome/browser/chromeos/arc/arc_auth_service.cc
index d9484ce..a342fc0 100644
--- a/chrome/browser/chromeos/arc/arc_auth_service.cc
+++ b/chrome/browser/chromeos/arc/arc_auth_service.cc
@@ -164,7 +164,7 @@
         DCHECK(!account_info_callback_.is_null());
         mojom::AccountInfoPtr account_info = mojom::AccountInfo::New();
         if (!is_enforced) {
-          account_info->auth_code = nullptr;
+          account_info->auth_code = base::nullopt;
         } else {
           account_info->auth_code = auth_code;
         }
diff --git a/chrome/browser/chromeos/arc/arc_auth_service_browsertest.cc b/chrome/browser/chromeos/arc/arc_auth_service_browsertest.cc
index 95aeff89..b36aa09 100644
--- a/chrome/browser/chromeos/arc/arc_auth_service_browsertest.cc
+++ b/chrome/browser/chromeos/arc/arc_auth_service_browsertest.cc
@@ -53,14 +53,14 @@
 
 namespace {
 
-const char kRefreshToken[] = "fake-refresh-token";
+constexpr char kRefreshToken[] = "fake-refresh-token";
 // Set managed auth token for Android managed accounts.
-const char kManagedAuthToken[] = "managed-auth-token";
+constexpr char kManagedAuthToken[] = "managed-auth-token";
 // Set unmanaged auth token for other Android unmanaged accounts.
-const char kUnmanagedAuthToken[] = "unmanaged-auth-token";
-const char kWellKnownConsumerName[] = "test@gmail.com";
-const char kFakeUserName[] = "test@example.com";
-const char kFakeAuthCode[] = "fake-auth-code";
+constexpr char kUnmanagedAuthToken[] = "unmanaged-auth-token";
+constexpr char kWellKnownConsumerName[] = "test@gmail.com";
+constexpr char kFakeUserName[] = "test@example.com";
+constexpr char kFakeAuthCode[] = "fake-auth-code";
 
 // JobCallback for the interceptor.
 net::URLRequestJob* ResponseJob(net::URLRequest* request,
@@ -349,7 +349,7 @@
 
   auth_instance_.callback =
       base::Bind([](const mojom::AccountInfoPtr& account_info) {
-        EXPECT_EQ(kFakeAuthCode, account_info->auth_code);
+        EXPECT_EQ(kFakeAuthCode, account_info->auth_code.value());
         EXPECT_EQ(mojom::ChromeAccountType::ROBOT_ACCOUNT,
                   account_info->account_type);
         EXPECT_FALSE(account_info->is_managed);
diff --git a/chrome/browser/chromeos/arc/downloads_watcher/arc_downloads_watcher_service.cc b/chrome/browser/chromeos/arc/downloads_watcher/arc_downloads_watcher_service.cc
index 21d5f697..ea12672 100644
--- a/chrome/browser/chromeos/arc/downloads_watcher/arc_downloads_watcher_service.cc
+++ b/chrome/browser/chromeos/arc/downloads_watcher/arc_downloads_watcher_service.cc
@@ -10,9 +10,7 @@
 #include <iterator>
 #include <map>
 #include <memory>
-#include <string>
 #include <utility>
-#include <vector>
 
 #include "base/callback.h"
 #include "base/files/file_enumerator.h"
@@ -186,7 +184,7 @@
 // Downloads directory.
 class ArcDownloadsWatcherService::DownloadsWatcher {
  public:
-  using Callback = base::Callback<void(mojo::Array<mojo::String> paths)>;
+  using Callback = base::Callback<void(const std::vector<std::string>& paths)>;
 
   explicit DownloadsWatcher(const Callback& callback);
   ~DownloadsWatcher();
@@ -296,13 +294,13 @@
 
   last_timestamp_map_ = std::move(current_timestamp_map);
 
-  mojo::Array<mojo::String> mojo_paths(changed_paths.size());
+  std::vector<std::string> string_paths(changed_paths.size());
   for (size_t i = 0; i < changed_paths.size(); ++i) {
-    mojo_paths[i] = changed_paths[i].value();
+    string_paths[i] = changed_paths[i].value();
   }
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
-      base::Bind(callback_, base::Passed(std::move(mojo_paths))));
+      base::Bind(callback_, base::Passed(std::move(string_paths))));
   if (last_notify_time_ > snapshot_time) {
     base::PostTaskAndReplyWithResult(
         BrowserThread::GetBlockingPool(), FROM_HERE,
@@ -359,14 +357,14 @@
 }
 
 void ArcDownloadsWatcherService::OnDownloadsChanged(
-    mojo::Array<mojo::String> paths) {
+    const std::vector<std::string>& paths) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   auto* instance = arc_bridge_service()->file_system()->GetInstanceForMethod(
       "RequestMediaScan");
   if (!instance)
     return;
-  instance->RequestMediaScan(std::move(paths));
+  instance->RequestMediaScan(paths);
 }
 
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/downloads_watcher/arc_downloads_watcher_service.h b/chrome/browser/chromeos/arc/downloads_watcher/arc_downloads_watcher_service.h
index 2e38ebac..7709c78a 100644
--- a/chrome/browser/chromeos/arc/downloads_watcher/arc_downloads_watcher_service.h
+++ b/chrome/browser/chromeos/arc/downloads_watcher/arc_downloads_watcher_service.h
@@ -6,14 +6,14 @@
 #define CHROME_BROWSER_CHROMEOS_ARC_DOWNLOADS_WATCHER_ARC_DOWNLOADS_WATCHER_SERVICE_H_
 
 #include <memory>
+#include <string>
+#include <vector>
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "components/arc/arc_service.h"
 #include "components/arc/common/file_system.mojom.h"
 #include "components/arc/instance_holder.h"
-#include "mojo/public/cpp/bindings/array.h"
-#include "mojo/public/cpp/bindings/string.h"
 
 namespace base {
 class FilePath;
@@ -45,7 +45,7 @@
   void StartWatchingDownloads();
   void StopWatchingDownloads();
 
-  void OnDownloadsChanged(mojo::Array<mojo::String> paths);
+  void OnDownloadsChanged(const std::vector<std::string>& paths);
 
   std::unique_ptr<DownloadsWatcher> watcher_;
 
diff --git a/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.cc b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.cc
new file mode 100644
index 0000000..e09e9ab
--- /dev/null
+++ b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.cc
@@ -0,0 +1,83 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.h"
+
+#include "chrome/browser/chromeos/login/screens/base_screen_delegate.h"
+#include "chrome/browser/chromeos/login/wizard_controller.h"
+#include "chrome/browser/metrics/metrics_reporting_state.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
+
+namespace chromeos {
+
+ArcTermsOfServiceScreen::ArcTermsOfServiceScreen(
+    BaseScreenDelegate* base_screen_delegate,
+    ArcTermsOfServiceScreenActor* actor)
+    : BaseScreen(base_screen_delegate), actor_(actor) {
+  DCHECK(actor_);
+  if (actor_)
+    actor_->SetDelegate(this);
+}
+
+ArcTermsOfServiceScreen::~ArcTermsOfServiceScreen() {
+  if (actor_)
+    actor_->SetDelegate(nullptr);
+}
+
+void ArcTermsOfServiceScreen::PrepareToShow() {}
+
+void ArcTermsOfServiceScreen::Show() {
+  if (!actor_)
+    return;
+
+  // Show the screen.
+  actor_->Show();
+}
+
+void ArcTermsOfServiceScreen::Hide() {
+  if (actor_)
+    actor_->Hide();
+}
+
+std::string ArcTermsOfServiceScreen::GetName() const {
+  return WizardController::kArcTermsOfServiceScreenName;
+}
+
+void ArcTermsOfServiceScreen::OnSkip() {
+  ApplyTerms(false);
+}
+
+void ArcTermsOfServiceScreen::OnAccept(bool enable_metrics,
+                                       bool enable_backup_restore,
+                                       bool enable_location_services) {
+  Profile* profile = ProfileManager::GetActiveUserProfile();
+  if (enable_metrics)
+    ChangeMetricsReportingState(true);
+
+  profile->GetPrefs()->SetBoolean(prefs::kArcBackupRestoreEnabled,
+                                  enable_backup_restore);
+  profile->GetPrefs()->SetBoolean(prefs::kArcLocationServiceEnabled,
+                                  enable_location_services);
+
+  ApplyTerms(true);
+}
+
+void ArcTermsOfServiceScreen::ApplyTerms(bool accepted) {
+  Profile* profile = ProfileManager::GetActiveUserProfile();
+  profile->GetPrefs()->SetBoolean(prefs::kArcTermsAccepted, accepted);
+  profile->GetPrefs()->SetBoolean(prefs::kArcEnabled, accepted);
+
+  Finish(BaseScreenDelegate::ARC_TERMS_OF_SERVICE_FINISHED);
+}
+
+void ArcTermsOfServiceScreen::OnActorDestroyed(
+    ArcTermsOfServiceScreenActor* actor) {
+  DCHECK_EQ(actor, actor_);
+  actor_ = nullptr;
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.h b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.h
new file mode 100644
index 0000000..61b0e677
--- /dev/null
+++ b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.h
@@ -0,0 +1,48 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_ARC_TERMS_OF_SERVICE_SCREEN_H_
+#define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_ARC_TERMS_OF_SERVICE_SCREEN_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor.h"
+#include "chrome/browser/chromeos/login/screens/base_screen.h"
+
+namespace chromeos {
+
+class BaseScreenDelegate;
+
+class ArcTermsOfServiceScreen : public BaseScreen,
+                                public ArcTermsOfServiceScreenActor::Delegate {
+ public:
+  ArcTermsOfServiceScreen(BaseScreenDelegate* base_screen_delegate,
+                          ArcTermsOfServiceScreenActor* actor);
+  ~ArcTermsOfServiceScreen() override;
+
+  // BaseScreen:
+  void PrepareToShow() override;
+  void Show() override;
+  void Hide() override;
+  std::string GetName() const override;
+
+  // ArcTermsOfServiceScreenActor::Delegate:
+  void OnSkip() override;
+  void OnAccept(bool enable_metrics,
+                bool enable_backup_restore,
+                bool enable_location_services) override;
+  void OnActorDestroyed(ArcTermsOfServiceScreenActor* actor) override;
+
+ private:
+  void ApplyTerms(bool accepted);
+
+  ArcTermsOfServiceScreenActor* actor_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArcTermsOfServiceScreen);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_ARC_TERMS_OF_SERVICE_SCREEN_H_
diff --git a/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor.h b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor.h
new file mode 100644
index 0000000..ea354b7
--- /dev/null
+++ b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor.h
@@ -0,0 +1,60 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_ARC_TERMS_OF_SERVICE_SCREEN_ACTOR_H_
+#define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_ARC_TERMS_OF_SERVICE_SCREEN_ACTOR_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+namespace chromeos {
+
+// Interface for dependency injection between TermsOfServiceScreen and its
+// WebUI representation.
+class ArcTermsOfServiceScreenActor {
+ public:
+  class Delegate {
+   public:
+    virtual ~Delegate() = default;
+
+    // Called when the user skips the PlayStore Terms of Service.
+    virtual void OnSkip() = 0;
+
+    // Called when the user accepts the PlayStore Terms of Service.
+    virtual void OnAccept(bool enable_metrics,
+                          bool enable_backup_restore,
+                          bool enable_location_services) = 0;
+
+    // Called when actor is destroyed so there is no dead reference to it.
+    virtual void OnActorDestroyed(ArcTermsOfServiceScreenActor* actor) = 0;
+
+   protected:
+    Delegate() = default;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(Delegate);
+  };
+
+  virtual ~ArcTermsOfServiceScreenActor() {}
+
+  // Sets screen this actor belongs to.
+  virtual void SetDelegate(Delegate* screen) = 0;
+
+  // Shows the contents of the screen.
+  virtual void Show() = 0;
+
+  // Hides the contents of the screen.
+  virtual void Hide() = 0;
+
+ protected:
+  ArcTermsOfServiceScreenActor() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ArcTermsOfServiceScreenActor);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_ARC_TERMS_OF_SERVICE_SCREEN_ACTOR_H_
diff --git a/chrome/browser/chromeos/login/screens/base_screen_delegate.h b/chrome/browser/chromeos/login/screens/base_screen_delegate.h
index ee626ea..069aa6e 100644
--- a/chrome/browser/chromeos/login/screens/base_screen_delegate.h
+++ b/chrome/browser/chromeos/login/screens/base_screen_delegate.h
@@ -53,6 +53,7 @@
     CONTROLLER_PAIRING_FINISHED = 20,
     ENABLE_DEBUGGING_FINISHED = 21,
     ENABLE_DEBUGGING_CANCELED = 22,
+    ARC_TERMS_OF_SERVICE_FINISHED = 23,
     EXIT_CODES_COUNT  // not a real code, must be the last
   };
 
diff --git a/chrome/browser/chromeos/login/screens/user_image_screen.cc b/chrome/browser/chromeos/login/screens/user_image_screen.cc
index afbba38..3c52e213 100644
--- a/chrome/browser/chromeos/login/screens/user_image_screen.cc
+++ b/chrome/browser/chromeos/login/screens/user_image_screen.cc
@@ -223,7 +223,7 @@
 }
 
 const user_manager::User* UserImageScreen::GetUser() {
-  return user_manager::UserManager::Get()->GetLoggedInUser();
+  return user_manager::UserManager::Get()->GetActiveUser();
 }
 
 UserImageManager* UserImageScreen::GetUserImageManager() {
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index 70dd32e62..60097de5 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -788,7 +788,7 @@
   if (!connection_error) {
     // We are in one of "done" states here.
     user_manager::UserManager::Get()->SaveUserOAuthStatus(
-        user_manager::UserManager::Get()->GetLoggedInUser()->GetAccountId(),
+        user_manager::UserManager::Get()->GetActiveUser()->GetAccountId(),
         user_status);
   }
 
diff --git a/chrome/browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc b/chrome/browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc
index 3328c76..5afe3b3 100644
--- a/chrome/browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc
+++ b/chrome/browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc
@@ -258,7 +258,7 @@
     run_loop.Run();
 
     const user_manager::User* user =
-        user_manager::UserManager::Get()->GetLoggedInUser();
+        user_manager::UserManager::Get()->GetActiveUser();
     ASSERT_TRUE(user);
     UserImageManagerImpl* uim = reinterpret_cast<UserImageManagerImpl*>(
         ChromeUserManager::Get()->GetUserImageManager(user->GetAccountId()));
diff --git a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
index e51cb552..2edff5a 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
@@ -1013,7 +1013,7 @@
   for (user_manager::UserList::iterator it = users_.begin();
        it != users_.end();) {
     if ((*it)->IsDeviceLocalAccount()) {
-      if (*it != GetLoggedInUser())
+      if (*it != GetActiveUser())
         DeleteUser(*it);
       it = users_.erase(it);
     } else {
@@ -1075,7 +1075,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!IsUserLoggedIn())
     return GetDefaultUserFlow();
-  return GetUserFlow(GetLoggedInUser()->GetAccountId());
+  return GetUserFlow(GetActiveUser()->GetAccountId());
 }
 
 UserFlow* ChromeUserManagerImpl::GetUserFlow(
diff --git a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
index 0990778..d8be14ea 100644
--- a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
+++ b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
@@ -127,7 +127,7 @@
 UserFlow* FakeChromeUserManager::GetCurrentUserFlow() const {
   if (!IsUserLoggedIn())
     return GetDefaultUserFlow();
-  return GetUserFlow(GetLoggedInUser()->GetAccountId());
+  return GetUserFlow(GetActiveUser()->GetAccountId());
 }
 
 UserFlow* FakeChromeUserManager::GetUserFlow(
diff --git a/chrome/browser/chromeos/login/users/mock_user_manager.cc b/chrome/browser/chromeos/login/users/mock_user_manager.cc
index 8f87e48..828db9f 100644
--- a/chrome/browser/chromeos/login/users/mock_user_manager.cc
+++ b/chrome/browser/chromeos/login/users/mock_user_manager.cc
@@ -44,46 +44,37 @@
   return user_list_;
 }
 
-const user_manager::User* MockUserManager::GetLoggedInUser() const {
-  return user_list_.empty() ? NULL : user_list_.front();
-}
-
-user_manager::User* MockUserManager::GetLoggedInUser() {
-  return user_list_.empty() ? NULL : user_list_.front();
-}
-
 user_manager::UserList MockUserManager::GetUnlockUsers() const {
   return user_list_;
 }
 
 const AccountId& MockUserManager::GetOwnerAccountId() const {
-  temporary_owner_account_id_ = GetLoggedInUser()->GetAccountId();
-  return temporary_owner_account_id_;
+  return GetActiveUser()->GetAccountId();
 }
 
 const user_manager::User* MockUserManager::GetActiveUser() const {
-  return GetLoggedInUser();
+  return user_list_.empty() ? nullptr : user_list_.front();
 }
 
 user_manager::User* MockUserManager::GetActiveUser() {
-  return GetLoggedInUser();
+  return user_list_.empty() ? nullptr : user_list_.front();
 }
 
 const user_manager::User* MockUserManager::GetPrimaryUser() const {
-  return GetLoggedInUser();
+  return GetActiveUser();
 }
 
 BootstrapManager* MockUserManager::GetBootstrapManager() {
-  return NULL;
+  return nullptr;
 }
 
 MultiProfileUserController* MockUserManager::GetMultiProfileUserController() {
-  return NULL;
+  return nullptr;
 }
 
 UserImageManager* MockUserManager::GetUserImageManager(
     const AccountId& account_id) {
-  return NULL;
+  return nullptr;
 }
 
 SupervisedUserManager* MockUserManager::GetSupervisedUserManager() {
diff --git a/chrome/browser/chromeos/login/users/mock_user_manager.h b/chrome/browser/chromeos/login/users/mock_user_manager.h
index e6f02dd..a600ea0a 100644
--- a/chrome/browser/chromeos/login/users/mock_user_manager.h
+++ b/chrome/browser/chromeos/login/users/mock_user_manager.h
@@ -122,10 +122,8 @@
   // You can't mock these functions easily because nobody can create
   // User objects but the ChromeUserManager and us.
   const user_manager::UserList& GetUsers() const override;
-  const user_manager::User* GetLoggedInUser() const override;
   user_manager::UserList GetUnlockUsers() const override;
   const AccountId& GetOwnerAccountId() const override;
-  user_manager::User* GetLoggedInUser() override;
   const user_manager::User* GetActiveUser() const override;
   user_manager::User* GetActiveUser() override;
   const user_manager::User* GetPrimaryUser() const override;
@@ -174,10 +172,6 @@
   std::unique_ptr<MockUserImageManager> user_image_manager_;
   std::unique_ptr<FakeSupervisedUserManager> supervised_user_manager_;
   user_manager::UserList user_list_;
-  // TODO (alemate): remove temporary_owner_account_id_ as soon as
-  // User::GetAccountId will
-  // return constant reference. crbug.com/546863
-  mutable AccountId temporary_owner_account_id_ = EmptyAccountId();
 };
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
index 82f66841..52455a06 100644
--- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
+++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
@@ -401,7 +401,7 @@
       return;
   }
   SetUserWallpaperNow(
-      user_manager::UserManager::Get()->GetLoggedInUser()->GetAccountId());
+      user_manager::UserManager::Get()->GetActiveUser()->GetAccountId());
 }
 
 void WallpaperManager::InitializeWallpaper() {
@@ -443,7 +443,7 @@
       InitializeRegisteredDeviceWallpaper();
     return;
   }
-  SetUserWallpaperDelayed(user_manager->GetLoggedInUser()->GetAccountId());
+  SetUserWallpaperDelayed(user_manager->GetActiveUser()->GetAccountId());
 }
 
 void WallpaperManager::Open() {
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index 6042585..ee6e4a4 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -27,12 +27,14 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
+#include "chrome/browser/chromeos/arc/arc_auth_service.h"
 #include "chrome/browser/chromeos/customization/customization_document.h"
 #include "chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen.h"
 #include "chrome/browser/chromeos/login/enrollment/enrollment_screen.h"
 #include "chrome/browser/chromeos/login/existing_user_controller.h"
 #include "chrome/browser/chromeos/login/helper.h"
 #include "chrome/browser/chromeos/login/hwid_checker.h"
+#include "chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.h"
 #include "chrome/browser/chromeos/login/screens/device_disabled_screen.h"
 #include "chrome/browser/chromeos/login/screens/enable_debugging_screen.h"
 #include "chrome/browser/chromeos/login/screens/error_screen.h"
@@ -100,14 +102,14 @@
 const unsigned int kResolveTimeZoneTimeoutSeconds = 60;
 
 // Stores the list of all screens that should be shown when resuming OOBE.
-const char *kResumableScreens[] = {
-  chromeos::WizardController::kNetworkScreenName,
-  chromeos::WizardController::kUpdateScreenName,
-  chromeos::WizardController::kEulaScreenName,
-  chromeos::WizardController::kEnrollmentScreenName,
-  chromeos::WizardController::kTermsOfServiceScreenName,
-  chromeos::WizardController::kAutoEnrollmentCheckScreenName
-};
+const char* kResumableScreens[] = {
+    chromeos::WizardController::kNetworkScreenName,
+    chromeos::WizardController::kUpdateScreenName,
+    chromeos::WizardController::kEulaScreenName,
+    chromeos::WizardController::kEnrollmentScreenName,
+    chromeos::WizardController::kTermsOfServiceScreenName,
+    chromeos::WizardController::kArcTermsOfServiceScreenName,
+    chromeos::WizardController::kAutoEnrollmentCheckScreenName};
 
 // Checks flag for HID-detection screen show.
 bool CanShowHIDDetectionScreen() {
@@ -213,6 +215,7 @@
 const char WizardController::kKioskAutolaunchScreenName[] = "autolaunch";
 const char WizardController::kErrorScreenName[] = "error-message";
 const char WizardController::kTermsOfServiceScreenName[] = "tos";
+const char WizardController::kArcTermsOfServiceScreenName[] = "arc_tos";
 const char WizardController::kAutoEnrollmentCheckScreenName[] =
   "auto-enrollment-check";
 const char WizardController::kWrongHWIDScreenName[] = "wrong-hwid";
@@ -390,6 +393,9 @@
   } else if (screen_name == kTermsOfServiceScreenName) {
     return new TermsOfServiceScreen(this,
                                     oobe_ui_->GetTermsOfServiceScreenActor());
+  } else if (screen_name == kArcTermsOfServiceScreenName) {
+    return new ArcTermsOfServiceScreen(
+        this, oobe_ui_->GetArcTermsOfServiceScreenActor());
   } else if (screen_name == kWrongHWIDScreenName) {
     return new WrongHWIDScreen(this, oobe_ui_->GetWrongHWIDScreenActor());
   } else if (screen_name == kSupervisedUserCreationScreenName) {
@@ -477,7 +483,7 @@
   // logins.
   if (user_manager->IsLoggedInAsPublicAccount() ||
       (user_manager->IsCurrentUserNonCryptohomeDataEphemeral() &&
-       user_manager->GetLoggedInUser()->GetType() !=
+       user_manager->GetActiveUser()->GetType() !=
            user_manager::USER_TYPE_REGULAR)) {
     OnUserImageSkipped();
     return;
@@ -533,11 +539,11 @@
 void WizardController::ShowTermsOfServiceScreen() {
   // Only show the Terms of Service when logging into a public account and Terms
   // of Service have been specified through policy. In all other cases, advance
-  // to the user image screen immediately.
+  // to the Arc opt-in screen immediately.
   if (!user_manager::UserManager::Get()->IsLoggedInAsPublicAccount() ||
       !ProfileManager::GetActiveUserProfile()->GetPrefs()->IsManagedPreference(
           prefs::kTermsOfServiceURL)) {
-    ShowUserImageScreen();
+    ShowArcTermsOfServiceScreen();
     return;
   }
 
@@ -546,6 +552,36 @@
   SetCurrentScreen(GetScreen(kTermsOfServiceScreenName));
 }
 
+void WizardController::ShowArcTermsOfServiceScreen() {
+  bool show_arc_terms = false;
+  const Profile* profile = ProfileManager::GetActiveUserProfile();
+
+  const base::CommandLine* command_line =
+      base::CommandLine::ForCurrentProcess();
+  if (!command_line->HasSwitch(chromeos::switches::kEnableArcOOBEOptIn)) {
+    VLOG(1) << "Skip Arc Terms of Service screen because Arc OOBE OptIn is "
+            << "disabled.";
+  } else if (!user_manager::UserManager::Get()->IsUserLoggedIn()) {
+    VLOG(1) << "Skip Arc Terms of Service screen because user is not "
+            << "logged in.";
+  } else if (!arc::ArcAuthService::IsAllowedForProfile(profile)) {
+    VLOG(1) << "Skip Arc Terms of Service screen because Arc is not allowed.";
+  } else if (profile->GetPrefs()->IsManagedPreference(prefs::kArcEnabled) &&
+             !profile->GetPrefs()->GetBoolean(prefs::kArcEnabled)) {
+    VLOG(1) << "Skip Arc Terms of Service screen because Arc is disabled.";
+  } else {
+    show_arc_terms = true;
+  }
+
+  if (show_arc_terms) {
+    VLOG(1) << "Showing Arc Terms of Service screen.";
+    SetStatusAreaVisible(true);
+    SetCurrentScreen(GetScreen(kArcTermsOfServiceScreenName));
+  } else {
+    ShowUserImageScreen();
+  }
+}
+
 void WizardController::ShowWrongHWIDScreen() {
   VLOG(1) << "Showing wrong HWID screen.";
   SetStatusAreaVisible(false);
@@ -809,7 +845,14 @@
 }
 
 void WizardController::OnTermsOfServiceAccepted() {
-  // If the user accepts the Terms of Service, advance to the user image screen.
+  // If the user accepts the Terms of Service, advance to the PlayStore terms
+  // of serice.
+  ShowArcTermsOfServiceScreen();
+}
+
+void WizardController::OnArcTermsOfServiceFinished() {
+  // If the user finished with the PlayStore Terms of Service, advance to the
+  // user image screen.
   ShowUserImageScreen();
 }
 
@@ -996,6 +1039,8 @@
     ShowEnrollmentScreen();
   } else if (screen_name == kTermsOfServiceScreenName) {
     ShowTermsOfServiceScreen();
+  } else if (screen_name == kArcTermsOfServiceScreenName) {
+    ShowArcTermsOfServiceScreen();
   } else if (screen_name == kWrongHWIDScreenName) {
     ShowWrongHWIDScreen();
   } else if (screen_name == kAutoEnrollmentCheckScreenName) {
@@ -1107,6 +1152,9 @@
     case TERMS_OF_SERVICE_ACCEPTED:
       OnTermsOfServiceAccepted();
       break;
+    case ARC_TERMS_OF_SERVICE_FINISHED:
+      OnArcTermsOfServiceFinished();
+      break;
     case WRONG_HWID_WARNING_SKIPPED:
       OnWrongHWIDWarningSkipped();
       break;
diff --git a/chrome/browser/chromeos/login/wizard_controller.h b/chrome/browser/chromeos/login/wizard_controller.h
index e9aa363d..a6408905 100644
--- a/chrome/browser/chromeos/login/wizard_controller.h
+++ b/chrome/browser/chromeos/login/wizard_controller.h
@@ -154,6 +154,7 @@
   static const char kKioskAutolaunchScreenName[];
   static const char kErrorScreenName[];
   static const char kTermsOfServiceScreenName[];
+  static const char kArcTermsOfServiceScreenName[];
   static const char kAutoEnrollmentCheckScreenName[];
   static const char kWrongHWIDScreenName[];
   static const char kSupervisedUserCreationScreenName[];
@@ -178,6 +179,7 @@
   void ShowEnableDebuggingScreen();
   void ShowKioskEnableScreen();
   void ShowTermsOfServiceScreen();
+  void ShowArcTermsOfServiceScreen();
   void ShowWrongHWIDScreen();
   void ShowAutoEnrollmentCheckScreen();
   void ShowSupervisedUserCreationScreen();
@@ -209,6 +211,7 @@
   void OnWrongHWIDWarningSkipped();
   void OnTermsOfServiceDeclined();
   void OnTermsOfServiceAccepted();
+  void OnArcTermsOfServiceFinished();
   void OnControllerPairingFinished();
   void OnAutoEnrollmentCheckCompleted();
 
diff --git a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
index edc3b62..130b6fd 100644
--- a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
+++ b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
@@ -1223,7 +1223,9 @@
 // TODO(dzhioev): Add tests for controller/host pairing flow.
 // http://crbug.com/375191
 
-static_assert(BaseScreenDelegate::EXIT_CODES_COUNT == 23,
+// TODO(khmel): Add tests for Arc OptIn flow.
+// http://crbug.com/651144
+static_assert(BaseScreenDelegate::EXIT_CODES_COUNT == 24,
               "tests for new control flow are missing");
 
 }  // namespace chromeos
diff --git a/chrome/browser/content_settings/host_content_settings_map_factory.cc b/chrome/browser/content_settings/host_content_settings_map_factory.cc
index f9189fe8..1d0547c2 100644
--- a/chrome/browser/content_settings/host_content_settings_map_factory.cc
+++ b/chrome/browser/content_settings/host_content_settings_map_factory.cc
@@ -13,8 +13,9 @@
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/sync_preferences/pref_service_syncable.h"
 #include "content/public/browser/browser_thread.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_service.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/extension_system_provider.h"
@@ -34,7 +35,7 @@
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
   DependsOn(SupervisedUserSettingsServiceFactory::GetInstance());
 #endif
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   DependsOn(
       extensions::ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
 #endif
@@ -86,14 +87,14 @@
                    settings_map->GetWeakPtr(), true /* after_sync */));
   }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   ExtensionService *ext_service =
       extensions::ExtensionSystem::Get(profile)->extension_service();
   // This may be null in testing or when the extenion_service hasn't been
   // initialized, in which case it will be registered then.
   if (ext_service)
     ext_service->RegisterContentSettings(settings_map.get());
-#endif // defined(ENABLE_EXTENSIONS)
+#endif // BUILDFLAG(ENABLE_EXTENSIONS)
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
   SupervisedUserSettingsService* supervised_service =
       SupervisedUserSettingsServiceFactory::GetForProfile(profile);
diff --git a/chrome/browser/download/chrome_download_manager_delegate.cc b/chrome/browser/download/chrome_download_manager_delegate.cc
index 9af11e6..bce8164 100644
--- a/chrome/browser/download/chrome_download_manager_delegate.cc
+++ b/chrome/browser/download/chrome_download_manager_delegate.cc
@@ -52,6 +52,7 @@
 #include "content/public/browser/download_manager.h"
 #include "content/public/browser/notification_source.h"
 #include "content/public/browser/page_navigator.h"
+#include "extensions/features/features.h"
 #include "net/base/filename_util.h"
 #include "net/base/mime_util.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -66,7 +67,7 @@
 #include "chrome/browser/chromeos/drive/file_system_util.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/api/downloads/downloads_api.h"
 #include "chrome/browser/extensions/crx_installer.h"
 #include "chrome/browser/extensions/webstore_installer.h"
@@ -288,7 +289,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (path.Extension().empty())
     return false;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // TODO(asanka): This determination is done based on |path|, while
   // ShouldOpenDownload() detects extension downloads based on the
   // characteristics of the download. Reconcile this. http://crbug.com/167702
@@ -384,7 +385,7 @@
 
 bool ChromeDownloadManagerDelegate::ShouldOpenDownload(
     DownloadItem* item, const content::DownloadOpenDelayedCallback& callback) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (download_crx_util::IsExtensionDownload(*item) &&
       !extensions::WebstoreInstaller::GetAssociatedApproval(*item)) {
     scoped_refptr<extensions::CrxInstaller> crx_installer =
@@ -569,7 +570,7 @@
     const base::FilePath& virtual_path,
     const NotifyExtensionsCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::ExtensionDownloadsEventRouter* router =
       DownloadServiceFactory::GetForBrowserContext(profile_)
           ->GetExtensionEventRouter();
@@ -751,7 +752,7 @@
     int type,
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   DCHECK_EQ(extensions::NOTIFICATION_CRX_INSTALLER_DONE, type);
 
   registrar_.Remove(this, extensions::NOTIFICATION_CRX_INSTALLER_DONE, source);
diff --git a/chrome/browser/download/chrome_download_manager_delegate.h b/chrome/browser/download/chrome_download_manager_delegate.h
index 7d738e52..5fd0a258 100644
--- a/chrome/browser/download/chrome_download_manager_delegate.h
+++ b/chrome/browser/download/chrome_download_manager_delegate.h
@@ -22,6 +22,7 @@
 #include "content/public/browser/download_manager_delegate.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
+#include "extensions/features/features.h"
 
 class DownloadPrefs;
 class Profile;
@@ -165,7 +166,7 @@
   IdCallbackVector id_callbacks_;
   std::unique_ptr<DownloadPrefs> download_prefs_;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Maps from pending extension installations to DownloadItem IDs.
   typedef base::hash_map<extensions::CrxInstaller*,
       content::DownloadOpenDelayedCallback> CrxInstallerMap;
diff --git a/chrome/browser/download/download_history.cc b/chrome/browser/download/download_history.cc
index 49262d0..c472e7c 100644
--- a/chrome/browser/download/download_history.cc
+++ b/chrome/browser/download/download_history.cc
@@ -40,8 +40,9 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_item.h"
 #include "content/public/browser/download_manager.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/api/downloads/downloads_api.h"
 #endif
 
@@ -117,7 +118,7 @@
 history::DownloadRow GetDownloadRow(
     content::DownloadItem* item) {
   std::string by_ext_id, by_ext_name;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::DownloadedByExtension* by_ext =
       extensions::DownloadedByExtension::Get(item);
   if (by_ext) {
@@ -275,7 +276,7 @@
         history::ToContentDownloadDangerType(it->danger_type),
         history::ToContentDownloadInterruptReason(it->interrupt_reason),
         it->opened);
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     if (!it->by_ext_id.empty() && !it->by_ext_name.empty()) {
       new extensions::DownloadedByExtension(
           item, it->by_ext_id, it->by_ext_name);
diff --git a/chrome/browser/download/download_history_unittest.cc b/chrome/browser/download/download_history_unittest.cc
index 04a22bf..f692be6 100644
--- a/chrome/browser/download/download_history_unittest.cc
+++ b/chrome/browser/download/download_history_unittest.cc
@@ -23,9 +23,10 @@
 #include "content/public/test/mock_download_manager.h"
 #include "content/public/test/test_browser_thread.h"
 #include "content/public/test/test_utils.h"
+#include "extensions/features/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/api/downloads/downloads_api.h"
 #endif
 
@@ -437,7 +438,7 @@
     EXPECT_CALL(manager(), GetDownload(id))
         .WillRepeatedly(Return(&item(index)));
     EXPECT_CALL(item(index), IsTemporary()).WillRepeatedly(Return(false));
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     new extensions::DownloadedByExtension(
         &item(index), by_extension_id, by_extension_name);
 #endif
diff --git a/chrome/browser/download/download_service.h b/chrome/browser/download/download_service.h
index 44a61f9..261cb566 100644
--- a/chrome/browser/download/download_service.h
+++ b/chrome/browser/download/download_service.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "extensions/features/features.h"
 
 class ChromeDownloadManagerDelegate;
 class DownloadHistory;
@@ -38,7 +39,7 @@
   // no HistoryService for profile. Virtual for testing.
   virtual DownloadHistory* GetDownloadHistory() = 0;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   virtual extensions::ExtensionDownloadsEventRouter*
   GetExtensionEventRouter() = 0;
 #endif
diff --git a/chrome/browser/download/download_service_impl.cc b/chrome/browser/download/download_service_impl.cc
index 5d184bda..9d3c959 100644
--- a/chrome/browser/download/download_service_impl.cc
+++ b/chrome/browser/download/download_service_impl.cc
@@ -15,8 +15,9 @@
 #include "chrome/browser/profiles/profile.h"
 #include "components/history/core/browser/history_service.h"
 #include "content/public/browser/download_manager.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/api/downloads/downloads_api.h"
 #endif
 
@@ -49,7 +50,7 @@
 
   manager_delegate_->SetDownloadManager(manager);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extension_event_router_.reset(
       new extensions::ExtensionDownloadsEventRouter(profile_, manager));
 #endif
@@ -84,7 +85,7 @@
   return download_history_.get();
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 extensions::ExtensionDownloadsEventRouter*
 DownloadServiceImpl::GetExtensionEventRouter() {
   return extension_event_router_.get();
@@ -144,7 +145,7 @@
     // manually earlier. See http://crbug.com/131692
     BrowserContext::GetDownloadManager(profile_)->Shutdown();
   }
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extension_event_router_.reset();
 #endif
   manager_delegate_.reset();
diff --git a/chrome/browser/download/download_service_impl.h b/chrome/browser/download/download_service_impl.h
index 1030df5..8a15239 100644
--- a/chrome/browser/download/download_service_impl.h
+++ b/chrome/browser/download/download_service_impl.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "chrome/browser/download/download_service.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "extensions/features/features.h"
 
 class ChromeDownloadManagerDelegate;
 class DownloadHistory;
@@ -35,7 +36,7 @@
   // DownloadService
   ChromeDownloadManagerDelegate* GetDownloadManagerDelegate() override;
   DownloadHistory* GetDownloadHistory() override;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::ExtensionDownloadsEventRouter* GetExtensionEventRouter() override;
 #endif
   bool HasCreatedDownloadManager() override;
@@ -70,7 +71,7 @@
 // Once we have extensions on android, we probably need the EventRouter
 // in ContentViewDownloadDelegate which knows about both GET and POST
 // downloads.
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // The ExtensionDownloadsEventRouter dispatches download creation, change, and
   // erase events to extensions. Like ChromeDownloadManagerDelegate, it's a
   // chrome-level concept and its lifetime should match DownloadManager. There
diff --git a/chrome/browser/download/download_target_determiner.cc b/chrome/browser/download/download_target_determiner.cc
index b3841d7..e1e662e7 100644
--- a/chrome/browser/download/download_target_determiner.cc
+++ b/chrome/browser/download/download_target_determiner.cc
@@ -26,11 +26,12 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_interrupt_reasons.h"
 #include "extensions/common/constants.h"
+#include "extensions/features/features.h"
 #include "net/base/filename_util.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "url/origin.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/webstore_installer.h"
 #include "extensions/common/feature_switch.h"
 #endif
@@ -875,7 +876,7 @@
     return DownloadFileType::NOT_DANGEROUS;
   }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Extensions that are not from the gallery are considered dangerous.
   // When off-store install is disabled we skip this, since in this case, we
   // will not offer to install the extension.
diff --git a/chrome/browser/download/download_target_determiner_unittest.cc b/chrome/browser/download/download_target_determiner_unittest.cc
index b050b36..ca44f4c 100644
--- a/chrome/browser/download/download_target_determiner_unittest.cc
+++ b/chrome/browser/download/download_target_determiner_unittest.cc
@@ -37,6 +37,7 @@
 #include "content/public/test/mock_download_item.h"
 #include "content/public/test/test_renderer_host.h"
 #include "content/public/test/web_contents_tester.h"
+#include "extensions/features/features.h"
 #include "net/base/mime_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -48,7 +49,7 @@
 #include "content/public/common/webplugininfo.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/common/extension.h"
 #endif
 
@@ -1213,7 +1214,7 @@
 }
 #endif  // !BUILDFLAG(ANDROID_JAVA_UI)
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 // These test cases are run with "Prompt for download" user preference set to
 // true. Automatic extension downloads shouldn't cause prompting.
 // Android doesn't support extensions.
@@ -1259,7 +1260,7 @@
   RunTestCasesWithActiveItem(kPromptingTestCases,
                              arraysize(kPromptingTestCases));
 }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 // If the download path is managed, then we don't show any prompts.
 // Note that if the download path is managed, then PromptForDownload() is false.
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index 5688605..965847a 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -5,6 +5,7 @@
 import("//build/config/features.gni")
 import("//build/config/ui.gni")
 import("//chrome/common/features.gni")
+import("//extensions/features/features.gni")
 
 assert(enable_extensions)
 
@@ -887,6 +888,7 @@
     "//extensions/browser",
     "//extensions/browser/api:api_registration",
     "//extensions/common/api",
+    "//extensions/features",
     "//extensions/strings",
     "//net",
     "//printing/features",
diff --git a/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc b/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc
index 5eb39da..3583b20 100644
--- a/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc
@@ -119,7 +119,7 @@
       result->SetBoolean("isGuest", user_manager->IsLoggedInAsGuest());
       result->SetBoolean("isKiosk", user_manager->IsLoggedInAsKioskApp());
 
-      const user_manager::User* user = user_manager->GetLoggedInUser();
+      const user_manager::User* user = user_manager->GetActiveUser();
       result->SetString("email", user->GetAccountId().GetUserEmail());
       result->SetString("displayEmail", user->display_email());
 
diff --git a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc
index b27b6cd..cc139f9e 100644
--- a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc
+++ b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc
@@ -291,6 +291,8 @@
     : web_contents_(web_contents),
       profile_(
           Profile::FromBrowserContext(web_contents_->GetBrowserContext())) {
+  // We need to guarantee the BookmarkTabHelper is created.
+  BookmarkTabHelper::CreateForWebContents(web_contents_);
   BookmarkTabHelper* bookmark_tab_helper =
       BookmarkTabHelper::FromWebContents(web_contents_);
   bookmark_tab_helper->set_bookmark_drag_delegate(this);
@@ -298,10 +300,8 @@
 
 BookmarkManagerPrivateDragEventRouter::
     ~BookmarkManagerPrivateDragEventRouter() {
-  BookmarkTabHelper* bookmark_tab_helper =
-      BookmarkTabHelper::FromWebContents(web_contents_);
-  if (bookmark_tab_helper->bookmark_drag_delegate() == this)
-    bookmark_tab_helper->set_bookmark_drag_delegate(NULL);
+  // No need to remove ourselves as the BookmarkTabHelper's delegate, since they
+  // are both WebContentsUserData and will be deleted at the same time.
 }
 
 void BookmarkManagerPrivateDragEventRouter::MaybeCreateForWebContents(
diff --git a/chrome/browser/extensions/api/braille_display_private/braille_display_private_apitest.cc b/chrome/browser/extensions/api/braille_display_private/braille_display_private_apitest.cc
index 129548a..97a4424 100644
--- a/chrome/browser/extensions/api/braille_display_private/braille_display_private_apitest.cc
+++ b/chrome/browser/extensions/api/braille_display_private/braille_display_private_apitest.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef USE_BRLAPI
-#error This test requires brlapi.
-#endif
-
 #include <stddef.h>
 
 #include <deque>
diff --git a/chrome/browser/extensions/api/image_writer_private/operation.h b/chrome/browser/extensions/api/image_writer_private/operation.h
index 6710298c..98412b7 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation.h
+++ b/chrome/browser/extensions/api/image_writer_private/operation.h
@@ -19,7 +19,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.h"
 #include "chrome/common/extensions/api/image_writer_private.h"
-
+#include "extensions/common/extension_id.h"
 
 namespace image_writer_api = extensions::api::image_writer_private;
 
@@ -59,7 +59,6 @@
  public:
   typedef base::Callback<void(bool, const std::string&)> StartWriteCallback;
   typedef base::Callback<void(bool, const std::string&)> CancelWriteCallback;
-  typedef std::string ExtensionId;
 
   Operation(base::WeakPtr<OperationManager> manager,
             const ExtensionId& extension_id,
diff --git a/chrome/browser/extensions/api/image_writer_private/operation_manager.h b/chrome/browser/extensions/api/image_writer_private/operation_manager.h
index 60d25a67..993019b 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation_manager.h
+++ b/chrome/browser/extensions/api/image_writer_private/operation_manager.h
@@ -19,6 +19,7 @@
 #include "content/public/browser/notification_registrar.h"
 #include "extensions/browser/browser_context_keyed_api_factory.h"
 #include "extensions/browser/extension_registry_observer.h"
+#include "extensions/common/extension_id.h"
 #include "url/gurl.h"
 
 namespace image_writer_api = extensions::api::image_writer_private;
@@ -43,8 +44,6 @@
                          public extensions::ExtensionRegistryObserver,
                          public base::SupportsWeakPtr<OperationManager> {
  public:
-  typedef std::string ExtensionId;
-
   explicit OperationManager(content::BrowserContext* context);
   ~OperationManager() override;
 
diff --git a/chrome/browser/extensions/api/messaging/message_service.h b/chrome/browser/extensions/api/messaging/message_service.h
index 2c6b52d..667594e 100644
--- a/chrome/browser/extensions/api/messaging/message_service.h
+++ b/chrome/browser/extensions/api/messaging/message_service.h
@@ -19,6 +19,7 @@
 #include "extensions/browser/api/messaging/native_message_host.h"
 #include "extensions/browser/browser_context_keyed_api_factory.h"
 #include "extensions/common/api/messaging/message.h"
+#include "extensions/common/extension_id.h"
 
 class GURL;
 class Profile;
@@ -195,9 +196,8 @@
 
   // A map of channel ID to information about the extension that is waiting
   // for that channel to open. Used for lazy background pages.
-  using ExtensionID = std::string;
   using PendingLazyBackgroundPageChannel =
-      std::pair<content::BrowserContext*, ExtensionID>;
+      std::pair<content::BrowserContext*, ExtensionId>;
   using PendingLazyBackgroundPageChannelMap =
       std::map<int, PendingLazyBackgroundPageChannel>;
 
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index 021df4b..f882148 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -1320,13 +1320,25 @@
     return true;
   }
 
-  web_contents_->GetController().LoadURL(
-      url, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
+  bool use_renderer_initiated = false;
+  // For the PDF extension, treat it as renderer-initiated so that it does not
+  // show in the omnibox until it commits.  This avoids URL spoofs since urls
+  // can be opened on behalf of untrusted content.
+  // TODO(devlin|nasko): Make this the default for all extensions.
+  if (extension() && extension()->id() == extension_misc::kPdfExtensionId)
+    use_renderer_initiated = true;
+  NavigationController::LoadURLParams load_params(url);
+  load_params.is_renderer_initiated = use_renderer_initiated;
+  web_contents_->GetController().LoadURLWithParams(load_params);
 
   // The URL of a tab contents never actually changes to a JavaScript URL, so
   // this check only makes sense in other cases.
-  if (!url.SchemeIs(url::kJavaScriptScheme))
-    DCHECK_EQ(url.spec(), web_contents_->GetURL().spec());
+  if (!url.SchemeIs(url::kJavaScriptScheme)) {
+    // The URL should be present in the pending entry, though it may not be
+    // visible in the omnibox until it commits.
+    DCHECK_EQ(
+        url, web_contents_->GetController().GetPendingEntry()->GetVirtualURL());
+  }
 
   return true;
 }
diff --git a/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc b/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
index 7a5cc4bc..5b8193f 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api_unittest.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 "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/extensions/api/tabs/tabs_api.h"
 #include "chrome/browser/extensions/extension_function_test_utils.h"
 #include "chrome/browser/extensions/extension_service_test_base.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/browser/sessions/session_tab_helper.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/test_browser_window.h"
@@ -14,6 +16,7 @@
 #include "content/public/common/browser_side_navigation_policy.h"
 #include "content/public/test/browser_side_navigation_test_utils.h"
 #include "content/public/test/web_contents_tester.h"
+#include "extensions/browser/api_test_utils.h"
 #include "extensions/common/extension_builder.h"
 #include "extensions/common/test_util.h"
 
@@ -216,4 +219,54 @@
   }
 }
 
+// Test that using the PDF extension for tab updates is treated as a
+// renderer-initiated navigation. crbug.com/660498
+TEST_F(TabsApiUnitTest, PDFExtensionNavigation) {
+  DictionaryBuilder manifest;
+  manifest.Set("name", "pdfext")
+      .Set("description", "desc")
+      .Set("version", "0.1")
+      .Set("manifest_version", 2)
+      .Set("permissions", ListBuilder().Append("tabs").Build());
+  scoped_refptr<const Extension> extension =
+      ExtensionBuilder()
+          .SetManifest(manifest.Build())
+          .SetID(extension_misc::kPdfExtensionId)
+          .Build();
+  ASSERT_TRUE(extension);
+
+  content::WebContents* web_contents =
+      content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
+  ASSERT_TRUE(web_contents);
+  content::WebContentsTester* web_contents_tester =
+      content::WebContentsTester::For(web_contents);
+  const GURL kGoogle("http://www.google.com");
+  web_contents_tester->NavigateAndCommit(kGoogle);
+  EXPECT_EQ(kGoogle, web_contents->GetLastCommittedURL());
+  EXPECT_EQ(kGoogle, web_contents->GetVisibleURL());
+
+  SessionTabHelper::CreateForWebContents(web_contents);
+  int tab_id = SessionTabHelper::IdForTab(web_contents);
+  browser()->tab_strip_model()->AppendWebContents(web_contents, true);
+
+  scoped_refptr<TabsUpdateFunction> function = new TabsUpdateFunction();
+  function->set_extension(extension.get());
+  function->set_browser_context(profile());
+  std::unique_ptr<base::ListValue> args(
+      extension_function_test_utils::ParseList(base::StringPrintf(
+          "[%d, {\"url\":\"http://example.com\"}]", tab_id)));
+  function->SetArgs(args.get());
+  api_test_utils::SendResponseHelper response_helper(function.get());
+  function->RunWithValidation()->Execute();
+
+  EXPECT_EQ(kGoogle, web_contents->GetLastCommittedURL());
+  EXPECT_EQ(kGoogle, web_contents->GetVisibleURL());
+
+  // Clean up.
+  response_helper.WaitForResponse();
+  while (!browser()->tab_strip_model()->empty())
+    browser()->tab_strip_model()->CloseWebContentsAt(0, 0);
+  base::RunLoop().RunUntilIdle();
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/tabs/tabs_test.cc b/chrome/browser/extensions/api/tabs/tabs_test.cc
index 67beee0..9e33edf 100644
--- a/chrome/browser/extensions/api/tabs/tabs_test.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_test.cc
@@ -41,6 +41,7 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/page_zoom.h"
 #include "content/public/common/url_constants.h"
+#include "content/public/test/browser_test_utils.h"
 #include "extensions/browser/api_test_utils.h"
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/app_window/app_window_registry.h"
@@ -50,6 +51,7 @@
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/result_catcher.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "ui/base/window_open_disposition.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
@@ -2116,4 +2118,33 @@
       base::MatchPattern(error, manifest_errors::kCannotAccessChromeUrl));
 }
 
+// Regression test for crbug.com/660498.
+IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Foo) {
+  ASSERT_TRUE(StartEmbeddedTestServer());
+  content::WebContents* first_web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(first_web_contents);
+  chrome::NewTab(browser());
+  content::WebContents* second_web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_NE(first_web_contents, second_web_contents);
+  GURL url = embedded_test_server()->GetURL(
+      "/extensions/api_test/tabs/pdf_extension_test.html");
+  content::TestNavigationManager navigation_manager(
+      second_web_contents, GURL("http://www.facebook.com:83"));
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url, WindowOpenDisposition::CURRENT_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+  EXPECT_TRUE(navigation_manager.WaitForRequestStart());
+
+  browser()->tab_strip_model()->ActivateTabAt(0, true);
+  EXPECT_EQ(first_web_contents,
+            browser()->tab_strip_model()->GetActiveWebContents());
+  browser()->tab_strip_model()->ActivateTabAt(1, true);
+  EXPECT_EQ(second_web_contents,
+            browser()->tab_strip_model()->GetActiveWebContents());
+
+  EXPECT_EQ(url, second_web_contents->GetVisibleURL());
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/component_extensions_whitelist/whitelist.cc b/chrome/browser/extensions/component_extensions_whitelist/whitelist.cc
index d540b5f..8de6f24 100644
--- a/chrome/browser/extensions/component_extensions_whitelist/whitelist.cc
+++ b/chrome/browser/extensions/component_extensions_whitelist/whitelist.cc
@@ -93,7 +93,7 @@
     case IDR_HOTWORD_MANIFEST:
 #endif
     case IDR_IDENTITY_API_SCOPE_APPROVAL_MANIFEST:
-#if defined(IMAGE_LOADER_EXTENSION)
+#if defined(OS_CHROMEOS)
     case IDR_IMAGE_LOADER_MANIFEST:
 #endif
     case IDR_NETWORK_SPEECH_SYNTHESIS_MANIFEST:
diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc
index 15b2f4c..44bfce1e 100644
--- a/chrome/browser/extensions/component_loader.cc
+++ b/chrome/browser/extensions/component_loader.cc
@@ -363,10 +363,10 @@
 }
 
 void ComponentLoader::AddImageLoaderExtension() {
-#if defined(IMAGE_LOADER_EXTENSION)
+#if defined(OS_CHROMEOS)
   Add(IDR_IMAGE_LOADER_MANIFEST,
       base::FilePath(FILE_PATH_LITERAL("image_loader")));
-#endif  // defined(IMAGE_LOADER_EXTENSION)
+#endif  // defined(OS_CHROMEOS)
 }
 
 void ComponentLoader::AddNetworkSpeechSynthesisExtension() {
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index 0c1d502..3235cab9 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -34,8 +34,9 @@
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_set.h"
 #include "extensions/common/manifest.h"
+#include "extensions/features/features.h"
 
-#if !defined(ENABLE_EXTENSIONS)
+#if !BUILDFLAG(ENABLE_EXTENSIONS)
 #error "Extensions must be enabled"
 #endif
 
diff --git a/chrome/browser/geolocation/geolocation_permission_context_extensions.cc b/chrome/browser/geolocation/geolocation_permission_context_extensions.cc
index d92f7ee..7ae8189 100644
--- a/chrome/browser/geolocation/geolocation_permission_context_extensions.cc
+++ b/chrome/browser/geolocation/geolocation_permission_context_extensions.cc
@@ -5,8 +5,9 @@
 #include "chrome/browser/geolocation/geolocation_permission_context_extensions.h"
 
 #include "base/callback.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/permissions/permission_request_id.h"
 #include "chrome/browser/profiles/profile.h"
 #include "extensions/browser/extension_registry.h"
@@ -22,19 +23,19 @@
 
 namespace {
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 void CallbackContentSettingWrapper(
     const base::Callback<void(ContentSetting)>& callback,
     bool allowed) {
   callback.Run(allowed ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK);
 }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 }  // anonymous namespace
 
 GeolocationPermissionContextExtensions::GeolocationPermissionContextExtensions(
     Profile* profile)
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     : profile_(profile)
 #endif
 {
@@ -53,7 +54,7 @@
     const base::Callback<void(ContentSetting)>& callback,
     bool* permission_set,
     bool* new_permission) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   GURL requesting_frame_origin = requesting_frame.GetOrigin();
 
   extensions::WebViewPermissionHelper* web_view_permission_helper =
@@ -98,14 +99,14 @@
     *new_permission = false;
     return true;
   }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
   return false;
 }
 
 bool GeolocationPermissionContextExtensions::CancelPermissionRequest(
     content::WebContents* web_contents,
     int bridge_id) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::WebViewPermissionHelper* web_view_permission_helper =
       web_contents ?
       extensions::WebViewPermissionHelper::FromWebContents(web_contents)
@@ -114,6 +115,6 @@
     web_view_permission_helper->CancelGeolocationPermissionRequest(bridge_id);
     return true;
   }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
   return false;
 }
diff --git a/chrome/browser/geolocation/geolocation_permission_context_extensions.h b/chrome/browser/geolocation/geolocation_permission_context_extensions.h
index df042b8..bdefb95 100644
--- a/chrome/browser/geolocation/geolocation_permission_context_extensions.h
+++ b/chrome/browser/geolocation/geolocation_permission_context_extensions.h
@@ -8,6 +8,7 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "components/content_settings/core/common/content_settings.h"
+#include "extensions/features/features.h"
 
 namespace content {
 class WebContents;
@@ -40,7 +41,7 @@
                                int bridge_id);
 
  private:
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   Profile* profile_;
 #endif
 
diff --git a/chrome/browser/geolocation/geolocation_permission_context_unittest.cc b/chrome/browser/geolocation/geolocation_permission_context_unittest.cc
index 81d8c6e..a0e8add 100644
--- a/chrome/browser/geolocation/geolocation_permission_context_unittest.cc
+++ b/chrome/browser/geolocation/geolocation_permission_context_unittest.cc
@@ -48,6 +48,7 @@
 #include "content/public/test/test_renderer_host.h"
 #include "content/public/test/test_utils.h"
 #include "content/public/test/web_contents_tester.h"
+#include "extensions/features/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if BUILDFLAG(ANDROID_JAVA_UI)
@@ -59,7 +60,7 @@
 #include "chrome/browser/ui/website_settings/mock_permission_prompt_factory.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/view_type_utils.h"
 #endif
 
@@ -247,7 +248,7 @@
       ->SendNavigate(entry->GetUniqueID(), true, url);
 
   // Set up required helpers, and make this be as "tabby" as the code requires.
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::SetViewType(new_tab, extensions::VIEW_TYPE_TAB_CONTENTS);
 #endif
 
@@ -280,7 +281,7 @@
   ChromeRenderViewHostTestHarness::SetUp();
 
   // Set up required helpers, and make this be as "tabby" as the code requires.
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::SetViewType(web_contents(), extensions::VIEW_TYPE_TAB_CONTENTS);
 #endif
   InfoBarService::CreateForWebContents(web_contents());
diff --git a/chrome/browser/interstitials/chrome_metrics_helper.cc b/chrome/browser/interstitials/chrome_metrics_helper.cc
index 54e5e2e..f69b390a 100644
--- a/chrome/browser/interstitials/chrome_metrics_helper.cc
+++ b/chrome/browser/interstitials/chrome_metrics_helper.cc
@@ -11,8 +11,9 @@
 #include "components/history/core/browser/history_service.h"
 #include "components/rappor/rappor_service.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/api/experience_sampling_private/experience_sampling.h"
 #endif
 
@@ -34,7 +35,7 @@
           g_browser_process->rappor_service()
               ? g_browser_process->rappor_service()->AsWeakPtr()
               : base::WeakPtr<rappor::RapporService>()),
-#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) || defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) || BUILDFLAG(ENABLE_EXTENSIONS)
       web_contents_(web_contents),
 #endif
       request_url_(request_url),
@@ -62,7 +63,7 @@
 
 void ChromeMetricsHelper::RecordExtraUserDecisionMetrics(
     security_interstitials::MetricsHelper::Decision decision) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (!sampling_event_.get()) {
     sampling_event_.reset(new extensions::ExperienceSamplingEvent(
         sampling_event_name_, request_url_,
@@ -88,7 +89,7 @@
 
 void ChromeMetricsHelper::RecordExtraUserInteractionMetrics(
     security_interstitials::MetricsHelper::Interaction interaction) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (!sampling_event_.get()) {
     sampling_event_.reset(new extensions::ExperienceSamplingEvent(
         sampling_event_name_, request_url_,
diff --git a/chrome/browser/interstitials/chrome_metrics_helper.h b/chrome/browser/interstitials/chrome_metrics_helper.h
index 15736de..f4a7b91 100644
--- a/chrome/browser/interstitials/chrome_metrics_helper.h
+++ b/chrome/browser/interstitials/chrome_metrics_helper.h
@@ -10,6 +10,7 @@
 #include "base/macros.h"
 #include "chrome/common/features.h"
 #include "components/security_interstitials/core/metrics_helper.h"
+#include "extensions/features/features.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -47,12 +48,12 @@
   void RecordExtraShutdownMetrics() override;
 
  private:
-#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) || defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) || BUILDFLAG(ENABLE_EXTENSIONS)
   content::WebContents* web_contents_;
 #endif
   const GURL request_url_;
   const std::string sampling_event_name_;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   std::unique_ptr<extensions::ExperienceSamplingEvent> sampling_event_;
 #endif
 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc
index 48a61fca..f25a7ec 100644
--- a/chrome/browser/io_thread.cc
+++ b/chrome/browser/io_thread.cc
@@ -65,6 +65,7 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/user_agent.h"
+#include "extensions/features/features.h"
 #include "net/base/host_mapping_rules.h"
 #include "net/base/logging_network_change_observer.h"
 #include "net/base/sdch_manager.h"
@@ -109,7 +110,7 @@
 #include "net/url_request/url_request_job_factory_impl.h"
 #include "url/url_constants.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/event_router_forwarder.h"
 #endif
 
@@ -317,7 +318,7 @@
     net_log::ChromeNetLog* net_log,
     extensions::EventRouterForwarder* extension_event_router_forwarder)
     : net_log_(net_log),
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
       extension_event_router_forwarder_(extension_event_router_forwarder),
 #endif
       globals_(nullptr),
@@ -494,7 +495,7 @@
   // Setup the HistogramWatcher to run on the IO thread.
   net::NetworkChangeNotifier::InitHistogramWatcher();
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   globals_->extension_event_router_forwarder =
       extension_event_router_forwarder_;
 #endif
diff --git a/chrome/browser/io_thread.h b/chrome/browser/io_thread.h
index b5a2a9d..1fed51b 100644
--- a/chrome/browser/io_thread.h
+++ b/chrome/browser/io_thread.h
@@ -28,6 +28,7 @@
 #include "components/ssl_config/ssl_config_service_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/browser_thread_delegate.h"
+#include "extensions/features/features.h"
 #include "net/base/network_change_notifier.h"
 #include "net/http/http_network_session.h"
 
@@ -183,7 +184,7 @@
     // |system_cookie_store| and |system_channel_id_service| are shared
     // between |proxy_script_fetcher_context| and |system_request_context|.
     std::unique_ptr<net::CookieStore> system_cookie_store;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     scoped_refptr<extensions::EventRouterForwarder>
         extension_event_router_forwarder;
 #endif
@@ -295,7 +296,7 @@
   void UpdateNegotiateEnablePort();
 
   extensions::EventRouterForwarder* extension_event_router_forwarder() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     return extension_event_router_forwarder_;
 #else
     return NULL;
@@ -326,7 +327,7 @@
   // threads during shutdown, but is used most frequently on the IOThread.
   net_log::ChromeNetLog* net_log_;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // The extensions::EventRouterForwarder allows for sending events to
   // extensions from the IOThread.
   extensions::EventRouterForwarder* extension_event_router_forwarder_;
diff --git a/chrome/browser/io_thread_unittest.cc b/chrome/browser/io_thread_unittest.cc
index 736958ed..da1bd497 100644
--- a/chrome/browser/io_thread_unittest.cc
+++ b/chrome/browser/io_thread_unittest.cc
@@ -24,6 +24,7 @@
 #include "components/variations/variations_associated_data.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "extensions/features/features.h"
 #include "net/cert_net/nss_ocsp.h"
 #include "net/http/http_auth_preferences.h"
 #include "net/http/http_auth_scheme.h"
@@ -33,7 +34,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/event_router_forwarder.h"
 #endif
 
@@ -106,7 +107,7 @@
   IOThreadTestWithIOThreadObject()
       : thread_bundle_(content::TestBrowserThreadBundle::REAL_IO_THREAD |
                        content::TestBrowserThreadBundle::DONT_START_THREADS) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     event_router_forwarder_ = new extensions::EventRouterForwarder;
 #endif
     PrefRegistrySimple* pref_registry = pref_service_.registry();
@@ -128,7 +129,7 @@
     // The IOThread constructor registers the IOThread object with as the
     // BrowserThreadDelegate for the io thread.
     io_thread_.reset(new IOThread(&pref_service_, &policy_service_, nullptr,
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
                                   event_router_forwarder_.get()
 #else
                                   nullptr
@@ -163,7 +164,7 @@
  private:
   base::ShadowingAtExitManager at_exit_manager_;
   TestingPrefServiceSimple pref_service_;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   scoped_refptr<extensions::EventRouterForwarder> event_router_forwarder_;
 #endif
   policy::PolicyMap policy_map_;
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
index 3b85089..e6faf04a 100644
--- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
+++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
@@ -66,6 +66,7 @@
 #include "content/public/browser/stream_info.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/resource_response.h"
+#include "extensions/features/features.h"
 #include "net/base/load_flags.h"
 #include "net/base/load_timing_info.h"
 #include "net/base/request_priority.h"
@@ -77,7 +78,7 @@
 #include "chrome/browser/component_updater/pnacl_component_installer.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/apps/app_url_redirector.h"
 #include "chrome/browser/extensions/api/streams_private/streams_private_api.h"
 #include "chrome/browser/extensions/user_script_listener.h"
@@ -117,7 +118,7 @@
 using content::ResourceRequestInfo;
 using content::ResourceType;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 using extensions::Extension;
 using extensions::StreamsPrivateAPI;
 #endif
@@ -172,7 +173,7 @@
     prerender_manager->AddProfileNetworkBytesIfEnabled(bytes);
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 void SendExecuteMimeTypeHandlerEvent(
     std::unique_ptr<content::StreamInfo> stream,
     int64_t expected_content_size,
@@ -214,7 +215,7 @@
       extension_id, std::move(stream), view_id, expected_content_size, embedded,
       frame_tree_node_id, render_process_id, render_frame_id);
 }
-#endif  // !defined(ENABLE_EXTENSIONS)
+#endif  // !BUILDFLAG(ENABLE_EXTENSIONS)
 
 void LaunchURL(
     const GURL& url,
@@ -377,7 +378,7 @@
 ChromeResourceDispatcherHostDelegate::ChromeResourceDispatcherHostDelegate()
     : download_request_limiter_(g_browser_process->download_request_limiter()),
       safe_browsing_(g_browser_process->safe_browsing_service())
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
       , user_script_listener_(new extensions::UserScriptListener())
 #endif
       {
@@ -388,7 +389,7 @@
 }
 
 ChromeResourceDispatcherHostDelegate::~ChromeResourceDispatcherHostDelegate() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   CHECK(stream_target_info_.empty());
 #endif
 }
@@ -561,7 +562,7 @@
     return false;
   }
   int child_id = info->GetChildID();
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // External protocols are disabled for guests. An exception is made for the
   // "mailto" protocol, so that pages that utilize it work properly in a
   // WebView.
@@ -573,7 +574,7 @@
       !url.SchemeIs(url::kMailToScheme)) {
     return false;
   }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 #if defined(OS_ANDROID)
   // Main frame external protocols are handled by
@@ -626,7 +627,7 @@
         io_data->supervised_user_url_filter()));
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   content::ResourceThrottle* wait_for_extensions_init_throttle =
       user_script_listener_->CreateResourceThrottle(request->url(),
                                                     resource_type);
@@ -656,7 +657,7 @@
 
 bool ChromeResourceDispatcherHostDelegate::ShouldForceDownloadResource(
     const GURL& url, const std::string& mime_type) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Special-case user scripts to get downloaded instead of viewed.
   return extensions::UserScript::IsURLUserScript(url, mime_type);
 #else
@@ -670,7 +671,7 @@
     const std::string& mime_type,
     GURL* origin,
     std::string* payload) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request);
   ProfileIOData* io_data =
       ProfileIOData::FromResourceContext(info->GetContext());
@@ -724,7 +725,7 @@
 void ChromeResourceDispatcherHostDelegate::OnStreamCreated(
     net::URLRequest* request,
     std::unique_ptr<content::StreamInfo> stream) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request);
   std::map<net::URLRequest*, StreamTargetInfo>::iterator ix =
       stream_target_info_.find(request);
@@ -755,7 +756,7 @@
                                               info->GetRouteID());
 
   // Built-in additional protection for the chrome web store origin.
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   GURL webstore_url(extension_urls::GetWebstoreLaunchURL());
   if (request->url().SchemeIsHTTPOrHTTPS() &&
       request->url().DomainIs(webstore_url.host().c_str())) {
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.h b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.h
index 2ac6a6df..f605d80 100644
--- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.h
+++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.h
@@ -14,6 +14,7 @@
 #include "base/memory/ref_counted.h"
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
 #include "content/public/browser/resource_dispatcher_host_delegate.h"
+#include "extensions/features/features.h"
 
 class DelayedResourceQueue;
 class DownloadRequestLimiter;
@@ -106,7 +107,7 @@
       ScopedVector<content::ResourceThrottle>* throttles);
 
  private:
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   struct StreamTargetInfo {
     std::string extension_id;
     std::string view_id;
@@ -115,7 +116,7 @@
 
   scoped_refptr<DownloadRequestLimiter> download_request_limiter_;
   scoped_refptr<safe_browsing::SafeBrowsingService> safe_browsing_;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   scoped_refptr<extensions::UserScriptListener> user_script_listener_;
   std::map<net::URLRequest*, StreamTargetInfo> stream_target_info_;
 #endif
diff --git a/chrome/browser/media/router/media_router_feature.cc b/chrome/browser/media/router/media_router_feature.cc
index a927294..d1a617ce 100644
--- a/chrome/browser/media/router/media_router_feature.cc
+++ b/chrome/browser/media/router/media_router_feature.cc
@@ -6,19 +6,20 @@
 
 #include "build/build_config.h"
 #include "content/public/browser/browser_context.h"
+#include "extensions/features/features.h"
 
 #if defined(ENABLE_MEDIA_ROUTER)
-#if defined(OS_ANDROID) || defined(ENABLE_EXTENSIONS)
+#if defined(OS_ANDROID) || BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_prefs/user_prefs.h"
-#endif  // defined(OS_ANDROID) || defined(ENABLE_EXTENSIONS)
+#endif  // defined(OS_ANDROID) || BUILDFLAG(ENABLE_EXTENSIONS)
 #endif  // defined(ENABLE_MEDIA_ROUTER)
 
 namespace media_router {
 
 #if defined(ENABLE_MEDIA_ROUTER)
-#if defined(OS_ANDROID) || defined(ENABLE_EXTENSIONS)
+#if defined(OS_ANDROID) || BUILDFLAG(ENABLE_EXTENSIONS)
 namespace {
 const PrefService::Preference* GetMediaRouterPref(
     content::BrowserContext* context) {
@@ -26,12 +27,12 @@
       ->FindPreference(prefs::kEnableMediaRouter);
 }
 }  // namespace
-#endif  // defined(OS_ANDROID) || defined(ENABLE_EXTENSIONS)
+#endif  // defined(OS_ANDROID) || BUILDFLAG(ENABLE_EXTENSIONS)
 #endif  // defined(ENABLE_MEDIA_ROUTER)
 
 bool MediaRouterEnabled(content::BrowserContext* context) {
 #if defined(ENABLE_MEDIA_ROUTER)
-#if defined(OS_ANDROID) || defined(ENABLE_EXTENSIONS)
+#if defined(OS_ANDROID) || BUILDFLAG(ENABLE_EXTENSIONS)
   const PrefService::Preference* pref = GetMediaRouterPref(context);
   // Only use the pref value if it set from a mandatory policy.
   if (pref->IsManaged() && !pref->IsDefaultValue()) {
@@ -40,9 +41,9 @@
     return allowed;
   }
   return true;
-#else  // !(defined(OS_ANDROID) || defined(ENABLE_EXTENSIONS))
+#else  // !(defined(OS_ANDROID) || BUILDFLAG(ENABLE_EXTENSIONS))
   return false;
-#endif  // defined(OS_ANDROID) || defined(ENABLE_EXTENSIONS)
+#endif  // defined(OS_ANDROID) || BUILDFLAG(ENABLE_EXTENSIONS)
 #else   // !defined(ENABLE_MEDIA_ROUTER)
   return false;
 #endif  // defined(ENABLE_MEDIA_ROUTER)
diff --git a/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc b/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc
index e4177e3..197cbe2 100644
--- a/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc
+++ b/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc
@@ -31,13 +31,14 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/media_stream_request.h"
 #include "extensions/common/constants.h"
+#include "extensions/features/features.h"
 #include "media/base/media_switches.h"
 
 #if defined(OS_CHROMEOS)
 #include "ash/shell.h"
 #endif  // defined(OS_CHROMEOS)
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/media/extension_media_access_handler.h"
 #include "chrome/browser/media/webrtc/desktop_capture_access_handler.h"
 #include "chrome/browser/media/webrtc/tab_capture_access_handler.h"
@@ -65,7 +66,7 @@
   return NULL;
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 inline CaptureAccessHandlerBase* ToCaptureAccessHandlerBase(
     MediaAccessHandler* handler) {
   return static_cast<CaptureAccessHandlerBase*>(handler);
@@ -82,7 +83,7 @@
       media_stream_capture_indicator_(new MediaStreamCaptureIndicator()) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   media_access_handlers_.push_back(new ExtensionMediaAccessHandler());
   media_access_handlers_.push_back(new DesktopCaptureAccessHandler());
   media_access_handlers_.push_back(new TabCaptureAccessHandler());
@@ -376,7 +377,7 @@
     int render_process_id,
     int render_frame_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   for (MediaAccessHandler* handler : media_access_handlers_) {
     if (handler->SupportsStreamType(content::MEDIA_DESKTOP_VIDEO_CAPTURE,
                                     nullptr) ||
@@ -430,7 +431,7 @@
       stream_type != content::MEDIA_DESKTOP_VIDEO_CAPTURE)
     return;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   for (MediaAccessHandler* handler : media_access_handlers_) {
     if (handler->SupportsStreamType(stream_type, nullptr)) {
       ToCaptureAccessHandlerBase(handler)->UpdateCapturingLinkSecured(
diff --git a/chrome/browser/media/webrtc/media_stream_capture_indicator.cc b/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
index d97d9c69..2e7fe1a 100644
--- a/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
+++ b/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
@@ -27,11 +27,12 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/browser/web_contents_observer.h"
+#include "extensions/features/features.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/image/image_skia.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/common/extensions/extension_constants.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension.h"
@@ -42,7 +43,7 @@
 
 namespace {
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 const extensions::Extension* GetExtension(WebContents* web_contents) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
@@ -69,7 +70,7 @@
 
   return false;
 }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 base::string16 GetTitle(WebContents* web_contents) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -77,7 +78,7 @@
   if (!web_contents)
     return base::string16();
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   const extensions::Extension* const extension = GetExtension(web_contents);
   if (extension)
     return base::UTF8ToUTF16(extension->name());
@@ -406,7 +407,7 @@
     // The audio/video icon is shown only for non-whitelisted extensions or on
     // Android. For regular tabs on desktop, we show an indicator in the tab
     // icon.
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     const extensions::Extension* extension = GetExtension(web_contents);
     if (!extension || IsWhitelistedExtension(extension))
       continue;
diff --git a/chrome/browser/memory_details.cc b/chrome/browser/memory_details.cc
index 48ecf06..0da07a2 100644
--- a/chrome/browser/memory_details.cc
+++ b/chrome/browser/memory_details.cc
@@ -30,13 +30,14 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/bindings_policy.h"
 #include "content/public/common/content_constants.h"
+#include "extensions/features/features.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
 #include "content/public/browser/zygote_host_linux.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/process_manager.h"
 #include "extensions/browser/process_map.h"
@@ -51,7 +52,7 @@
 using content::RenderViewHost;
 using content::RenderWidgetHost;
 using content::WebContents;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 using extensions::Extension;
 #endif
 
@@ -241,7 +242,7 @@
       render_process_host = widgets_by_pid[process.pid].front()->GetProcess();
     }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     // Determine if this is an extension process.
     bool process_is_for_extensions = false;
     if (render_process_host) {
@@ -303,7 +304,7 @@
         process.renderer_type = ProcessMemoryInformation::RENDERER_CHROME;
       }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
       if (!is_webui && process_is_for_extensions) {
         const Extension* extension =
             extensions::ExtensionRegistry::Get(
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index 746e8db..2153ae5 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -72,6 +72,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/histogram_fetcher.h"
 #include "content/public/browser/notification_service.h"
+#include "extensions/features/features.h"
 #include "printing/features/features.h"
 
 #if BUILDFLAG(ANDROID_JAVA_UI)
@@ -83,7 +84,7 @@
 #include "chrome/browser/service_process/service_process_control.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/metrics/extensions_metrics_provider.h"
 #endif
 
@@ -553,7 +554,7 @@
           new SubprocessMetricsProvider()));
 
   // Register metrics providers.
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   metrics_service_->RegisterMetricsProvider(
       std::unique_ptr<metrics::MetricsProvider>(
           new ExtensionsMetricsProvider(metrics_state_manager_)));
diff --git a/chrome/browser/metrics/chrome_stability_metrics_provider.cc b/chrome/browser/metrics/chrome_stability_metrics_provider.cc
index 4429dbe6..59171c9 100644
--- a/chrome/browser/metrics/chrome_stability_metrics_provider.cc
+++ b/chrome/browser/metrics/chrome_stability_metrics_provider.cc
@@ -14,8 +14,9 @@
 #include "content/public/browser/child_process_data.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/render_process_host.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/process_map.h"
 #endif
 
@@ -81,7 +82,7 @@
           content::Details<content::RenderProcessHost::RendererClosedDetails>(
               details).ptr();
       bool was_extension_process = false;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
       content::RenderProcessHost* host =
           content::Source<content::RenderProcessHost>(source).ptr();
       if (extensions::ProcessMap::Get(host->GetBrowserContext())
@@ -100,7 +101,7 @@
 
     case content::NOTIFICATION_RENDERER_PROCESS_CREATED: {
       bool was_extension_process = false;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
       content::RenderProcessHost* host =
           content::Source<content::RenderProcessHost>(source).ptr();
       if (extensions::ProcessMap::Get(host->GetBrowserContext())
diff --git a/chrome/browser/metrics/chrome_stability_metrics_provider_unittest.cc b/chrome/browser/metrics/chrome_stability_metrics_provider_unittest.cc
index a23da90..81032d9 100644
--- a/chrome/browser/metrics/chrome_stability_metrics_provider_unittest.cc
+++ b/chrome/browser/metrics/chrome_stability_metrics_provider_unittest.cc
@@ -22,9 +22,10 @@
 #include "content/public/common/process_type.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "extensions/features/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/process_map.h"
 #endif
 
@@ -131,7 +132,7 @@
   EXPECT_EQ(1, system_profile.stability().renderer_failed_launch_count());
   EXPECT_EQ(0, system_profile.stability().extension_renderer_crash_count());
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   provider.ClearSavedStabilityMetrics();
 
   // Owned by rph_factory.
diff --git a/chrome/browser/metrics/tab_usage_recorder.cc b/chrome/browser/metrics/tab_usage_recorder.cc
index ed9a3fdf..6840b06d 100644
--- a/chrome/browser/metrics/tab_usage_recorder.cc
+++ b/chrome/browser/metrics/tab_usage_recorder.cc
@@ -5,9 +5,8 @@
 #include "chrome/browser/metrics/tab_usage_recorder.h"
 
 #include "base/metrics/histogram_macros.h"
-#include "chrome/browser/bookmarks/bookmark_model_factory.h"
+#include "base/time/time.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "components/bookmarks/browser/bookmark_model.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "content/public/common/page_importance_signals.h"
 
@@ -17,7 +16,7 @@
 
 namespace {
 
-// This global is never freed.
+// The recorder is never stopped, thus it is ok to leak.
 TabUsageRecorder* g_tab_usage_recorder = nullptr;
 
 }  // namespace
@@ -39,15 +38,14 @@
 
   explicit WebContentsData(content::WebContents* contents);
 
-  // Returns true if |contents_|'s URL is bookmarked.
-  bool IsBookmarked();
-
   // The WebContents associated to this instance.
   content::WebContents* contents_;
 
   // Indicates if the tab is pinned to the tab strip.
   bool is_pinned_;
 
+  base::TimeTicks last_inactive_time_;
+
   DISALLOW_COPY_AND_ASSIGN(WebContentsData);
 };
 
@@ -62,29 +60,33 @@
     content::WebContents* contents)
     : contents_(contents), is_pinned_(false) {}
 
-bool TabUsageRecorder::WebContentsData::IsBookmarked() {
-  bookmarks::BookmarkModel* bookmark_model =
-      BookmarkModelFactory::GetForBrowserContextIfExists(
-          contents_->GetBrowserContext());
-
-  return bookmark_model &&
-         bookmark_model->IsBookmarked(contents_->GetLastCommittedURL());
-}
-
 void TabUsageRecorder::WebContentsData::RecordTabDeactivation() {
+  last_inactive_time_ = base::TimeTicks::Now();
   UMA_HISTOGRAM_BOOLEAN("Tab.Deactivation.Pinned", is_pinned_);
   UMA_HISTOGRAM_BOOLEAN(
       "Tab.Deactivation.HadFormInteraction",
       contents_->GetPageImportanceSignals().had_form_interaction);
-  UMA_HISTOGRAM_BOOLEAN("Tab.Deactivation.Bookmarked", IsBookmarked());
 }
 
 void TabUsageRecorder::WebContentsData::RecordTabReactivation() {
+  bool had_form_interaction =
+      contents_->GetPageImportanceSignals().had_form_interaction;
+
   UMA_HISTOGRAM_BOOLEAN("Tab.Reactivation.Pinned", is_pinned_);
-  UMA_HISTOGRAM_BOOLEAN(
-      "Tab.Reactivation.HadFormInteraction",
-      contents_->GetPageImportanceSignals().had_form_interaction);
-  UMA_HISTOGRAM_BOOLEAN("Tab.Reactivation.Bookmarked", IsBookmarked());
+  UMA_HISTOGRAM_BOOLEAN("Tab.Reactivation.HadFormInteraction",
+                        had_form_interaction);
+
+  base::TimeDelta time_to_reactivation =
+      base::TimeTicks::Now() - last_inactive_time_;
+  if (is_pinned_ || had_form_interaction) {
+    UMA_HISTOGRAM_CUSTOM_TIMES(
+        "Tab.TimeToReactivation.Important", time_to_reactivation,
+        base::TimeDelta::FromSeconds(1), base::TimeDelta::FromHours(2), 100);
+  } else {
+    UMA_HISTOGRAM_CUSTOM_TIMES(
+        "Tab.TimeToReactivation.Normal", time_to_reactivation,
+        base::TimeDelta::FromSeconds(1), base::TimeDelta::FromHours(2), 100);
+  }
 }
 
 // static
@@ -105,7 +107,7 @@
                                      content::WebContents* contents,
                                      int index,
                                      bool foreground) {
-  // Set the initial pinned value.
+  // Set the initial pin state.
   TabPinnedStateChanged(tab_strip_model, contents, index);
 }
 
diff --git a/chrome/browser/metrics/variations/chrome_variations_service_client.cc b/chrome/browser/metrics/variations/chrome_variations_service_client.cc
index 7260801..e62accb 100644
--- a/chrome/browser/metrics/variations/chrome_variations_service_client.cc
+++ b/chrome/browser/metrics/variations/chrome_variations_service_client.cc
@@ -11,6 +11,17 @@
 #include "components/version_info/version_info.h"
 #include "content/public/browser/browser_thread.h"
 
+#if defined(OS_WIN)
+#include "base/files/file_path.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/path_service.h"
+#include "base/strings/string16.h"
+#include "base/threading/thread_restrictions.h"
+#include "chrome/installer/util/google_update_settings.h"
+#include "chrome/installer/util/install_util.h"
+#include "components/variations/experiment_labels.h"
+#endif
+
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
 #include "chrome/browser/upgrade_detector_impl.h"
 #endif
@@ -36,6 +47,51 @@
   return base::Version(version_info::GetVersionNumber());
 }
 
+#if defined(OS_WIN)
+// Clear all Variations experiment labels from Google Update Registry Labels.
+// TODO(jwd): Remove this once we're confident most clients no longer have these
+// labels (M57-M58 timeframe).
+void ClearGoogleUpdateRegistryLabels() {
+  base::ThreadRestrictions::AssertIOAllowed();
+
+  // Note that all registry operations are done here on the UI thread as there
+  // are no threading restrictions on them.
+  base::FilePath chrome_exe;
+  if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
+    NOTREACHED() << "Failed to get chrome exe path";
+    return;
+  }
+  const bool is_system_install = !InstallUtil::IsPerUserInstall(chrome_exe);
+
+  // Read the current bits from the registry.
+  base::string16 registry_labels;
+  bool success = GoogleUpdateSettings::ReadExperimentLabels(is_system_install,
+                                                            &registry_labels);
+
+  if (!success) {
+    DVLOG(1) << "Error reading Variation labels from the registry.";
+    return;
+  }
+
+  // Only keep the non-Variations contents of experiment_labels.
+  const base::string16 labels_to_keep =
+      variations::ExtractNonVariationLabels(registry_labels);
+
+  // This is a weak check, which can give false positives if the implementation
+  // of variations::ExtractNonVariationLabels changes, but should be fine for
+  // temporary code.
+  bool needs_clearing = labels_to_keep != registry_labels;
+
+  UMA_HISTOGRAM_BOOLEAN("Variations.GoogleUpdateRegistryLabelsNeedClearing",
+                        needs_clearing);
+
+  if (!needs_clearing)
+    return;
+
+  GoogleUpdateSettings::SetExperimentLabels(is_system_install, labels_to_keep);
+}
+#endif  // defined(OS_WIN)
+
 }  // namespace
 
 ChromeVariationsServiceClient::ChromeVariationsServiceClient() {}
@@ -82,12 +138,12 @@
 
 void ChromeVariationsServiceClient::OnInitialStartup() {
 #if defined(OS_WIN)
-  StartGoogleUpdateRegistrySync();
+  // TODO(jwd): Remove this once we're confident most clients no longer have
+  // these labels (M57-M58 timeframe).
+  // Do the work on a blocking pool thread, as chrome://profiler has shown that
+  // it can cause jank if done on the UI thrread.
+  content::BrowserThread::GetBlockingPool()->PostDelayedTask(
+      FROM_HERE, base::Bind(&ClearGoogleUpdateRegistryLabels),
+      base::TimeDelta::FromSeconds(5));
 #endif
 }
-
-#if defined(OS_WIN)
-void ChromeVariationsServiceClient::StartGoogleUpdateRegistrySync() {
-  registry_syncer_.RequestRegistrySync();
-}
-#endif
diff --git a/chrome/browser/metrics/variations/chrome_variations_service_client.h b/chrome/browser/metrics/variations/chrome_variations_service_client.h
index e9b8890..38b7698 100644
--- a/chrome/browser/metrics/variations/chrome_variations_service_client.h
+++ b/chrome/browser/metrics/variations/chrome_variations_service_client.h
@@ -11,10 +11,6 @@
 #include "build/build_config.h"
 #include "components/variations/service/variations_service_client.h"
 
-#if defined(OS_WIN)
-#include "chrome/browser/metrics/variations/variations_registry_syncer_win.h"
-#endif
-
 // ChromeVariationsServiceClient provides an implementation of
 // VariationsServiceClient that depends on chrome/.
 class ChromeVariationsServiceClient
@@ -34,15 +30,6 @@
   bool OverridesRestrictParameter(std::string* parameter) override;
   void OnInitialStartup() override;
 
- private:
-#if defined(OS_WIN)
-  // Starts syncing Google Update Variation IDs with the registry.
-  void StartGoogleUpdateRegistrySync();
-
-  // Helper that handles synchronizing Variations with the Registry.
-  chrome_variations::VariationsRegistrySyncer registry_syncer_;
-#endif
-
   DISALLOW_COPY_AND_ASSIGN(ChromeVariationsServiceClient);
 };
 
diff --git a/chrome/browser/metrics/variations/variations_registry_syncer_win.cc b/chrome/browser/metrics/variations/variations_registry_syncer_win.cc
deleted file mode 100644
index 39262c8..0000000
--- a/chrome/browser/metrics/variations/variations_registry_syncer_win.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/metrics/variations/variations_registry_syncer_win.h"
-
-#include "base/files/file_path.h"
-#include "base/metrics/field_trial.h"
-#include "base/path_service.h"
-#include "base/strings/string16.h"
-#include "base/threading/thread_restrictions.h"
-#include "chrome/installer/util/google_update_settings.h"
-#include "chrome/installer/util/install_util.h"
-#include "components/variations/experiment_labels.h"
-#include "content/public/browser/browser_thread.h"
-
-namespace chrome_variations {
-
-namespace {
-
-// Delay before performing a registry sync, in seconds.
-const int kRegistrySyncDelaySeconds = 5;
-
-// Performs the actual synchronization process with the registry, which should
-// be done on a blocking pool thread.
-void SyncWithRegistryOnBlockingPool() {
-  base::ThreadRestrictions::AssertIOAllowed();
-
-  // Note that all registry operations are done here on the UI thread as there
-  // are no threading restrictions on them.
-  base::FilePath chrome_exe;
-  if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
-    NOTREACHED() << "Failed to get chrome exe path";
-    return;
-  }
-  const bool is_system_install = !InstallUtil::IsPerUserInstall(chrome_exe);
-
-  // Read the current bits from the registry.
-  base::string16 registry_labels;
-  bool success = GoogleUpdateSettings::ReadExperimentLabels(is_system_install,
-                                                            &registry_labels);
-  if (!success) {
-    DVLOG(1) << "Error reading Variation labels from the registry.";
-    return;
-  }
-
-  // Since the non-Variations contents of experiment_labels should not be,
-  // clobbered, separate them from the Variations contents.
-  const base::string16 other_labels =
-      variations::ExtractNonVariationLabels(registry_labels);
-
-  // Compute the new Variations part of the label.
-  base::FieldTrial::ActiveGroups active_groups;
-  base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
-  const base::string16 variation_labels =
-      variations::BuildGoogleUpdateExperimentLabel(active_groups);
-
-  // Append the old non-Variations labels with the new Variations labels and
-  // write it back to the registry.
-  const base::string16 combined_labels =
-      variations::CombineExperimentLabels(variation_labels, other_labels);
-
-  if (!GoogleUpdateSettings::SetExperimentLabels(is_system_install,
-                                                 combined_labels)) {
-    DVLOG(1) << "Error writing Variation labels to the registry: "
-             << combined_labels;
-  }
-}
-
-}  // namespace
-
-VariationsRegistrySyncer::VariationsRegistrySyncer() {
-}
-
-VariationsRegistrySyncer::~VariationsRegistrySyncer() {
-}
-
-void VariationsRegistrySyncer::RequestRegistrySync() {
-  if (timer_.IsRunning()) {
-    timer_.Reset();
-    return;
-  }
-
-  timer_.Start(FROM_HERE,
-               base::TimeDelta::FromSeconds(kRegistrySyncDelaySeconds),
-               this,
-               &VariationsRegistrySyncer::StartRegistrySync);
-}
-
-void VariationsRegistrySyncer::StartRegistrySync() {
-  // Do the work on a blocking pool thread, as chrome://profiler has shown
-  // that it can cause jank if done on the UI thrread.
-  content::BrowserThread::GetBlockingPool()->PostTask(
-      FROM_HERE, base::Bind(&SyncWithRegistryOnBlockingPool));
-}
-
-}  // namespace chrome_variations
diff --git a/chrome/browser/metrics/variations/variations_registry_syncer_win.h b/chrome/browser/metrics/variations/variations_registry_syncer_win.h
deleted file mode 100644
index 43ce6310..0000000
--- a/chrome/browser/metrics/variations/variations_registry_syncer_win.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_METRICS_VARIATIONS_VARIATIONS_REGISTRY_SYNCER_WIN_H_
-#define CHROME_BROWSER_METRICS_VARIATIONS_VARIATIONS_REGISTRY_SYNCER_WIN_H_
-
-#include "base/macros.h"
-#include "base/timer/timer.h"
-
-namespace chrome_variations {
-
-// This class manages synchronizing active VariationIDs with the Google Update
-// experiment_labels value in the registry.
-class VariationsRegistrySyncer {
- public:
-  VariationsRegistrySyncer();
-  ~VariationsRegistrySyncer();
-
-  // Starts a timer that, when it expires, updates the registry with the current
-  // Variations associated with Google Update. If the timer is already running,
-  // calling this just resets the timer.
-  void RequestRegistrySync();
-
- private:
-  // Starts the actual synchronization process with the registry. Posts a task
-  // to do it on the blocking pool to avoid jank.
-  void StartRegistrySync();
-
-  // A timer used to delay the writes to the registry. This is done to optimize
-  // the case where lazy-loaded features start their field trials some time
-  // after initial batch of field trials are created, and also to avoid blocking
-  // the UI thread. The timer effectively allows this class to batch together
-  // update requests, to avoid reading and writing from the registry too much.
-  base::OneShotTimer timer_;
-
-  DISALLOW_COPY_AND_ASSIGN(VariationsRegistrySyncer);
-};
-
-}  // namespace chrome_variations
-
-#endif  // CHROME_BROWSER_METRICS_VARIATIONS_VARIATIONS_REGISTRY_SYNCER_WIN_H_
diff --git a/chrome/browser/nacl_host/nacl_browser_delegate_impl.cc b/chrome/browser/nacl_host/nacl_browser_delegate_impl.cc
index add454f..b971070b 100644
--- a/chrome/browser/nacl_host/nacl_browser_delegate_impl.cc
+++ b/chrome/browser/nacl_host/nacl_browser_delegate_impl.cc
@@ -13,9 +13,6 @@
 #include "base/strings/string_split.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/component_updater/pnacl_component_installer.h"
-#if defined(ENABLE_EXTENSIONS)
-#include "chrome/browser/extensions/extension_service.h"
-#endif
 #include "chrome/browser/nacl_host/nacl_infobar_delegate.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -26,7 +23,11 @@
 #include "chrome/common/logging_chrome.h"
 #include "chrome/common/pepper_permission_util.h"
 #include "content/public/browser/browser_thread.h"
-#if defined(ENABLE_EXTENSIONS)
+#include "extensions/features/features.h"
+#include "url/gurl.h"
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "chrome/browser/extensions/extension_service.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/info_map.h"
 #include "extensions/browser/process_manager.h"
@@ -34,7 +35,6 @@
 #include "extensions/common/extension.h"
 #include "extensions/common/url_pattern.h"
 #endif
-#include "url/gurl.h"
 
 namespace {
 
@@ -70,7 +70,7 @@
   if (instance_data.size() < 1)
     return;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::ProcessManager::OnKeepaliveFromPlugin(
       instance_data[0].render_process_id,
       instance_data[0].render_frame_id,
@@ -146,7 +146,7 @@
 
 void NaClBrowserDelegateImpl::SetDebugPatterns(
     const std::string& debug_patterns) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (debug_patterns.empty()) {
     return;
   }
@@ -176,12 +176,12 @@
       debug_patterns_.push_back(pattern);
     }
   }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 }
 
 bool NaClBrowserDelegateImpl::URLMatchesDebugPatterns(
     const GURL& manifest_url) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Empty patterns are forbidden so we ignore them.
   if (debug_patterns_.empty()) {
     return true;
@@ -201,7 +201,7 @@
   }
 #else
   return false;
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 }
 
 // This function is security sensitive.  Be sure to check with a security
@@ -211,7 +211,7 @@
     bool use_blocking_api,
     const base::FilePath& profile_directory,
     base::FilePath* file_path) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   scoped_refptr<extensions::InfoMap> extension_info_map =
       GetExtensionInfoMap(profile_directory);
   return extension_info_map->MapUrlToLocalFilePath(
@@ -229,7 +229,7 @@
 bool NaClBrowserDelegateImpl::IsNonSfiModeAllowed(
     const base::FilePath& profile_directory,
     const GURL& manifest_url) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   const extensions::ExtensionSet* extension_set =
       &GetExtensionInfoMap(profile_directory)->extensions();
   return chrome::IsExtensionOrSharedModuleWhitelisted(
@@ -239,7 +239,7 @@
 #endif
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 scoped_refptr<extensions::InfoMap> NaClBrowserDelegateImpl::GetExtensionInfoMap(
     const base::FilePath& profile_directory) {
   // Get the profile associated with the renderer.
diff --git a/chrome/browser/nacl_host/nacl_browser_delegate_impl.h b/chrome/browser/nacl_host/nacl_browser_delegate_impl.h
index 5ca7dbb..a968d975 100644
--- a/chrome/browser/nacl_host/nacl_browser_delegate_impl.h
+++ b/chrome/browser/nacl_host/nacl_browser_delegate_impl.h
@@ -11,8 +11,9 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "components/nacl/browser/nacl_browser_delegate.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "base/memory/ref_counted.h"
 #include "extensions/common/url_pattern.h"
 
@@ -50,7 +51,7 @@
                            const GURL& manifest_url) override;
 
  private:
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   scoped_refptr<extensions::InfoMap> GetExtensionInfoMap(
       const base::FilePath& profile_directory);
   std::vector<URLPattern> debug_patterns_;
diff --git a/chrome/browser/net/chrome_extensions_network_delegate.cc b/chrome/browser/net/chrome_extensions_network_delegate.cc
index 37329af..86161ddd 100644
--- a/chrome/browser/net/chrome_extensions_network_delegate.cc
+++ b/chrome/browser/net/chrome_extensions_network_delegate.cc
@@ -7,9 +7,10 @@
 #include <stdint.h>
 
 #include "base/macros.h"
+#include "extensions/features/features.h"
 #include "net/base/net_errors.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "base/debug/alias.h"
 #include "base/debug/dump_without_crashing.h"
 #include "base/strings/string_util.h"
@@ -313,12 +314,12 @@
 
 }  // namespace
 
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 // static
 ChromeExtensionsNetworkDelegate* ChromeExtensionsNetworkDelegate::Create(
     extensions::EventRouterForwarder* event_router) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return new ChromeExtensionsNetworkDelegateImpl(event_router);
 #else
   return new ChromeExtensionsNetworkDelegate();
@@ -333,7 +334,7 @@
 
 void ChromeExtensionsNetworkDelegate::set_extension_info_map(
     extensions::InfoMap* extension_info_map) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extension_info_map_ = extension_info_map;
 #endif
 }
diff --git a/chrome/browser/net/chrome_extensions_network_delegate.h b/chrome/browser/net/chrome_extensions_network_delegate.h
index 0645e05..2b23f408 100644
--- a/chrome/browser/net/chrome_extensions_network_delegate.h
+++ b/chrome/browser/net/chrome_extensions_network_delegate.h
@@ -8,6 +8,7 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "extensions/features/features.h"
 #include "net/base/network_delegate_impl.h"
 
 namespace extensions {
@@ -77,7 +78,7 @@
 
   void* profile_;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   scoped_refptr<extensions::InfoMap> extension_info_map_;
 #endif
 
diff --git a/chrome/browser/net/chrome_network_delegate.cc b/chrome/browser/net/chrome_network_delegate.cc
index 340d903f..3543941d 100644
--- a/chrome/browser/net/chrome_network_delegate.cc
+++ b/chrome/browser/net/chrome_network_delegate.cc
@@ -48,6 +48,7 @@
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/process_type.h"
+#include "extensions/features/features.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
@@ -71,7 +72,7 @@
 #include "chrome/common/chrome_switches.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/common/constants.h"
 #endif
 
diff --git a/chrome/browser/net/chrome_network_delegate_unittest.cc b/chrome/browser/net/chrome_network_delegate_unittest.cc
index 06b6b485..4a1da852 100644
--- a/chrome/browser/net/chrome_network_delegate_unittest.cc
+++ b/chrome/browser/net/chrome_network_delegate_unittest.cc
@@ -35,6 +35,7 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/resource_type.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "extensions/features/features.h"
 #include "net/base/request_priority.h"
 #include "net/http/http_request_headers.h"
 #include "net/socket/socket_test_util.h"
@@ -43,7 +44,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/event_router_forwarder.h"
 #endif
 
@@ -144,7 +145,7 @@
   ChromeNetworkDelegateTest()
       : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
         context_(new net::TestURLRequestContext(true)) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     forwarder_ = new extensions::EventRouterForwarder();
 #endif
   }
@@ -176,7 +177,7 @@
   }
 
   extensions::EventRouterForwarder* forwarder() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     return forwarder_.get();
 #else
     return nullptr;
@@ -186,7 +187,7 @@
  private:
   std::unique_ptr<TestingProfileManager> profile_manager_;
   content::TestBrowserThreadBundle thread_bundle_;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   scoped_refptr<extensions::EventRouterForwarder> forwarder_;
 #endif
   TestingProfile profile_;
@@ -365,7 +366,7 @@
  public:
   ChromeNetworkDelegatePolicyTest()
       : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     forwarder_ = new extensions::EventRouterForwarder();
 #endif
   }
@@ -376,7 +377,7 @@
    }
 
   extensions::EventRouterForwarder* forwarder() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     return forwarder_.get();
 #else
     return nullptr;
@@ -384,7 +385,7 @@
   }
 
   content::TestBrowserThreadBundle thread_bundle_;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   scoped_refptr<extensions::EventRouterForwarder> forwarder_;
 #endif
   TestingProfile profile_;
@@ -558,7 +559,7 @@
  public:
   ChromeNetworkDelegatePrivacyModeTest()
       : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
         forwarder_(new extensions::EventRouterForwarder()),
 #endif
         cookie_settings_(CookieSettingsFactory::GetForProfile(&profile_).get()),
@@ -590,7 +591,7 @@
 
  protected:
   extensions::EventRouterForwarder* forwarder() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     return forwarder_.get();
 #else
     return NULL;
@@ -598,7 +599,7 @@
   }
 
   content::TestBrowserThreadBundle thread_bundle_;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   scoped_refptr<extensions::EventRouterForwarder> forwarder_;
 #endif
   TestingProfile profile_;
diff --git a/chrome/browser/notifications/notifier_state_tracker.cc b/chrome/browser/notifications/notifier_state_tracker.cc
index 3d0c4a4..9e3180d1 100644
--- a/chrome/browser/notifications/notifier_state_tracker.cc
+++ b/chrome/browser/notifications/notifier_state_tracker.cc
@@ -17,9 +17,10 @@
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "content/public/browser/permission_type.h"
+#include "extensions/features/features.h"
 #include "ui/message_center/notifier_settings.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/common/extensions/api/notifications.h"
 #include "content/public/browser/browser_thread.h"
 #include "extensions/browser/event_router.h"
@@ -40,7 +41,7 @@
 
 NotifierStateTracker::NotifierStateTracker(Profile* profile)
     : profile_(profile)
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
       ,
       extension_registry_observer_(this)
 #endif
@@ -69,7 +70,7 @@
           base::Unretained(prefs::kMessageCenterDisabledSystemComponentIds),
           base::Unretained(&disabled_system_component_ids_)));
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extension_registry_observer_.Add(
       extensions::ExtensionRegistry::Get(profile_));
 #endif
@@ -126,7 +127,7 @@
       pref_name = prefs::kMessageCenterDisabledExtensionIds;
       add_new_item = !enabled;
       id.reset(new base::StringValue(notifier_id.id));
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
       FirePermissionLevelChangedEvent(notifier_id, enabled);
 #endif
       break;
@@ -169,7 +170,7 @@
   }
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 void NotifierStateTracker::OnExtensionUninstalled(
     content::BrowserContext* browser_context,
     const extensions::Extension* extension,
diff --git a/chrome/browser/notifications/notifier_state_tracker.h b/chrome/browser/notifications/notifier_state_tracker.h
index 984f96b..0aa6df42 100644
--- a/chrome/browser/notifications/notifier_state_tracker.h
+++ b/chrome/browser/notifications/notifier_state_tracker.h
@@ -11,8 +11,9 @@
 #include "base/macros.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/prefs/pref_member.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "base/scoped_observer.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_registry_observer.h"
@@ -30,7 +31,7 @@
 
 // Tracks whether a given NotifierId can send notifications.
 class NotifierStateTracker : public KeyedService
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
                            , public extensions::ExtensionRegistryObserver
 #endif
                                {
@@ -53,7 +54,7 @@
   void OnStringListPrefChanged(
       const char* pref_name, std::set<std::string>* ids_field);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Fires a permission-level change event when an extension notifier has had
   // their notification permission changed.
   void FirePermissionLevelChangedEvent(
@@ -81,7 +82,7 @@
   // On-memory data for the availability of system_component.
   std::set<std::string> disabled_system_component_ids_;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // An observer to listen when extension is uninstalled.
   ScopedObserver<extensions::ExtensionRegistry,
                  extensions::ExtensionRegistryObserver>
diff --git a/chrome/browser/notifications/platform_notification_service_impl.cc b/chrome/browser/notifications/platform_notification_service_impl.cc
index aa0683af..2f79b35 100644
--- a/chrome/browser/notifications/platform_notification_service_impl.cc
+++ b/chrome/browser/notifications/platform_notification_service_impl.cc
@@ -35,6 +35,7 @@
 #include "content/public/browser/user_metrics.h"
 #include "content/public/common/notification_resources.h"
 #include "content/public/common/platform_notification_data.h"
+#include "extensions/features/features.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/message_center/notification.h"
@@ -47,7 +48,7 @@
 #include "chrome/browser/lifetime/scoped_keep_alive.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/notifications/notifier_state_tracker.h"
 #include "chrome/browser/notifications/notifier_state_tracker_factory.h"
 #include "extensions/browser/extension_registry.h"
@@ -183,7 +184,7 @@
   Profile* profile = Profile::FromBrowserContext(browser_context);
   DCHECK(profile);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Extensions support an API permission named "notification". This will grant
   // not only grant permission for using the Chrome App extension API, but also
   // for the Web Notification API.
@@ -224,7 +225,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context);
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Extensions support an API permission named "notification". This will grant
   // not only grant permission for using the Chrome App extension API, but also
   // for the Web Notification API.
@@ -484,7 +485,7 @@
 base::string16 PlatformNotificationServiceImpl::DisplayNameForContextMessage(
     Profile* profile,
     const GURL& origin) const {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // If the source is an extension, lookup the display name.
   if (origin.SchemeIs(extensions::kExtensionScheme)) {
     const extensions::Extension* extension =
diff --git a/chrome/browser/notifications/platform_notification_service_unittest.cc b/chrome/browser/notifications/platform_notification_service_unittest.cc
index 1b88754d..8ca62641 100644
--- a/chrome/browser/notifications/platform_notification_service_unittest.cc
+++ b/chrome/browser/notifications/platform_notification_service_unittest.cc
@@ -27,11 +27,12 @@
 #include "content/public/common/notification_resources.h"
 #include "content/public/common/platform_notification_data.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "extensions/features/features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/WebKit/public/platform/modules/permissions/permission_status.mojom.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "base/command_line.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/test_extension_system.h"
@@ -43,7 +44,7 @@
 #include "extensions/common/value_builder.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS) && defined(OS_CHROMEOS)
+#if BUILDFLAG(ENABLE_EXTENSIONS) && defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
@@ -316,7 +317,7 @@
   EXPECT_GT(after_persistent_notification, after_page_notification);
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 
 TEST_F(PlatformNotificationServiceTest, DisplayNameForContextMessage) {
   base::string16 display_name = service()->DisplayNameForContextMessage(
@@ -441,4 +442,4 @@
             base::UTF16ToUTF8(notification.context_message()));
 }
 
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/performance_monitor/performance_monitor.cc b/chrome/browser/performance_monitor/performance_monitor.cc
index 238e046..6c26e4a9 100644
--- a/chrome/browser/performance_monitor/performance_monitor.cc
+++ b/chrome/browser/performance_monitor/performance_monitor.cc
@@ -17,8 +17,9 @@
 #include "content/public/browser/child_process_data.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/common/content_constants.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/extension_host.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/manifest_handlers/background_info.h"
@@ -39,7 +40,7 @@
 void GatherMetricsForRenderProcess(content::RenderProcessHost* host,
                                    ProcessMetricsMetadata* data) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   content::BrowserContext* browser_context = host->GetBrowserContext();
   extensions::ProcessMap* extension_process_map =
       extensions::ProcessMap::Get(browser_context);
diff --git a/chrome/browser/plugins/chrome_content_browser_client_plugins_part.cc b/chrome/browser/plugins/chrome_content_browser_client_plugins_part.cc
index 70ffc9b..f9d68ce5 100644
--- a/chrome/browser/plugins/chrome_content_browser_client_plugins_part.cc
+++ b/chrome/browser/plugins/chrome_content_browser_client_plugins_part.cc
@@ -13,10 +13,11 @@
 #include "chrome/common/pepper_permission_util.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/render_process_host.h"
+#include "extensions/features/features.h"
 #include "ppapi/host/ppapi_host.h"
 #include "ppapi/shared_impl/ppapi_switches.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_service.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/constants.h"
@@ -44,7 +45,7 @@
         content::BrowserContext* browser_context,
         const GURL& url,
         const std::set<std::string>& allowed_file_handle_origins) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   Profile* profile = Profile::FromBrowserContext(browser_context);
   const extensions::ExtensionSet* extension_set = NULL;
   if (profile) {
@@ -67,7 +68,7 @@
     bool private_api,
     const content::SocketPermissionRequest* params,
     const std::set<std::string>& allowed_socket_origin) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   Profile* profile = Profile::FromBrowserContext(browser_context);
   const extensions::ExtensionSet* extension_set = NULL;
   if (profile) {
@@ -116,7 +117,7 @@
 bool ChromeContentBrowserClientPluginsPart::IsPepperVpnProviderAPIAllowed(
     content::BrowserContext* browser_context,
     const GURL& url) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   Profile* profile = Profile::FromBrowserContext(browser_context);
   if (!profile)
     return false;
@@ -151,7 +152,7 @@
     return true;
   }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   Profile* profile = Profile::FromBrowserContext(browser_context);
   const extensions::ExtensionSet* extension_set = NULL;
   if (profile) {
diff --git a/chrome/browser/plugins/plugin_info_message_filter.cc b/chrome/browser/plugins/plugin_info_message_filter.cc
index 10b56e7c..2fdaaea 100644
--- a/chrome/browser/plugins/plugin_info_message_filter.cc
+++ b/chrome/browser/plugins/plugin_info_message_filter.cc
@@ -41,13 +41,14 @@
 #include "content/public/browser/plugin_service.h"
 #include "content/public/browser/plugin_service_filter.h"
 #include "content/public/common/content_constants.h"
+#include "extensions/features/features.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "ppapi/features/features.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 #include "widevine_cdm_version.h"  // In SHARED_INTERMEDIATE_DIR.
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "components/guest_view/browser/guest_view_base.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
@@ -118,7 +119,7 @@
   }
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 // Returns whether a request from a plugin to load |resource| from a renderer
 // with process id |process_id| is a request for an internal resource by an app
 // listed in |accessible_resources| in its manifest.
@@ -148,7 +149,7 @@
   return renderer_state->GetOwnerInfo(process_id, nullptr, &owner_extension) &&
          owner_extension == extension_id;
 }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 }  // namespace
 
@@ -156,7 +157,7 @@
                                           Profile* profile)
     : render_process_id_(render_process_id),
       resource_context_(profile->GetResourceContext()),
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
       extension_registry_(extensions::ExtensionRegistry::Get(profile)),
 #endif
       host_content_settings_map_(HostContentSettingsMapFactory::GetForProfile(
@@ -355,7 +356,7 @@
     return;
   }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // If an app has explicitly made internal resources available by listing them
   // in |accessible_resources| in the manifest, then allow them to be loaded by
   // plugins inside a guest-view.
@@ -365,7 +366,7 @@
           extension_registry_, render_process_id_, params.url)) {
     plugin_setting = CONTENT_SETTING_ALLOW;
   }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
   if (plugin_setting == CONTENT_SETTING_DETECT_IMPORTANT_CONTENT ||
       (plugin_setting == CONTENT_SETTING_ALLOW &&
@@ -380,7 +381,7 @@
                   : ChromeViewHostMsg_GetPluginInfo_Status::kBlocked;
   }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Allow an embedder of <webview> to block a plugin from being loaded inside
   // the guest. In order to do this, set the status to 'Unauthorized' here,
   // and update the status as appropriate depending on the response from the
diff --git a/chrome/browser/plugins/plugin_info_message_filter.h b/chrome/browser/plugins/plugin_info_message_filter.h
index 3c3506b..057c7ed 100644
--- a/chrome/browser/plugins/plugin_info_message_filter.h
+++ b/chrome/browser/plugins/plugin_info_message_filter.h
@@ -18,6 +18,7 @@
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/prefs/pref_member.h"
 #include "content/public/browser/browser_message_filter.h"
+#include "extensions/features/features.h"
 #include "ppapi/features/features.h"
 
 struct ChromeViewHostMsg_GetPluginInfo_Output;
@@ -82,7 +83,7 @@
    private:
     int render_process_id_;
     content::ResourceContext* resource_context_;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     extensions::ExtensionRegistry* extension_registry_;
 #endif
     const HostContentSettingsMap* host_content_settings_map_;
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index b52e060..d81c768 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -49,6 +49,7 @@
 #include "components/sync/driver/sync_policy_handler.h"
 #include "components/translate/core/common/translate_pref_names.h"
 #include "components/variations/pref_names.h"
+#include "extensions/features/features.h"
 
 #if BUILDFLAG(ANDROID_JAVA_UI)
 #include "chrome/browser/search/contextual_search_policy_handler_android.h"
@@ -70,7 +71,7 @@
 #include "chrome/browser/download/download_dir_policy_handler.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/api/messaging/native_messaging_policy_handler.h"
 #include "chrome/browser/extensions/extension_management_constants.h"
 #include "chrome/browser/extensions/policy_handlers.h"
@@ -417,11 +418,11 @@
   { key::kFullscreenAllowed,
     prefs::kFullscreenAllowed,
     base::Value::TYPE_BOOLEAN },
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   { key::kFullscreenAllowed,
     extensions::pref_names::kAppFullscreenAllowed,
     base::Value::TYPE_BOOLEAN },
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 #endif  // !defined(OS_MACOSX)
 
 #if defined(OS_CHROMEOS)
@@ -689,7 +690,7 @@
   DISALLOW_COPY_AND_ASSIGN(ForceYouTubeSafetyModePolicyHandler);
 };
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 void GetExtensionAllowedTypesMap(
     std::vector<std::unique_ptr<StringMappingListPolicyHandler::MappingEntry>>*
         result) {
@@ -771,7 +772,7 @@
       prefs::kEnableDeprecatedWebPlatformFeatures,
       base::Bind(GetDeprecatedFeaturesMap)));
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   handlers->AddHandler(base::MakeUnique<extensions::ExtensionListPolicyHandler>(
       key::kExtensionInstallWhitelist,
       extensions::pref_names::kInstallAllowList, false));
diff --git a/chrome/browser/policy/managed_bookmarks_policy_handler_unittest.cc b/chrome/browser/policy/managed_bookmarks_policy_handler_unittest.cc
index 9bf6384b..bba50c8 100644
--- a/chrome/browser/policy/managed_bookmarks_policy_handler_unittest.cc
+++ b/chrome/browser/policy/managed_bookmarks_policy_handler_unittest.cc
@@ -15,8 +15,9 @@
 #include "components/policy/core/common/policy_types.h"
 #include "components/policy/core/common/schema.h"
 #include "components/policy/policy_constants.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/common/value_builder.h"
 #endif
 
@@ -31,7 +32,7 @@
   }
 };
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 TEST_F(ManagedBookmarksPolicyHandlerTest, ApplyPolicySettings) {
   EXPECT_FALSE(store_->GetValue(bookmarks::prefs::kManagedBookmarks, NULL));
 
@@ -135,9 +136,9 @@
           .Build());
   EXPECT_TRUE(pref_value->Equals(expected.get()));
 }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 TEST_F(ManagedBookmarksPolicyHandlerTest, ApplyPolicySettingsNoTitle) {
   EXPECT_FALSE(store_->GetValue(bookmarks::prefs::kManagedBookmarks, NULL));
 
@@ -175,7 +176,7 @@
           .Build());
   EXPECT_TRUE(pref_value->Equals(expected.get()));
 }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 TEST_F(ManagedBookmarksPolicyHandlerTest, WrongPolicyType) {
   PolicyMap policy;
@@ -194,7 +195,7 @@
   EXPECT_FALSE(store_->GetValue(bookmarks::prefs::kManagedBookmarks, NULL));
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 TEST_F(ManagedBookmarksPolicyHandlerTest, UnknownKeys) {
   PolicyMap policy;
   policy.Set(key::kManagedBookmarks, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
@@ -224,7 +225,7 @@
 }
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 TEST_F(ManagedBookmarksPolicyHandlerTest, BadBookmark) {
   PolicyMap policy;
   policy.Set(key::kManagedBookmarks, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 6af2627a..5ab02098 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -106,6 +106,7 @@
 #include "components/variations/service/variations_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
+#include "extensions/features/features.h"
 #include "net/http/http_server_properties_manager.h"
 #include "printing/features/features.h"
 
@@ -118,7 +119,7 @@
 #include "chrome/browser/background/background_mode_manager.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/accessibility/animation_policy_prefs.h"
 #include "chrome/browser/apps/shortcut_manager.h"
 #include "chrome/browser/extensions/activity_log/activity_log.h"
@@ -132,7 +133,7 @@
 #include "chrome/browser/ui/webui/extensions/extension_settings_handler.h"
 #include "extensions/browser/api/runtime/runtime_api.h"
 #include "extensions/browser/extension_prefs.h"
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 #if BUILDFLAG(ENABLE_PLUGIN_INSTALLATION)
 #include "chrome/browser/plugins/plugins_resource_service.h"
@@ -371,7 +372,7 @@
   policy::BrowserPolicyConnector::RegisterPrefs(registry);
   policy::PolicyStatisticsCollector::RegisterPrefs(registry);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   EasyUnlockService::RegisterPrefs(registry);
 #endif
 
@@ -513,7 +514,7 @@
   policy::URLBlacklistManager::RegisterProfilePrefs(registry);
   certificate_transparency::CTPolicyManager::RegisterPrefs(registry);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   EasyUnlockService::RegisterProfilePrefs(registry);
   ExtensionWebUI::RegisterProfilePrefs(registry);
   RegisterAnimationPolicyPrefs(registry);
@@ -523,13 +524,13 @@
   extensions::ExtensionPrefs::RegisterProfilePrefs(registry);
   extensions::launch_util::RegisterProfilePrefs(registry);
   extensions::RuntimeAPI::RegisterPrefs(registry);
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 #if defined(ENABLE_NOTIFICATIONS)
   NotifierStateTracker::RegisterProfilePrefs(registry);
 #endif
 
-#if defined(ENABLE_NOTIFICATIONS) && defined(ENABLE_EXTENSIONS) && \
+#if defined(ENABLE_NOTIFICATIONS) && BUILDFLAG(ENABLE_EXTENSIONS) && \
     !defined(OS_ANDROID)
   // The extension welcome notification requires a build that enables extensions
   // and notifications, and uses the UI message center.
diff --git a/chrome/browser/prefs/chrome_pref_service_factory.cc b/chrome/browser/prefs/chrome_pref_service_factory.cc
index fe58fc3..0c761e6 100644
--- a/chrome/browser/prefs/chrome_pref_service_factory.cc
+++ b/chrome/browser/prefs/chrome_pref_service_factory.cc
@@ -58,10 +58,11 @@
 #include "components/user_prefs/tracked/pref_names.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
+#include "extensions/features/features.h"
 #include "sql/error_delegate_util.h"
 #include "ui/base/resource/resource_bundle.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/pref_names.h"
 #endif
 
@@ -125,7 +126,7 @@
     PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
     PrefHashFilter::VALUE_IMPERSONAL
   },
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   {
     5, extensions::pref_names::kExtensions,
     PrefHashFilter::NO_ENFORCEMENT,
@@ -341,7 +342,7 @@
       data.enforcement_level = PrefHashFilter::ENFORCE_ON_LOAD;
     }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     if (enforcement_group >= GROUP_ENFORCE_ALWAYS_WITH_EXTENSIONS_AND_DSE &&
         data.name == extensions::pref_names::kExtensions) {
       // Specifically enable extension settings enforcement.
diff --git a/chrome/browser/prerender/prerender_link_manager.cc b/chrome/browser/prerender/prerender_link_manager.cc
index f3409e0..1f4bc51 100644
--- a/chrome/browser/prerender/prerender_link_manager.cc
+++ b/chrome/browser/prerender/prerender_link_manager.cc
@@ -23,10 +23,11 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/session_storage_namespace.h"
 #include "content/public/common/referrer.h"
+#include "extensions/features/features.h"
 #include "ui/gfx/geometry/size.h"
 #include "url/gurl.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "components/guest_view/browser/guest_view_base.h"
 #endif
 
@@ -169,7 +170,7 @@
             FindByLauncherChildIdAndPrerenderId(launcher_child_id,
                                                 prerender_id));
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   content::RenderViewHost* rvh =
       content::RenderViewHost::FromID(launcher_child_id, render_view_route_id);
   content::WebContents* web_contents =
diff --git a/chrome/browser/prerender/prerender_manager_factory.cc b/chrome/browser/prerender/prerender_manager_factory.cc
index 895e4f4..5466972f 100644
--- a/chrome/browser/prerender/prerender_manager_factory.cc
+++ b/chrome/browser/prerender/prerender_manager_factory.cc
@@ -12,8 +12,9 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/extension_system_provider.h"
 #include "extensions/browser/extensions_browser_client.h"
 #endif
@@ -39,7 +40,7 @@
     : BrowserContextKeyedServiceFactory(
         "PrerenderManager",
         BrowserContextDependencyManager::GetInstance()) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   DependsOn(
       extensions::ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
 #endif
diff --git a/chrome/browser/printing/print_view_manager_common.cc b/chrome/browser/printing/print_view_manager_common.cc
index b4b3603a..83a94f7 100644
--- a/chrome/browser/printing/print_view_manager_common.cc
+++ b/chrome/browser/printing/print_view_manager_common.cc
@@ -4,12 +4,13 @@
 
 #include "chrome/browser/printing/print_view_manager_common.h"
 
+#include "extensions/features/features.h"
 #include "printing/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "components/guest_view/browser/guest_view_manager.h"
 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
 #include "chrome/browser/printing/print_view_manager.h"
@@ -19,7 +20,7 @@
 
 namespace printing {
 namespace {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 // Stores |guest_contents| in |result| and returns true if |guest_contents| is a
 // full page MimeHandlerViewGuest plugin. Otherwise, returns false.
 bool StoreFullPagePlugin(content::WebContents** result,
@@ -32,12 +33,12 @@
   }
   return false;
 }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 // If we have a single full-page embedded mime handler view guest, print the
 // guest's WebContents instead.
 content::WebContents* GetWebContentsToUse(content::WebContents* contents) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   guest_view::GuestViewManager* guest_view_manager =
       guest_view::GuestViewManager::FromBrowserContext(
           contents->GetBrowserContext());
@@ -46,7 +47,7 @@
         contents,
         base::Bind(&StoreFullPagePlugin, &contents));
   }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
   return contents;
 }
 
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index 2eaf0ad..2dcb1d2 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -70,9 +70,10 @@
 #include "chrome/browser/undo/bookmark_undo_service_factory.h"
 #include "chrome/browser/web_data_service_factory.h"
 #include "chrome/common/features.h"
+#include "extensions/features/features.h"
 #include "printing/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "apps/browser_context_keyed_service_factories.h"
 #include "chrome/browser/apps/shortcut_manager_factory.h"
 #include "chrome/browser/extensions/api/networking_private/networking_private_ui_delegate_factory_impl.h"
@@ -174,7 +175,7 @@
 // static
 void ChromeBrowserMainExtraPartsProfiles::
 EnsureBrowserContextKeyedServiceFactoriesBuilt() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   apps::EnsureBrowserContextKeyedServiceFactoriesBuilt();
   extensions::EnsureBrowserContextKeyedServiceFactoriesBuilt();
   extensions::ExtensionManagementFactory::GetInstance();
@@ -212,7 +213,7 @@
   CrossDevicePromoFactory::GetInstance();
 #endif
 #if defined(ENABLE_NOTIFICATIONS)
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   ExtensionWelcomeNotificationFactory::GetInstance();
 #endif
   NotifierStateTrackerFactory::GetInstance();
@@ -221,7 +222,7 @@
   dom_distiller::DomDistillerServiceFactory::GetInstance();
   domain_reliability::DomainReliabilityServiceFactory::GetInstance();
   DownloadServiceFactory::GetInstance();
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   EasyUnlockServiceFactory::GetInstance();
   EnhancedBookmarkKeyServiceFactory::GetInstance();
 #endif
@@ -238,7 +239,7 @@
 #endif
   GoogleURLTrackerFactory::GetInstance();
   HistoryServiceFactory::GetInstance();
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   HotwordServiceFactory::GetInstance();
 #endif
   HostContentSettingsMapFactory::GetInstance();
@@ -258,7 +259,7 @@
   SupervisedUserSyncServiceFactory::GetInstance();
 #endif
 #endif
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #if defined(OS_CHROMEOS) || defined(OS_WIN) || defined(OS_MACOSX)
   std::unique_ptr<extensions::NetworkingPrivateVerifyDelegateFactoryImpl>
       networking_private_verify_delegate_factory(
diff --git a/chrome/browser/profiles/off_the_record_profile_impl.cc b/chrome/browser/profiles/off_the_record_profile_impl.cc
index 2d8ce6df..c6af133 100644
--- a/chrome/browser/profiles/off_the_record_profile_impl.cc
+++ b/chrome/browser/profiles/off_the_record_profile_impl.cc
@@ -56,6 +56,7 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/url_data_source.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/features/features.h"
 #include "net/http/http_server_properties.h"
 #include "net/http/transport_security_state.h"
 #include "storage/browser/database/database_tracker.h"
@@ -74,7 +75,7 @@
 #include "chrome/browser/policy/cloud/user_cloud_policy_manager_factory.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_special_storage_policy.h"
 #include "components/guest_view/browser/guest_view_manager.h"
@@ -93,7 +94,7 @@
 using content::DownloadManagerDelegate;
 using content::HostZoomMap;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 namespace {
 
 void NotifyOTRProfileCreatedOnIOThread(void* original_profile,
@@ -153,7 +154,7 @@
       this, io_data_->GetResourceContextNoInit());
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Make the chrome//extension-icon/ resource available.
   extensions::ExtensionIconSource* icon_source =
       new extensions::ExtensionIconSource(profile_);
@@ -180,7 +181,7 @@
   BrowserContextDependencyManager::GetInstance()->DestroyBrowserContextServices(
       this);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
       base::Bind(&NotifyOTRProfileDestroyedOnIOThread, profile_, this));
@@ -365,7 +366,7 @@
 }
 
 content::BrowserPluginGuestManager* OffTheRecordProfileImpl::GetGuestManager() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return guest_view::GuestViewManager::FromBrowserContext(this);
 #else
   return NULL;
@@ -374,7 +375,7 @@
 
 storage::SpecialStoragePolicy*
 OffTheRecordProfileImpl::GetSpecialStoragePolicy() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return GetExtensionSpecialStoragePolicy();
 #else
   return NULL;
diff --git a/chrome/browser/profiles/off_the_record_profile_io_data.cc b/chrome/browser/profiles/off_the_record_profile_io_data.cc
index b202b54e..e76199dd 100644
--- a/chrome/browser/profiles/off_the_record_profile_io_data.cc
+++ b/chrome/browser/profiles/off_the_record_profile_io_data.cc
@@ -30,6 +30,7 @@
 #include "content/public/browser/cookie_store_factory.h"
 #include "content/public/browser/resource_context.h"
 #include "extensions/common/constants.h"
+#include "extensions/features/features.h"
 #include "net/base/sdch_manager.h"
 #include "net/http/http_cache.h"
 #include "net/http/http_network_session.h"
@@ -41,7 +42,7 @@
 #include "net/url_request/url_request_job_factory_impl.h"
 #include "storage/browser/database/database_tracker.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/common/extension.h"
 #endif
 
@@ -259,7 +260,7 @@
   sdch_policy_.reset(
       new net::SdchOwner(main_context->sdch_manager(), main_context));
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   InitializeExtensionsRequestContext(profile_params);
 #endif
 }
diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc
index 281e922c..e7979cce 100644
--- a/chrome/browser/profiles/profile.cc
+++ b/chrome/browser/profiles/profile.cc
@@ -25,6 +25,7 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
+#include "extensions/features/features.h"
 
 #if defined(OS_CHROMEOS)
 #include "base/command_line.h"
@@ -32,7 +33,7 @@
 #include "chromeos/chromeos_switches.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/pref_names.h"
 #endif
 
@@ -110,7 +111,7 @@
   // TODO(skare): Remove or rename ENABLE_GOOGLE_NOW: http://crbug.com/459827.
   registry->RegisterBooleanPref(prefs::kGoogleNowLauncherEnabled, true);
   registry->RegisterBooleanPref(prefs::kDisableExtensions, false);
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   registry->RegisterBooleanPref(extensions::pref_names::kAlertsInitialized,
                                 false);
 #endif
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index 62571b8..7747795 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -114,6 +114,7 @@
 #include "content/public/browser/user_metrics.h"
 #include "content/public/common/content_constants.h"
 #include "content/public/common/page_zoom.h"
+#include "extensions/features/features.h"
 #include "printing/features/features.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -137,7 +138,7 @@
 #include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_special_storage_policy.h"
 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
@@ -271,7 +272,7 @@
 
 PrefStore* CreateExtensionPrefStore(Profile* profile,
                                     bool incognito_pref_store) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return new ExtensionPrefStore(
       ExtensionPrefValueMapFactory::GetForBrowserContext(profile),
       incognito_pref_store);
@@ -697,7 +698,7 @@
     ProfileDestroyer::DestroyOffTheRecordProfileNow(
         off_the_record_profile_.get());
   } else {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     ExtensionPrefValueMapFactory::GetForBrowserContext(this)->
         ClearAllIncognitoSessionOnlyPreferences();
 #endif
@@ -767,7 +768,7 @@
 void ProfileImpl::DestroyOffTheRecordProfile() {
   off_the_record_profile_.reset();
   otr_prefs_->ClearMutableValues();
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   ExtensionPrefValueMapFactory::GetForBrowserContext(this)->
       ClearAllIncognitoSessionOnlyPreferences();
 #endif
@@ -800,7 +801,7 @@
 
 ExtensionSpecialStoragePolicy*
     ProfileImpl::GetExtensionSpecialStoragePolicy() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (!extension_special_storage_policy_.get()) {
     TRACE_EVENT0("browser", "ProfileImpl::GetExtensionSpecialStoragePolicy")
     extension_special_storage_policy_ = new ExtensionSpecialStoragePolicy(
@@ -953,7 +954,7 @@
 }
 
 content::BrowserPluginGuestManager* ProfileImpl::GetGuestManager() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return guest_view::GuestViewManager::FromBrowserContext(this);
 #else
   return NULL;
@@ -966,7 +967,7 @@
 }
 
 storage::SpecialStoragePolicy* ProfileImpl::GetSpecialStoragePolicy() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return GetExtensionSpecialStoragePolicy();
 #else
   return NULL;
diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h
index 54c74357..f945b08 100644
--- a/chrome/browser/profiles/profile_impl.h
+++ b/chrome/browser/profiles/profile_impl.h
@@ -23,6 +23,7 @@
 #include "components/prefs/pref_change_registrar.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/host_zoom_map.h"
+#include "extensions/features/features.h"
 
 class PrefService;
 
@@ -226,7 +227,7 @@
   std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs_;
   std::unique_ptr<sync_preferences::PrefServiceSyncable> otr_prefs_;
   ProfileImplIOData::Handle io_data_;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   scoped_refptr<ExtensionSpecialStoragePolicy>
       extension_special_storage_policy_;
 #endif
diff --git a/chrome/browser/profiles/profile_impl_io_data.cc b/chrome/browser/profiles/profile_impl_io_data.cc
index 63a2cbd..a8d9d72 100644
--- a/chrome/browser/profiles/profile_impl_io_data.cc
+++ b/chrome/browser/profiles/profile_impl_io_data.cc
@@ -60,6 +60,7 @@
 #include "content/public/browser/storage_partition.h"
 #include "extensions/browser/extension_protocols.h"
 #include "extensions/common/constants.h"
+#include "extensions/features/features.h"
 #include "net/base/cache_type.h"
 #include "net/base/sdch_manager.h"
 #include "net/cookies/cookie_store.h"
@@ -556,7 +557,7 @@
   main_context->set_network_quality_estimator(
       io_thread_globals->network_quality_estimator.get());
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   InitializeExtensionsRequestContext(profile_params);
 #endif
 
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index 1351f9c..298404ce 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -82,6 +82,7 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/resource_context.h"
 #include "content/public/common/content_switches.h"
+#include "extensions/features/features.h"
 #include "net/base/keygen_handler.h"
 #include "net/cert/cert_verifier.h"
 #include "net/cert/ct_log_verifier.h"
@@ -112,7 +113,7 @@
 #include "net/url_request/url_request_job_factory_impl.h"
 #include "third_party/WebKit/public/public_features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_cookie_monster_delegate.h"
 #include "chrome/browser/extensions/extension_resource_protocols.h"
 #include "extensions/browser/extension_protocols.h"
@@ -402,7 +403,7 @@
       HostContentSettingsMapFactory::GetForProfile(profile);
   params->ssl_config_service = profile->GetSSLConfigService();
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   params->extension_info_map =
       extensions::ExtensionSystem::Get(profile)->info_map();
   params->cookie_monster_delegate = new ExtensionCookieMonsterDelegate(profile);
@@ -731,7 +732,7 @@
     url::kFileScheme,
     content::kChromeDevToolsScheme,
     dom_distiller::kDomDistillerScheme,
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     extensions::kExtensionScheme,
     extensions::kExtensionResourceScheme,
 #endif
@@ -851,7 +852,7 @@
 
 extensions::InfoMap* ProfileIOData::GetExtensionInfoMap() const {
   DCHECK(initialized_) << "ExtensionSystem not initialized";
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return extension_info_map_.get();
 #else
   return nullptr;
@@ -861,7 +862,7 @@
 extensions::ExtensionThrottleManager*
 ProfileIOData::GetExtensionThrottleManager() const {
   DCHECK(initialized_) << "ExtensionSystem not initialized";
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return extension_throttle_manager_.get();
 #else
   return nullptr;
@@ -1027,13 +1028,13 @@
 
   std::unique_ptr<ChromeNetworkDelegate> network_delegate(
       new ChromeNetworkDelegate(
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
           io_thread_globals->extension_event_router_forwarder.get(),
 #else
           NULL,
 #endif
           &enable_referrers_, io_thread->GetMetricsDataUseForwarder()));
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   network_delegate->set_extension_info_map(
       profile_params_->extension_info_map.get());
   if (!command_line.HasSwitch(switches::kDisableExtensionsHttpThrottling)) {
@@ -1087,7 +1088,7 @@
   // Take ownership over these parameters.
   cookie_settings_ = profile_params_->cookie_settings;
   host_content_settings_map_ = profile_params_->host_content_settings_map;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extension_info_map_ = profile_params_->extension_info_map;
 #endif
 
@@ -1189,7 +1190,7 @@
                   base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)));
   DCHECK(set_protocol);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   DCHECK(extension_info_map_.get());
   // Check only for incognito (and not Chrome OS guest mode GUEST_PROFILE).
   bool is_incognito = profile_type() == Profile::INCOGNITO_PROFILE;
diff --git a/chrome/browser/profiles/profile_io_data.h b/chrome/browser/profiles/profile_io_data.h
index 33e60111..016ed49 100644
--- a/chrome/browser/profiles/profile_io_data.h
+++ b/chrome/browser/profiles/profile_io_data.h
@@ -30,6 +30,7 @@
 #include "components/prefs/pref_member.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/resource_context.h"
+#include "extensions/features/features.h"
 #include "net/cookies/cookie_monster.h"
 #include "net/http/http_cache.h"
 #include "net/http/http_network_session.h"
@@ -319,7 +320,7 @@
     scoped_refptr<HostContentSettingsMap> host_content_settings_map;
     scoped_refptr<net::SSLConfigService> ssl_config_service;
     scoped_refptr<net::CookieMonsterDelegate> cookie_monster_delegate;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     scoped_refptr<extensions::InfoMap> extension_info_map;
 #endif
     std::unique_ptr<chrome_browser_net::ResourcePrefetchPredictorObserver>
@@ -561,7 +562,7 @@
   mutable std::unique_ptr<policy::PolicyHeaderIOHelper> policy_header_helper_;
 
   // Pointed to by URLRequestContext.
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   mutable scoped_refptr<extensions::InfoMap> extension_info_map_;
 #endif
 
@@ -620,7 +621,7 @@
       supervised_user_url_filter_;
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Is NULL if switches::kDisableExtensionsHttpThrottling is on.
   mutable std::unique_ptr<extensions::ExtensionThrottleManager>
       extension_throttle_manager_;
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index bda2efe..3a69b5e04 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -87,13 +87,14 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/user_metrics.h"
 #include "content/public/common/content_switches.h"
+#include "extensions/features/features.h"
 #include "net/http/http_transaction_factory.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.h"
 #include "ui/base/l10n/l10n_util.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_service.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
@@ -265,7 +266,7 @@
 
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 
 // Returns the number of installed (and enabled) apps, excluding any component
 // apps.
@@ -1126,7 +1127,7 @@
                                             bool go_off_the_record) {
   TRACE_EVENT0("browser", "ProfileManager::DoFinalInitForServices");
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Ensure that the HostContentSettingsMap has been created before the
   // ExtensionSystem is initialized otherwise the ExtensionSystem will be
   // registered twice
@@ -1209,7 +1210,7 @@
   TRACE_EVENT0("browser", "ProfileManager::DoFinalInitLogging");
   // Count number of extensions in this profile.
   int enabled_app_count = -1;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   enabled_app_count = GetEnabledAppCount(profile);
 #endif
 
diff --git a/chrome/browser/profiles/profile_window.cc b/chrome/browser/profiles/profile_window.cc
index a7c0264..37093b9 100644
--- a/chrome/browser/profiles/profile_window.cc
+++ b/chrome/browser/profiles/profile_window.cc
@@ -49,15 +49,16 @@
 #include "components/signin/core/common/signin_switches.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/user_metrics.h"
+#include "extensions/features/features.h"
 #include "net/base/escape.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_service.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_registry_factory.h"
 #include "extensions/browser/extension_system.h"
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 #if !defined(OS_ANDROID)
 #include "chrome/browser/ui/browser_finder.h"
@@ -72,7 +73,7 @@
 
 namespace {
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 void BlockExtensions(Profile* profile) {
   ExtensionService* extension_service =
       extensions::ExtensionSystem::Get(profile)->extension_service();
@@ -84,7 +85,7 @@
       extensions::ExtensionSystem::Get(profile)->extension_service();
   extension_service->UnblockAllExtensions();
 }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 // Handles running a callback when a new Browser for the given profile
 // has been completely created.
@@ -248,7 +249,7 @@
     is_first_run = chrome::startup::IS_FIRST_RUN;
   }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // The signin bit will still be set if the profile is being unlocked and the
   // browser window for it is opening. As part of this unlock process, unblock
   // all the extensions.
@@ -260,7 +261,7 @@
       UnblockExtensions(profile);
     }
   }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
   // If |always_create| is false, and we have a |callback| to run, check
   // whether a browser already exists so that we can run the callback. We don't
@@ -384,10 +385,10 @@
   DCHECK(has_entry);
   entry->SetIsSigninRequired(true);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Profile guaranteed to exist for it to have been locked.
   BlockExtensions(profile_manager->GetProfileByPath(profile_path));
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
   chrome::HideTaskManager();
   UserManager::Show(profile_path,
diff --git a/chrome/browser/renderer_context_menu/context_menu_content_type_factory.cc b/chrome/browser/renderer_context_menu/context_menu_content_type_factory.cc
index 351ff01..f60f25c 100644
--- a/chrome/browser/renderer_context_menu/context_menu_content_type_factory.cc
+++ b/chrome/browser/renderer_context_menu/context_menu_content_type_factory.cc
@@ -9,8 +9,9 @@
 #include "components/renderer_context_menu/context_menu_content_type.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/url_constants.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/app_mode/app_mode_utils.h"
 #include "chrome/browser/guest_view/web_view/context_menu_content_type_web_view.h"
 #include "chrome/browser/renderer_context_menu/context_menu_content_type_app_mode.h"
@@ -57,7 +58,7 @@
 ContextMenuContentType* ContextMenuContentTypeFactory::CreateInternal(
     content::WebContents* web_contents,
     const content::ContextMenuParams& params) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (extensions::WebViewGuest::FromWebContents(web_contents))
     return new ContextMenuContentTypeWebView(web_contents, params);
 
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index d905548..a479ce1 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -104,6 +104,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/menu_item.h"
 #include "content/public/common/url_utils.h"
+#include "extensions/features/features.h"
 #include "net/base/escape.h"
 #include "printing/features/features.h"
 #include "third_party/WebKit/public/public_features.h"
@@ -124,7 +125,7 @@
 #include "chrome/browser/renderer_context_menu/spelling_options_submenu_observer.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/devtools_util.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "extensions/browser/extension_host.h"
@@ -407,7 +408,7 @@
 }
 
 content::WebContents* GetWebContentsToUse(content::WebContents* web_contents) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // If we're viewing in a MimeHandlerViewGuest, use its embedder WebContents.
   if (extensions::MimeHandlerViewGuest::FromWebContents(web_contents)) {
     WebContents* top_level_web_contents =
@@ -502,7 +503,7 @@
 gfx::Vector2d RenderViewContextMenu::GetOffset(
     RenderFrameHost* render_frame_host) {
   gfx::Vector2d offset;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // When --use-cross-process-frames-for-guests is enabled, the position is
   // transformed in the browser process hittesting code.
   WebContents* web_contents =
@@ -520,7 +521,7 @@
       offset = bounds.origin() - top_level_bounds.origin();
     }
   }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
   return offset;
 }
 
@@ -590,7 +591,7 @@
 
 // Menu construction functions -------------------------------------------------
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 // static
 bool RenderViewContextMenu::ExtensionContextAndPatternMatch(
     const content::ContextMenuParams& params,
@@ -738,7 +739,7 @@
   extension_items_.AppendExtensionItems(key, PrintableSelectionText(), &index,
                                         false /* is_action_menu */);
 }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 void RenderViewContextMenu::InitMenu() {
   RenderViewContextMenuBase::InitMenu();
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.h b/chrome/browser/renderer_context_menu/render_view_context_menu.h
index 731dde5c..302cf17 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.h
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.h
@@ -20,12 +20,13 @@
 #include "components/renderer_context_menu/render_view_context_menu_observer.h"
 #include "components/renderer_context_menu/render_view_context_menu_proxy.h"
 #include "content/public/common/context_menu_params.h"
+#include "extensions/features/features.h"
 #include "printing/features/features.h"
 #include "ui/base/models/simple_menu_model.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/gfx/geometry/vector2d.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/context_menu_matcher.h"
 #include "chrome/browser/extensions/menu_manager.h"
 #endif
@@ -89,7 +90,7 @@
   // Helper function to escape "&" as "&&".
   void EscapeAmpersands(base::string16* text);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::ContextMenuMatcher extension_items_;
 #endif
   void RecordUsedItem(int id) override;
@@ -104,7 +105,7 @@
 
   static bool IsDevToolsURL(const GURL& url);
   static bool IsInternalResourcesURL(const GURL& url);
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   static bool ExtensionContextAndPatternMatch(
       const content::ContextMenuParams& params,
       const extensions::MenuItem::ContextList& contexts,
@@ -146,7 +147,7 @@
   void AppendLanguageSettings();
   void AppendSpellingSuggestionItems();
   void AppendSearchProvider();
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   void AppendAllExtensionItems();
   void AppendCurrentExtensionItems();
 #endif
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h b/chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h
index 05ceabe..e84a66e 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h
@@ -10,9 +10,10 @@
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
+#include "extensions/features/features.h"
 #include "url/gurl.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/context_menu_matcher.h"
 #endif
 
@@ -60,7 +61,7 @@
   // Returns the command id of the menu item with the specified |path|.
   int GetCommandIDByProfilePath(const base::FilePath& path);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::ContextMenuMatcher& extension_items() { return extension_items_; }
 #endif
 
diff --git a/chrome/browser/renderer_host/chrome_navigation_ui_data.cc b/chrome/browser/renderer_host/chrome_navigation_ui_data.cc
index cffc26b6..4bec1bc 100644
--- a/chrome/browser/renderer_host/chrome_navigation_ui_data.cc
+++ b/chrome/browser/renderer_host/chrome_navigation_ui_data.cc
@@ -7,12 +7,13 @@
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/sessions/session_tab_helper.h"
 #include "content/public/browser/navigation_handle.h"
+#include "extensions/features/features.h"
 
 ChromeNavigationUIData::ChromeNavigationUIData() {}
 
 ChromeNavigationUIData::ChromeNavigationUIData(
     content::NavigationHandle* navigation_handle) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   SessionTabHelper* session_tab_helper =
       SessionTabHelper::FromWebContents(navigation_handle->GetWebContents());
   int tab_id = session_tab_helper ? session_tab_helper->session_id().id() : -1;
@@ -30,7 +31,7 @@
   std::unique_ptr<ChromeNavigationUIData> copy =
       base::MakeUnique<ChromeNavigationUIData>();
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (extension_data_)
     copy->SetExtensionNavigationUIData(extension_data_->DeepCopy());
 #endif
@@ -38,7 +39,7 @@
   return std::move(copy);
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 void ChromeNavigationUIData::SetExtensionNavigationUIData(
     std::unique_ptr<extensions::ExtensionNavigationUIData> extension_data) {
   extension_data_ = std::move(extension_data);
diff --git a/chrome/browser/renderer_host/chrome_navigation_ui_data.h b/chrome/browser/renderer_host/chrome_navigation_ui_data.h
index 4f441e0..2a9d8cb 100644
--- a/chrome/browser/renderer_host/chrome_navigation_ui_data.h
+++ b/chrome/browser/renderer_host/chrome_navigation_ui_data.h
@@ -10,6 +10,7 @@
 #include "base/macros.h"
 #include "content/public/browser/navigation_ui_data.h"
 #include "extensions/browser/extension_navigation_ui_data.h"
+#include "extensions/features/features.h"
 
 namespace content {
 class NavigationHandle;
@@ -31,7 +32,7 @@
   // reflected in the clone.  |extension_data_| is deep copied.
   std::unique_ptr<content::NavigationUIData> Clone() const override;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   void SetExtensionNavigationUIData(
       std::unique_ptr<extensions::ExtensionNavigationUIData> extension_data);
 
@@ -41,7 +42,7 @@
 #endif
 
  private:
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Manages the lifetime of optional ExtensionNavigationUIData information.
   std::unique_ptr<extensions::ExtensionNavigationUIData> extension_data_;
 #endif
diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.cc b/chrome/browser/renderer_host/chrome_render_message_filter.cc
index 44b6ad0..14d90e8 100644
--- a/chrome/browser/renderer_host/chrome_render_message_filter.cc
+++ b/chrome/browser/renderer_host/chrome_render_message_filter.cc
@@ -29,8 +29,9 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/service_worker_context.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/guest_view/web_view/web_view_permission_helper.h"
 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
 #include "extensions/common/manifest_handlers/default_locale_handler.h"
@@ -208,7 +209,7 @@
   Send(reply_msg);
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 void ChromeRenderMessageFilter::FileSystemAccessedSyncOnUIThread(
     int render_process_id,
     int render_frame_id,
@@ -264,7 +265,7 @@
   bool allowed =
       cookie_settings_->IsSettingCookieAllowed(origin_url, top_origin_url);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   bool is_web_view_guest = extensions::WebViewRendererState::GetInstance()
       ->IsGuest(render_process_id_);
   if (is_web_view_guest) {
@@ -293,7 +294,7 @@
                  !allowed));
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 void ChromeRenderMessageFilter::FileSystemAccessedOnUIThread(
     int render_process_id,
     int render_frame_id,
diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.h b/chrome/browser/renderer_host/chrome_render_message_filter.h
index 171e6c8..afb9538a 100644
--- a/chrome/browser/renderer_host/chrome_render_message_filter.h
+++ b/chrome/browser/renderer_host/chrome_render_message_filter.h
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "base/sequenced_task_runner_helpers.h"
 #include "content/public/browser/browser_message_filter.h"
+#include "extensions/features/features.h"
 
 class GURL;
 class Profile;
@@ -84,7 +85,7 @@
                                      const GURL& origin_url,
                                      const GURL& top_origin_url,
                                      IPC::Message* message);
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   static void FileSystemAccessedSyncOnUIThread(
       int render_process_id,
       int render_frame_id,
@@ -105,7 +106,7 @@
                                  const GURL& origin_url,
                                  const GURL& top_origin_url,
                                  base::Callback<void(bool)> callback);
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   static void FileSystemAccessedOnUIThread(int render_process_id,
                                            int render_frame_id,
                                            const GURL& url,
diff --git a/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc b/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc
index 09d7c0b9..30a58414 100644
--- a/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc
+++ b/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc
@@ -16,12 +16,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/child_process_security_policy.h"
 #include "content/public/browser/render_view_host.h"
-#if defined(ENABLE_EXTENSIONS)
-#include "extensions/browser/extension_registry.h"
-#include "extensions/common/constants.h"
-#include "extensions/common/extension.h"
-#include "extensions/common/extension_set.h"
-#endif
+#include "extensions/features/features.h"
 #include "ppapi/c/pp_errors.h"
 #include "ppapi/host/dispatch_host_message.h"
 #include "ppapi/host/host_message_context.h"
@@ -30,6 +25,13 @@
 #include "ppapi/shared_impl/file_system_util.h"
 #include "storage/browser/fileapi/isolated_context.h"
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/constants.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_set.h"
+#endif
+
 namespace chrome {
 
 namespace {
@@ -102,7 +104,7 @@
 
 std::string PepperIsolatedFileSystemMessageFilter::CreateCrxFileSystem(
     Profile* profile) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   const extensions::Extension* extension =
       extensions::ExtensionRegistry::Get(profile)->enabled_extensions().GetByID(
           document_url_.host());
@@ -140,7 +142,7 @@
 
 int32_t PepperIsolatedFileSystemMessageFilter::OpenCrxFileSystem(
     ppapi::host::HostMessageContext* context) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   Profile* profile = GetProfile();
   const extensions::ExtensionSet* extension_set = NULL;
   if (profile) {
diff --git a/chrome/browser/resources/bluetooth_internals/adapter_broker.js b/chrome/browser/resources/bluetooth_internals/adapter_broker.js
index c17c89f..ae9bfaa 100644
--- a/chrome/browser/resources/bluetooth_internals/adapter_broker.js
+++ b/chrome/browser/resources/bluetooth_internals/adapter_broker.js
@@ -28,7 +28,7 @@
 
     /**
      * Sets client of Adapter service.
-     * @param {interfaces.BluetoothAdapter.AdapterClient} adapterClient
+     * @param {!interfaces.BluetoothAdapter.AdapterClient} adapterClient
      */
     setClient: function(adapterClient) {
       this.adapter_.setClient(interfaces.Connection.bindStubDerivedImpl(
@@ -37,7 +37,7 @@
 
     /**
      * Gets an array of currently detectable devices from the Adapter service.
-     * @return {Array<interfaces.BluetoothDevice.DeviceInfo>}
+     * @return {!Array<!interfaces.BluetoothDevice.DeviceInfo>}
      */
     getDevices: function() {
       return this.adapter_.getDevices();
@@ -45,7 +45,7 @@
 
     /**
      * Gets the current state of the Adapter.
-     * @return {interfaces.BluetoothAdapter.AdapterInfo}
+     * @return {!interfaces.BluetoothAdapter.AdapterInfo}
      */
     getInfo: function() {
       return this.adapter_.getInfo();
@@ -109,15 +109,13 @@
 
   /**
    * Initializes an AdapterBroker if one doesn't exist.
-   * @return {Promise<AdapterBroker>} resolves with AdapterBroker,
+   * @return {!Promise<!AdapterBroker>} resolves with AdapterBroker,
    *     rejects if Bluetooth is not supported.
    */
   function getAdapterBroker() {
-    if (adapterBroker) {
-      return Promise.resolve(adapterBroker);
-    }
+    if (adapterBroker) return Promise.resolve(adapterBroker);
 
-    return interfaces.importInterfaces().then(function(adapter) {
+    return interfaces.setupInterfaces().then(function(adapter) {
       // Hook up the instance properties.
       AdapterClient.prototype.__proto__ =
           interfaces.BluetoothAdapter.AdapterClient.stubClass.prototype;
diff --git a/chrome/browser/resources/bluetooth_internals/bluetooth_internals.html b/chrome/browser/resources/bluetooth_internals/bluetooth_internals.html
index 2a6760c5..8502623e 100644
--- a/chrome/browser/resources/bluetooth_internals/bluetooth_internals.html
+++ b/chrome/browser/resources/bluetooth_internals/bluetooth_internals.html
@@ -9,11 +9,9 @@
   <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
   <link rel="stylesheet" href="bluetooth_internals.css">
   <link rel="import" href="chrome://resources/html/cr/ui.html">
-
-  <script src="chrome://resources/js/assert.js"></script>
-  <script src="chrome://resources/js/cr/event_target.js"></script>
-  <script src="chrome://resources/js/cr/ui/array_data_model.js"></script>
-  <script src="chrome://resources/js/util.js"></script>
+  <link rel="import" href="chrome://resources/html/cr/event_target.html">
+  <link rel="import" href="chrome://resources/html/cr/ui/array_data_model.html">
+  <link rel="import" href="chrome://resources/html/util.html">
 
   <script src="interfaces.js"></script>
   <script src="adapter_broker.js"></script>
diff --git a/chrome/browser/resources/bluetooth_internals/device_table.js b/chrome/browser/resources/bluetooth_internals/device_table.js
index b4db9b0..443c590f 100644
--- a/chrome/browser/resources/bluetooth_internals/device_table.js
+++ b/chrome/browser/resources/bluetooth_internals/device_table.js
@@ -3,13 +3,10 @@
 // found in the LICENSE file.
 
 /**
- * Javascript for DeviceTable UI, served from
- *     chrome://bluetooth-internals/.
+ * Javascript for DeviceTable UI, served from chrome://bluetooth-internals/.
  */
 
 cr.define('device_table', function() {
-  var REMOVED_CSS = 'removed';
-
   /**
    * A table that lists the devices and responds to changes in the given
    *     DeviceCollection.
@@ -17,7 +14,7 @@
    * @extends {HTMLTableElement}
    */
   var DeviceTable = cr.ui.define(function() {
-    // @type {Array<device_collection.Device>}
+    /** @private {?Array<device_collection.Device>} */
     this.devices_ = null;
 
     return document.importNode($('table-template').content.children[0],
@@ -32,7 +29,9 @@
      *    table body and headers.
      */
     decorate: function() {
+      /** @private */
       this.body_ = this.tBodies[0];
+      /** @private */
       this.headers_ = this.tHead.rows[0].cells;
     },
 
@@ -117,11 +116,7 @@
       assert(this.body_.rows[index], 'Row ' + index + ' is not in the table.');
       var row = this.body_.rows[index];
 
-      if (device.removed) {
-        row.classList.add(REMOVED_CSS);
-      } else {
-        row.classList.remove(REMOVED_CSS);
-      }
+      row.classList.toggle('removed', device.removed);
 
       // Update the properties based on the header field path.
       for (var i = 0; i < this.headers_.length; i++) {
diff --git a/chrome/browser/resources/bluetooth_internals/interfaces.js b/chrome/browser/resources/bluetooth_internals/interfaces.js
index 391deac..a48eb81 100644
--- a/chrome/browser/resources/bluetooth_internals/interfaces.js
+++ b/chrome/browser/resources/bluetooth_internals/interfaces.js
@@ -9,10 +9,10 @@
 
 cr.define('interfaces', function() {
   /**
-   * Imports Mojo interfaces and adds them to window.interfaces.
+   * Sets up Mojo interfaces and adds them to window.interfaces.
    * @return {Promise}
    */
-  function importInterfaces() {
+  function setupInterfaces() {
     return importModules([
       'content/public/renderer/frame_interfaces',
       'device/bluetooth/public/interfaces/adapter.mojom',
@@ -20,20 +20,14 @@
       'mojo/public/js/connection',
     ]).then(function([frameInterfaces, bluetoothAdapter, bluetoothDevice,
         connection]) {
-      Object.assign(interfaces, {
-        BluetoothAdapter: bluetoothAdapter,
-        BluetoothDevice: bluetoothDevice,
-        Connection: connection,
-        FrameInterfaces: frameInterfaces,
-      });
+      interfaces.BluetoothAdapter = bluetoothAdapter;
+      interfaces.BluetoothDevice = bluetoothDevice;
+      interfaces.Connection = connection;
+      interfaces.FrameInterfaces = frameInterfaces;
     });
   }
 
   return {
-    BluetoothAdapter: {},
-    BluetoothDevice: {},
-    Connection: {},
-    FrameInterfaces: {},
-    importInterfaces: importInterfaces,
+    setupInterfaces: setupInterfaces,
   };
 });
diff --git a/chrome/browser/resources/chromeos/login/login.js b/chrome/browser/resources/chromeos/login/login.js
index f5e258d9..53be21604 100644
--- a/chrome/browser/resources/chromeos/login/login.js
+++ b/chrome/browser/resources/chromeos/login/login.js
@@ -30,6 +30,7 @@
       login.PasswordChangedScreen.register();
       login.SupervisedUserCreationScreen.register();
       login.TermsOfServiceScreen.register();
+      login.ArcTermsOfServiceScreen.register();
       login.AppLaunchSplashScreen.register();
       login.ConfirmPasswordScreen.register();
       login.FatalErrorScreen.register();
diff --git a/chrome/browser/resources/chromeos/login/login_non_lock_shared.html b/chrome/browser/resources/chromeos/login/login_non_lock_shared.html
index 10ac9cdd..4154892 100644
--- a/chrome/browser/resources/chromeos/login/login_non_lock_shared.html
+++ b/chrome/browser/resources/chromeos/login/login_non_lock_shared.html
@@ -21,6 +21,7 @@
 <link rel="stylesheet" href="oobe_screen_user_image.css">
 
 <link rel="stylesheet" href="screen_app_launch_splash.css">
+<link rel="stylesheet" href="screen_arc_terms_of_service.css">
 <link rel="stylesheet" href="screen_gaia_signin.css">
 <link rel="stylesheet" href="screen_error_message.css">
 <link rel="stylesheet" href="screen_tpm_error.css">
diff --git a/chrome/browser/resources/chromeos/login/login_non_lock_shared.js b/chrome/browser/resources/chromeos/login/login_non_lock_shared.js
index 47d8aeb..7cb52e6 100644
--- a/chrome/browser/resources/chromeos/login/login_non_lock_shared.js
+++ b/chrome/browser/resources/chromeos/login/login_non_lock_shared.js
@@ -18,6 +18,7 @@
 <include src="oobe_screen_user_image.js">
 
 <include src="screen_app_launch_splash.js">
+<include src="screen_arc_terms_of_service.js">
 <include src="screen_error_message.js">
 <include src="screen_gaia_signin.js">
 <include src="screen_password_changed.js">
diff --git a/chrome/browser/resources/chromeos/login/login_screens.html b/chrome/browser/resources/chromeos/login/login_screens.html
index be7208a..8b928823 100644
--- a/chrome/browser/resources/chromeos/login/login_screens.html
+++ b/chrome/browser/resources/chromeos/login/login_screens.html
@@ -4,6 +4,7 @@
 <include src="oobe_screen_terms_of_service.html">
 <include src="oobe_screen_user_image.html">
 <include src="../../../../../ui/login/account_picker/screen_account_picker.html">
+<include src="screen_arc_terms_of_service.html">
 <include src="screen_error_message.html">
 <include src="screen_gaia_signin.html">
 <include src="screen_supervised_user_creation.html">
diff --git a/chrome/browser/resources/chromeos/login/oobe.js b/chrome/browser/resources/chromeos/login/oobe.js
index 137ac842..6ad641c 100644
--- a/chrome/browser/resources/chromeos/login/oobe.js
+++ b/chrome/browser/resources/chromeos/login/oobe.js
@@ -110,6 +110,7 @@
       login.PasswordChangedScreen.register();
       login.SupervisedUserCreationScreen.register();
       login.TermsOfServiceScreen.register();
+      login.ArcTermsOfServiceScreen.register();
       login.AppLaunchSplashScreen.register();
       login.ConfirmPasswordScreen.register();
       login.FatalErrorScreen.register();
diff --git a/chrome/browser/resources/chromeos/login/oobe_screens.html b/chrome/browser/resources/chromeos/login/oobe_screens.html
index 508eea33..fc8a619f 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screens.html
+++ b/chrome/browser/resources/chromeos/login/oobe_screens.html
@@ -13,6 +13,7 @@
 <include src="oobe_screen_hid_detection.html">
 <include src="../../../../../ui/login/account_picker/screen_account_picker.html">
 <include src="screen_error_message.html">
+<include src="screen_arc_terms_of_service.html">
 <include src="screen_gaia_signin.html">
 <include src="screen_supervised_user_creation.html">
 <include src="screen_password_changed.html">
diff --git a/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.css b/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.css
new file mode 100644
index 0000000..d1c500f
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.css
@@ -0,0 +1,198 @@
+/* Copyright 2016 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+#arc-tos {
+  padding: 16px 17px 21px;
+}
+
+#arc-tos .step-contents {
+  -webkit-margin-start: 32px;
+  margin-bottom: 49px;
+}
+
+#arc-tos a:link,
+#arc-tos a:visited {
+  color: rgb(51, 103, 214);
+  text-decoration: none;
+}
+
+#arc-tos h1 {
+  color: rgba(0, 0, 0, 0.54);
+  font-size: 14px;
+  font-weight: 600;
+  margin: 12px 0 8px 0;
+  width: 630px;
+}
+
+#arc-tos input[type='checkbox'] {
+  flex-shrink: 0;
+  height: 12px;
+  margin: 0 8px 0 0;
+  padding: 0;
+  width: 12px;
+}
+
+#arc-tos label {
+  color: rgba(0, 0, 0, 0.54);
+  display: flex;
+  flex-direction: row;
+  font-size: 10px;
+  margin: auto;
+  padding: 8px 0 0 0;
+  text-align: left;
+  width: 100%;
+}
+
+#arc-tos-container {
+  height: 320px;
+  overflow: hidden;
+  width: 630px;
+}
+
+#arc-tos-content {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  margin: auto;
+  padding: 16 px;
+  width: 100%;
+}
+
+#arc-tos-content p {
+  font-size: 10px;
+  font-weight: 400;
+  line-height: 16px;
+  margin: 0;
+}
+
+#arc-tos-loading,
+#arc-tos-error {
+  -webkit-align-items: center;
+  -webkit-justify-content: center;
+  display: none;
+  flex-direction: column;
+  height: 100%;
+  width: 100%;
+}
+
+#arc-tos-loading p,
+#arc-tos-error p {
+  color: rgba(0, 0, 0, 0.54);
+  font-size: 16px;
+  font-weight: 400;
+  line-height: 16px;
+}
+
+#arc-tos-logo {
+  height: 32px;
+  margin-left: -28px;
+  width: auto;
+}
+
+#arc-tos-view {
+  display: block;
+  height: 10px;
+  margin: auto;
+  padding: 0;
+}
+
+#arc-tos-view-container {
+  border: 1px solid #d9d9d9;
+  box-sizing: border-box;
+  flex: auto;
+  margin: auto;
+  padding: 0;
+  width: 100%;
+}
+
+#arc-tos-overlay-content-text {
+  box-sizing: border-box;
+  height: 200px;
+  margin: auto;
+  padding: 24px 8px 8px 8px;
+  width: 100%;
+}
+
+#arc-tos-overlay-content-text p {
+  color: rgba(0, 0, 0, 0.54);
+  font-size: 12px;
+  font-weight: 400;
+  text-align: justify;
+}
+
+#arc-tos-overlay-text button,
+#arc-tos-overlay-url button {
+  float: right;
+  margin: 8px;
+}
+
+#arc-tos-overlay-webview {
+  border: 1px solid #d9d9d9;
+  display: block;
+  height: 270px;
+  width: 622px;
+}
+
+#arc-tos-overlay-webview-container {
+  box-sizing: border-box;
+  height: 300px;
+  margin: auto;
+  padding: 24px 8px 8px 8px;
+  width: 100%;
+}
+
+#arc-tos-skip-button {
+  margin-right: 12px;
+}
+
+.step.arc-tos-loading #arc-tos-content {
+  display: none;
+}
+
+.step.arc-tos-loading #arc-tos-loading {
+  display: flex;
+}
+
+.step.error #arc-tos-content,
+.step.error #arc-tos-accept-button {
+  display: none;
+}
+
+.step.arc-tos-loading #arc-tos-retry-button,
+.step.arc-tos-loaded #arc-tos-retry-button {
+  display: none;
+}
+
+.step.error #arc-tos-error {
+  display: flex;
+}
+
+.arc-tos-overlay-wide {
+  width: 640px;
+}
+
+.arc-tos-overlay-close-up {
+  background-image: url(chrome://theme/IDR_CLOSE_DIALOG);
+  background-position: center;
+  background-repeat: no-repeat;
+  height: 14px;
+  position: absolute;
+  right: 7px;
+  top: 7px;
+  width: 14px;
+  z-index: 1;
+}
+
+html[dir='rtl'] .arc-tos-overlay-close-up {
+  left: 10px;
+  right: auto;
+}
+
+.arc-tos-overlay-close-up:hover {
+  background-image: url(chrome://theme/IDR_CLOSE_DIALOG_H);
+}
+
+.arc-tos-overlay-close-up:active {
+  background-image: url(chrome://theme/IDR_CLOSE_DIALOG_P);
+}
diff --git a/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.html b/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.html
new file mode 100644
index 0000000..c609c31
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.html
@@ -0,0 +1,66 @@
+<link rel="stylesheet" href="chrome://resources/css/overlay.css">
+
+<div class="step right hidden arc-tos-loading no-logo" id="arc-tos" hidden>
+  <div class="step-contents">
+    <img id="arc-tos-logo" 
+        src="https://play.google.com/about/images/play_logo.png" alt>
+    <h1 i18n-content="arcTermsOfServiceScreenHeading"></h1>
+    <div id="arc-tos-container">
+      <div id="arc-tos-content">
+        <div id="arc-tos-view-container">
+          <webview id="arc-tos-view"></webview>
+        </div>
+        <label>
+          <a id="arc-policy-link" href="#" i18n-content="arcPolicyLink">
+          </a>
+        </label>
+        <label>
+          <input type="checkbox" id="arc-enable-metrics">
+          <p id="arc-text-metrics"></p>
+        </label>
+        <label>
+          <input type="checkbox" id="arc-enable-backup-restore">
+          <p i18n-values=".innerHTML:arcTextBackupRestore"></p>
+        </label>
+        <label>
+          <input type="checkbox" id="arc-enable-location-service">
+          <p i18n-values=".innerHTML:arcTextLocationService"></p>
+        </label>
+      </div>
+      <div id="arc-tos-loading">
+        <p i18n-content="arcTermsOfServiceLoading"></p>
+      </div>
+      <div id="arc-tos-error">
+        <p i18n-content="arcTermsOfServiceError"></p>
+      </div>
+    </div>
+  </div>
+  <div id="arc-tos-controls" class="step-controls"></div>
+
+  <div id="arc-tos-overlay-text" class="popup-overlay" hidden>
+    <div class="oobe-popup not-resizable">
+      <div class="arc-tos-overlay-close-up arc-overlay-close-button">
+      </div>
+      <div id="arc-tos-overlay-content-text">
+        <span id="arc-learn-more-content"></span>
+      </div>
+      <button class="arc-tos-overlay-close-bottom arc-overlay-close-button"
+          i18n-content="arcOverlayClose">
+      </button>
+    </div>
+  </div>
+
+  <div id="arc-tos-overlay-url" class="popup-overlay" hidden>
+    <div class="oobe-popup not-resizable arc-tos-overlay-wide">
+      <div class="arc-tos-overlay-close-up arc-overlay-close-button">
+      </div>
+      <div id="arc-tos-overlay-webview-container">
+        <webview id="arc-tos-overlay-webview"></webview>
+      </div>
+      <button class="arc-tos-overlay-close-bottom arc-overlay-close-button"
+          i18n-content="arcOverlayClose">
+      </button>
+    </div>
+  </div>
+
+</div>
diff --git a/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.js b/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.js
new file mode 100644
index 0000000..ad349435
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.js
@@ -0,0 +1,303 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Oobe Arc Terms of Service screen implementation.
+ */
+
+login.createScreen('ArcTermsOfServiceScreen', 'arc-tos',
+  function() { return {
+    EXTERNAL_API: [
+      'setMetricsMode',
+      'setBackupAndRestoreMode',
+      'setLocationServicesMode'
+    ],
+
+    /** @override */
+    decorate: function(element) {
+      var closeButtons = document.querySelectorAll('.arc-overlay-close-button');
+      for (var i = 0; i < closeButtons.length; i++) {
+        closeButtons[i].addEventListener('click', this.hideOverlay.bind(this));
+      }
+
+      var termsView = $('arc-tos-view');
+      var requestFilter = {
+        urls: ['<all_urls>'],
+        types: ['main_frame']
+      };
+
+      termsView.request.onErrorOccurred.addListener(
+          this.onTermsViewErrorOccurred.bind(this), requestFilter);
+      termsView.addEventListener('contentload',
+          this.onTermsViewContentLoad.bind(this));
+
+      // Open links from webview in overlay dialog.
+      var self = this;
+      termsView.addEventListener('newwindow', function(event) {
+        event.preventDefault();
+        self.showUrlOverlay(event.targetUrl);
+      });
+
+      termsView.addContentScripts([
+        { name: 'postProcess',
+          matches: ['https://play.google.com/*'],
+          css: { files: ['playstore.css'] },
+          js: { files: ['playstore.js'] },
+          run_at: 'document_end'
+        }]);
+
+      $('arc-policy-link').onclick = function() {
+        termsView.executeScript(
+            {code: 'getPrivacyPolicyLink();'},
+            function(results) {
+              if (results && results.length == 1 &&
+                  typeof results[0] == 'string') {
+                self.showUrlOverlay(results[0]);
+              } else {
+                self.showUrlOverlay('https://www.google.com/policies/privacy/');
+              }
+            });
+      };
+
+      this.updateLocalizedContent();
+    },
+
+    /**
+     * Sets current metrics mode.
+     * @param {string} text Describes current metrics state.
+     * @param {boolean} canEnable Defines if user is allowed to change this
+     *                            metrics option.
+     * @param {boolean} on Defines if metrics are active currently.
+     */
+    setMetricsMode: function(text, canEnable, on) {
+      $('arc-enable-metrics').hidden = !canEnable;
+      $('arc-enable-metrics').checked = on;
+      $('arc-text-metrics').innerHTML = text;
+
+      var self = this;
+      var leanMoreStatisticsText =
+          loadTimeData.getString('arcLearnMoreStatistics');
+      $('learn-more-link-metrics').onclick = function() {
+        self.showLearnMoreOverlay(leanMoreStatisticsText);
+      };
+    },
+
+    /**
+     * Applies current enabled/managed state to checkbox and text.
+     * @param {string} checkBoxId Id of checkbox to set on/off.
+     * @param {string} textId Id of text to set enabled state.
+     * @param {boolean} enabled Defines the value of the checkbox.
+     * @param {boolean} managed Defines whether this setting is set by policy.
+     */
+    setPreference(checkBoxId, textId, enabled, managed) {
+      $(checkBoxId).checked = enabled;
+      $(checkBoxId).disabled = managed;
+      $(textId).disabled = managed;
+    },
+
+    /**
+     * Sets current backup and restore mode.
+     * @param {boolean} enabled Defines the value for backup and restore
+     *                          checkbox.
+     * @param {boolean} managed Defines whether this setting is set by policy.
+     */
+    setBackupAndRestoreMode: function(enabled, managed) {
+      this.setPreference('arc-enable-backup-restore',
+                         'arc-text-backup-restore',
+                         enabled, managed);
+    },
+
+    /**
+     * Sets current usage of location service opt in mode.
+     * @param {boolean} enabled Defines the value for location service opt in.
+     * @param {boolean} managed Defines whether this setting is set by policy.
+     */
+    setLocationServicesMode: function(enabled, managed) {
+      this.setPreference('arc-enable-location-service',
+                         'arc-text-location-service',
+                         enabled, managed);
+    },
+
+    /**
+     * Buttons in Oobe wizard's button strip.
+     * @type {array} Array of Buttons.
+     */
+    get buttons() {
+      var buttons = [];
+
+      var skipButton = this.ownerDocument.createElement('button');
+      skipButton.id = 'arc-tos-skip-button';
+      skipButton.textContent =
+          loadTimeData.getString('arcTermsOfServiceSkipButton');
+      skipButton.addEventListener('click', function(event) {
+        $('arc-tos-skip-button').disabled = true;
+        $('arc-tos-accept-button').disabled = true;
+        chrome.send('arcTermsOfServiceSkip');
+      });
+      buttons.push(skipButton);
+
+      var retryButton = this.ownerDocument.createElement('button');
+      retryButton.id = 'arc-tos-retry-button';
+      retryButton.textContent =
+          loadTimeData.getString('arcTermsOfServiceRetryButton');
+      retryButton.addEventListener('click', this.reloadPlayStore.bind(this));
+      buttons.push(retryButton);
+
+      var acceptButton = this.ownerDocument.createElement('button');
+      acceptButton.id = 'arc-tos-accept-button';
+      acceptButton.disabled = this.classList.contains('arc-tos-loading');
+      acceptButton.classList.add('preserve-disabled-state');
+      acceptButton.textContent =
+          loadTimeData.getString('arcTermsOfServiceAcceptButton');
+      acceptButton.addEventListener('click', function(event) {
+        $('arc-tos-skip-button').disabled = true;
+        $('arc-tos-accept-button').disabled = true;
+
+        var enableMetrics = $('arc-enable-metrics');
+        var isMetricsEnabled = !enableMetrics.hidden && enableMetrics.checked;
+        var isBackupRestoreEnabled = $('arc-enable-backup-restore').checked;
+        var isLocationServiceEnabled = $('arc-enable-location-service').checked;
+
+        chrome.send('arcTermsOfServiceAccept',
+            [isMetricsEnabled,
+             isBackupRestoreEnabled,
+             isLocationServiceEnabled]);
+      });
+      buttons.push(acceptButton);
+
+      return buttons;
+    },
+
+    /**
+     * Returns the control which should receive initial focus.
+     */
+    get defaultControl() {
+      return $('arc-tos-accept-button').disabled ? $('arc-tos-skip-button') :
+                                                   $('arc-tos-accept-button');
+    },
+
+    /**
+     * Sets learn more content text and shows it as overlay dialog.
+     * @param {string} content HTML formatted text to show.
+     */
+    showLearnMoreOverlay: function(content) {
+      $('arc-learn-more-content').innerHTML = content;
+      $('arc-tos-overlay-content-text').hidden = false;
+      $('arc-tos-overlay-text').hidden = false;
+    },
+
+    /**
+     * Opens external URL in popup overlay.
+     * @param {string} targetUrl URL to open.
+     */
+    showUrlOverlay: function(targetUrl) {
+      $('arc-tos-overlay-webview').src = targetUrl;
+      $('arc-tos-overlay-url').hidden = false;
+    },
+
+    /**
+     * Hides overlay dialog.
+     */
+    hideOverlay: function() {
+      $('arc-tos-overlay-text').hidden = true;
+      $('arc-tos-overlay-url').hidden = true;
+    },
+
+    /**
+     * Reloads Play Store.
+     */
+    reloadPlayStore: function() {
+      this.termsError = false;
+      var termsView = $('arc-tos-view');
+      termsView.src = 'https://play.google.com/about/play-terms.html';
+      this.classList.remove('arc-tos-loaded');
+      this.classList.remove('error');
+      this.classList.add('arc-tos-loading');
+    },
+
+    /**
+     * Handles event when terms view is loaded.
+     */
+    onTermsViewContentLoad: function() {
+      if (this.termsError) {
+        return;
+      }
+
+      this.classList.remove('arc-tos-loading');
+      this.classList.remove('error');
+      this.classList.add('arc-tos-loaded');
+
+      var acceptButton = $('arc-tos-accept-button');
+      var skipButton = $('arc-tos-skip-button');
+
+      acceptButton.disabled = false;
+      if (document.activeElement != skipButton) {
+        acceptButton.focus();
+      }
+
+      var termsView = $('arc-tos-view');
+      var termsViewContainer = $('arc-tos-view-container');
+      var setTermsHeight = function() {
+        // Reset terms-view height in order to stabilize style computation.
+        // For some reason, child webview affects final result.
+        termsView.style.height = '0px';
+        var style = window.getComputedStyle(termsViewContainer, null);
+        var height = style.getPropertyValue('height');
+        termsView.style.height = height;
+      };
+      setTimeout(setTermsHeight, 0);
+    },
+
+    /**
+     * Handles event when terms view cannot be loaded.
+     */
+    onTermsViewErrorOccurred: function(details) {
+      this.termsError = true;
+      this.classList.remove('arc-tos-loading');
+      this.classList.remove('arc-tos-loaded');
+      this.classList.add('error');
+    },
+
+    /**
+     * Event handler that is invoked just before the screen is shown.
+     * @param {object} data Screen init payload.
+     */
+    onBeforeShow: function(data) {
+      Oobe.getInstance().headerHidden = true;
+
+      this.hideOverlay();
+      this.reloadPlayStore();
+    },
+
+    /**
+     * Updates localized content of the screen that is not updated via template.
+     */
+    updateLocalizedContent: function() {
+      var self = this;
+
+      var leanMoreBackupAndRestoreText =
+          loadTimeData.getString('arcLearnMoreBackupAndRestore');
+      $('learn-more-link-backup-restore').onclick = function() {
+        self.showLearnMoreOverlay(leanMoreBackupAndRestoreText);
+      };
+
+      var leanMoreLocationServiceText =
+          loadTimeData.getString('arcLearnMoreLocationService');
+      $('learn-more-link-location-service').onclick = function() {
+        self.showLearnMoreOverlay(leanMoreLocationServiceText);
+      };
+
+      var scriptSetCountryCode = 'document.countryCode = \'' +
+          loadTimeData.getString('arcCountryCode') + '\';';
+      var termsView = $('arc-tos-view');
+      termsView.removeContentScripts(['preProcess']);
+      termsView.addContentScripts([
+          { name: 'preProcess',
+            matches: ['https://play.google.com/*'],
+            js: { code: scriptSetCountryCode },
+            run_at: 'document_start'
+          }]);
+    }
+  };
+});
diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd
index d733deb..2cec82f 100644
--- a/chrome/browser/resources/component_extension_resources.grd
+++ b/chrome/browser/resources/component_extension_resources.grd
@@ -40,7 +40,7 @@
       <include name="IDR_BOOKMARK_MANAGER_BOOKMARK_DND_JS" file="bookmark_manager/js/dnd.js" type="BINDATA" />
       <include name="IDR_BOOKMARK_MANAGER_BOOKMARK_BMM_JS" file="bookmark_manager/js/bmm.js" type="BINDATA" />
       <!-- Material Design Bookmarks -->
-      <include name="IDR_MD_BOOKMARKS_BOOKMARKS_HTML" file="md_bookmarks/bookmarks.html" type="BINDATA" />
+      <include name="IDR_COMPONENT_MD_BOOKMARKS_BOOKMARKS_HTML" file="md_bookmarks/bookmarks.html" type="BINDATA" />
       <!-- Gaia auth extension -->
       <include name="IDR_GAIA_AUTH_MAIN" file="gaia_auth/main.html" allowexternalscript="true" type="BINDATA" />
       <include name="IDR_GAIA_AUTH_MAIN_JS" file="gaia_auth/main.js" type="BINDATA" />
diff --git a/chrome/browser/resources/settings/site_settings/site_data_details_subpage.html b/chrome/browser/resources/settings/site_settings/site_data_details_subpage.html
index 813ff27..ece1b3e 100644
--- a/chrome/browser/resources/settings/site_settings/site_data_details_subpage.html
+++ b/chrome/browser/resources/settings/site_settings/site_data_details_subpage.html
@@ -13,6 +13,13 @@
       [first] {
         border-top: none;
       }
+
+      .secondary,
+      .start {
+        -webkit-user-select: text;
+        max-width: 100%;
+        word-wrap: break-word;
+      }
     </style>
     <template is="dom-repeat" items="[[entries_]]">
       <div class="settings-box" first$="[[!index]]">
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_behavior.js b/chrome/browser/resources/settings/site_settings/site_settings_behavior.js
index f8d1933..197af766 100644
--- a/chrome/browser/resources/settings/site_settings/site_settings_behavior.js
+++ b/chrome/browser/resources/settings/site_settings/site_settings_behavior.js
@@ -433,8 +433,10 @@
    */
   expandSiteException: function(exception) {
     var origin = exception.origin;
-    var url = this.toUrl(origin);
-    var originForDisplay = url ? this.sanitizePort(url.origin) : origin;
+    // TODO(dschuyler): If orginForDisplay becomes different from origin in the
+    // site settings, that filtering would happen here. If that doesn't happen
+    // then originForDisplay should be removed (it's redundant with origin).
+    // e.g. var originForDisplay = someFilter(origin);
 
     var embeddingOrigin = exception.embeddingOrigin;
     var embeddingOriginForDisplay = '';
@@ -445,7 +447,7 @@
 
     return {
       origin: origin,
-      originForDisplay: originForDisplay,
+      originForDisplay: origin,
       embeddingOrigin: embeddingOrigin,
       embeddingOriginForDisplay: embeddingOriginForDisplay,
       incognito: exception.incognito,
diff --git a/chrome/browser/safe_browsing/incident_reporting/extension_data_collection.cc b/chrome/browser/safe_browsing/incident_reporting/extension_data_collection.cc
index 6287629..d2723ab 100644
--- a/chrome/browser/safe_browsing/incident_reporting/extension_data_collection.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/extension_data_collection.cc
@@ -19,6 +19,7 @@
 #include "extensions/browser/extension_registry_factory.h"
 #include "extensions/common/extension_set.h"
 #include "extensions/common/manifest_constants.h"
+#include "extensions/features/features.h"
 
 namespace safe_browsing {
 
@@ -100,7 +101,7 @@
 // Finds the last installed extension and adds relevant information to data's
 // last_installed_extension field.
 void CollectExtensionData(ClientIncidentReport_ExtensionData* data) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   scoped_refptr<const extensions::Extension> last_installed_extension;
   Profile* profile_for_last_installed_extension = nullptr;
   base::Time last_install_time;
diff --git a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
index b60f4cf..04b8520 100644
--- a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
@@ -33,6 +33,7 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
 #include "crypto/sha2.h"
+#include "extensions/features/features.h"
 
 namespace safe_browsing {
 
@@ -73,7 +74,7 @@
 #endif
 
 // Extensions are supported where enabled.
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (download_type == ClientDownloadRequest::CHROME_EXTENSION)
     return true;
 #endif
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
index 9605b24..7bbcd48 100644
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
@@ -588,7 +588,8 @@
     model_client->GetSecurityInfo(&security_info);
     EXPECT_EQ(security_state::SecurityStateModel::DANGEROUS,
               security_info.security_level);
-    EXPECT_TRUE(security_info.fails_malware_check);
+    EXPECT_NE(security_state::SecurityStateModel::MALICIOUS_CONTENT_STATUS_NONE,
+              security_info.malicious_content_status);
     // TODO(felt): Restore this check when https://crbug.com/641187 is fixed.
     // EXPECT_EQ(cert_status, model_client->GetSecurityInfo().cert_status);
   }
@@ -601,7 +602,8 @@
     model_client->GetSecurityInfo(&security_info);
     EXPECT_EQ(security_state::SecurityStateModel::NONE,
               security_info.security_level);
-    EXPECT_FALSE(security_info.fails_malware_check);
+    EXPECT_EQ(security_state::SecurityStateModel::MALICIOUS_CONTENT_STATUS_NONE,
+              security_info.malicious_content_status);
   }
 
  protected:
diff --git a/chrome/browser/safe_browsing/ui_manager.cc b/chrome/browser/safe_browsing/ui_manager.cc
index 931af35..cb88295 100644
--- a/chrome/browser/safe_browsing/ui_manager.cc
+++ b/chrome/browser/safe_browsing/ui_manager.cc
@@ -39,37 +39,57 @@
 using content::NavigationEntry;
 using content::WebContents;
 using safe_browsing::HitReport;
+using safe_browsing::SBThreatType;
 
 namespace {
 
 const void* const kWhitelistKey = &kWhitelistKey;
 
-// A WhitelistUrlSet holds the set of URLs that have been whitelisted for a
-// specific WebContents, along with pending entries that are still undecided.
-// The URLs in this set should come from GetWhitelistUrl() or
-// GetMainFrameWhitelistUrlForResource().
+// A WhitelistUrlSet holds the set of URLs that have been whitelisted
+// for a specific WebContents, along with pending entries that are still
+// undecided. Each URL is associated with the first SBThreatType that
+// was seen for that URL. The URLs in this set should come from
+// GetWhitelistUrl() or GetMainFrameWhitelistUrlForResource().
 class WhitelistUrlSet : public base::SupportsUserData::Data {
  public:
   WhitelistUrlSet() {}
 
-  bool Contains(const GURL url) { return set_.find(url) != set_.end(); }
+  bool Contains(const GURL url, SBThreatType* threat_type) {
+    auto found = map_.find(url);
+    if (found == map_.end())
+      return false;
+    if (threat_type)
+      *threat_type = found->second;
+    return true;
+  }
 
   void RemovePending(const GURL& url) { pending_.erase(url); }
 
-  void Insert(const GURL url) {
-    set_.insert(url);
+  void Insert(const GURL url, SBThreatType threat_type) {
+    if (Contains(url, nullptr))
+      return;
+    map_[url] = threat_type;
     RemovePending(url);
   }
 
-  bool ContainsPending(const GURL url) {
-    return pending_.find(url) != pending_.end();
+  bool ContainsPending(const GURL& url, SBThreatType* threat_type) {
+    auto found = pending_.find(url);
+    if (found == pending_.end())
+      return false;
+    if (threat_type)
+      *threat_type = found->second;
+    return true;
   }
 
-  void InsertPending(const GURL url) { pending_.insert(url); }
+  void InsertPending(const GURL url, SBThreatType threat_type) {
+    if (ContainsPending(url, nullptr))
+      return;
+    pending_[url] = threat_type;
+  }
 
  private:
-  std::set<GURL> set_;
-  std::set<GURL> pending_;
+  std::map<GURL, SBThreatType> map_;
+  std::map<GURL, SBThreatType> pending_;
 
   DISALLOW_COPY_AND_ASSIGN(WhitelistUrlSet);
 };
@@ -205,7 +225,8 @@
         nullptr /* no navigation entry needed for main resource */);
     if (proceed) {
       AddToWhitelistUrlSet(whitelist_url, web_contents,
-                           false /* Pending -> permanent */);
+                           false /* Pending -> permanent */,
+                           resource.threat_type);
     } else if (web_contents) {
       // |web_contents| doesn't exist if the tab has been closed.
       RemoveFromPendingWhitelistUrlSet(whitelist_url, web_contents);
@@ -302,7 +323,8 @@
   }
   AddToWhitelistUrlSet(GetMainFrameWhitelistUrlForResource(resource),
                        resource.web_contents_getter.Run(),
-                       true /* A decision is now pending */);
+                       true /* A decision is now pending */,
+                       resource.threat_type);
   SafeBrowsingBlockingPage::ShowBlockingPage(this, resource);
 }
 
@@ -421,7 +443,8 @@
 void SafeBrowsingUIManager::AddToWhitelistUrlSet(
     const GURL& whitelist_url,
     content::WebContents* web_contents,
-    bool pending) {
+    bool pending,
+    SBThreatType threat_type) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // A WebContents might not exist if the tab has been closed.
@@ -434,9 +457,9 @@
     return;
 
   if (pending) {
-    site_list->InsertPending(whitelist_url);
+    site_list->InsertPending(whitelist_url, threat_type);
   } else {
-    site_list->Insert(whitelist_url);
+    site_list->Insert(whitelist_url, threat_type);
   }
 
   // Notify security UI that security state has changed.
@@ -473,7 +496,7 @@
   // remove the main-frame URL from the pending whitelist, so the
   // main-frame URL will have already been removed when the subsequent
   // blocking pages are dismissed.
-  if (site_list->ContainsPending(whitelist_url))
+  if (site_list->ContainsPending(whitelist_url, nullptr))
     site_list->RemovePending(whitelist_url);
 
   // Notify security UI that security state has changed.
@@ -485,9 +508,10 @@
   if (resource.is_subresource) {
     entry = resource.GetNavigationEntryForResource();
   }
+  SBThreatType unused_threat_type;
   return IsUrlWhitelistedOrPendingForWebContents(
       resource.url, resource.is_subresource, entry,
-      resource.web_contents_getter.Run(), true);
+      resource.web_contents_getter.Run(), true, &unused_threat_type);
 }
 
 // Check if the user has already seen and/or ignored a SB warning for this
@@ -497,7 +521,8 @@
     bool is_subresource,
     NavigationEntry* entry,
     content::WebContents* web_contents,
-    bool whitelist_only) {
+    bool whitelist_only,
+    SBThreatType* threat_type) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   GURL lookup_url = GetWhitelistUrl(url, is_subresource, entry);
@@ -509,11 +534,11 @@
   if (!site_list)
     return false;
 
-  bool whitelisted = site_list->Contains(lookup_url);
+  bool whitelisted = site_list->Contains(lookup_url, threat_type);
   if (whitelist_only) {
     return whitelisted;
   } else {
-    return whitelisted || site_list->ContainsPending(lookup_url);
+    return whitelisted || site_list->ContainsPending(lookup_url, threat_type);
   }
 }
 
diff --git a/chrome/browser/safe_browsing/ui_manager.h b/chrome/browser/safe_browsing/ui_manager.h
index 0b79374..3c754c0 100644
--- a/chrome/browser/safe_browsing/ui_manager.h
+++ b/chrome/browser/safe_browsing/ui_manager.h
@@ -135,12 +135,17 @@
   // |url| is already displaying *or* if the user has seen an
   // interstitial for |url| before in this WebContents and proceeded
   // through it. Called on the UI thread.
+  //
+  // If the resource was found in the whitelist or pending for the
+  // whitelist, |threat_type| will be set to the SBThreatType for which
+  // the URL was first whitelisted.
   bool IsUrlWhitelistedOrPendingForWebContents(
       const GURL& url,
       bool is_subresource,
       content::NavigationEntry* entry,
       content::WebContents* web_contents,
-      bool whitelist_only);
+      bool whitelist_only,
+      SBThreatType* threat_type);
 
   // The blocking page for |web_contents| on the UI thread has
   // completed, with |proceed| set to true if the user has chosen to
@@ -214,7 +219,8 @@
   // Updates the whitelist URL set for |web_contents|. Called on the UI thread.
   void AddToWhitelistUrlSet(const GURL& whitelist_url,
                             content::WebContents* web_contents,
-                            bool is_pending);
+                            bool is_pending,
+                            SBThreatType threat_type);
 
   // Removes |whitelist_url| from the pending whitelist for
   // |web_contents|. Called on the UI thread.
diff --git a/chrome/browser/safe_browsing/ui_manager_unittest.cc b/chrome/browser/safe_browsing/ui_manager_unittest.cc
index 9d08f6bf..d0dfd247 100644
--- a/chrome/browser/safe_browsing/ui_manager_unittest.cc
+++ b/chrome/browser/safe_browsing/ui_manager_unittest.cc
@@ -87,7 +87,7 @@
     ui_manager_->AddToWhitelistUrlSet(
         SafeBrowsingUIManager::GetMainFrameWhitelistUrlForResourceForTesting(
             resource),
-        web_contents(), false);
+        web_contents(), false, resource.threat_type);
   }
 
   SafeBrowsingUIManager::UnsafeResource MakeUnsafeResource(
@@ -148,6 +148,21 @@
   EXPECT_FALSE(IsWhitelisted(resource));
 }
 
+TEST_F(SafeBrowsingUIManagerTest, WhitelistRemembersThreatType) {
+  SafeBrowsingUIManager::UnsafeResource resource =
+      MakeUnsafeResourceAndStartNavigation(kBadURL);
+  AddToWhitelist(resource);
+  EXPECT_TRUE(IsWhitelisted(resource));
+  SBThreatType threat_type;
+  content::NavigationEntry* entry =
+      web_contents()->GetController().GetVisibleEntry();
+  ASSERT_TRUE(entry);
+  EXPECT_TRUE(ui_manager()->IsUrlWhitelistedOrPendingForWebContents(
+      resource.url, resource.is_subresource, entry,
+      resource.web_contents_getter.Run(), true, &threat_type));
+  EXPECT_EQ(resource.threat_type, threat_type);
+}
+
 TEST_F(SafeBrowsingUIManagerTest, WhitelistIgnoresPath) {
   SafeBrowsingUIManager::UnsafeResource resource =
       MakeUnsafeResourceAndStartNavigation(kBadURL);
diff --git a/chrome/browser/sessions/chrome_tab_restore_service_client.cc b/chrome/browser/sessions/chrome_tab_restore_service_client.cc
index 9ea44a2..8d7ebe4 100644
--- a/chrome/browser/sessions/chrome_tab_restore_service_client.cc
+++ b/chrome/browser/sessions/chrome_tab_restore_service_client.cc
@@ -11,8 +11,9 @@
 #include "chrome/common/url_constants.h"
 #include "components/sessions/content/content_live_tab.h"
 #include "content/public/browser/browser_thread.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/extensions/extension_metrics.h"
@@ -30,7 +31,7 @@
 namespace {
 
 void RecordAppLaunch(Profile* profile, const GURL& url) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   const extensions::Extension* extension =
       extensions::ExtensionRegistry::Get(profile)
           ->enabled_extensions()
@@ -40,7 +41,7 @@
 
   extensions::RecordAppLaunchType(
       extension_misc::APP_LAUNCH_NTP_RECENTLY_CLOSED, extension->GetType());
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 }
 
 }  // namespace
@@ -91,7 +92,7 @@
     sessions::LiveTab* tab) {
   std::string extension_app_id;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::TabHelper* extensions_tab_helper =
       extensions::TabHelper::FromWebContents(
           static_cast<sessions::ContentLiveTab*>(tab)->web_contents());
diff --git a/chrome/browser/sessions/session_tab_helper.cc b/chrome/browser/sessions/session_tab_helper.cc
index e67fd41..69011963 100644
--- a/chrome/browser/sessions/session_tab_helper.cc
+++ b/chrome/browser/sessions/session_tab_helper.cc
@@ -9,8 +9,9 @@
 #include "chrome/browser/sessions/session_service_factory.h"
 #include "chrome/common/features.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/common/extension_messages.h"
 #endif
 
@@ -26,7 +27,7 @@
 void SessionTabHelper::SetWindowID(const SessionID& id) {
   window_id_ = id;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Extension code in the renderer holds the ID of the window that hosts it.
   // Notify it that the window ID changed.
   web_contents()->SendToAllFrames(
diff --git a/chrome/browser/site_details.cc b/chrome/browser/site_details.cc
index 6b473ac..8ad341db 100644
--- a/chrome/browser/site_details.cc
+++ b/chrome/browser/site_details.cc
@@ -8,9 +8,10 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
+#include "extensions/features/features.h"
 #include "url/origin.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
@@ -40,7 +41,7 @@
       // extensions are isolated as well.
       return !site.SchemeIs(url::kHttpScheme);
     case ISOLATE_EXTENSIONS: {
-#if !defined(ENABLE_EXTENSIONS)
+#if !BUILDFLAG(ENABLE_EXTENSIONS)
       return false;
 #else
       if (!site.SchemeIs(extensions::kExtensionScheme))
diff --git a/chrome/browser/speech/chrome_speech_recognition_manager_delegate.cc b/chrome/browser/speech/chrome_speech_recognition_manager_delegate.cc
index c7f2a56..9bd20a7 100644
--- a/chrome/browser/speech/chrome_speech_recognition_manager_delegate.cc
+++ b/chrome/browser/speech/chrome_speech_recognition_manager_delegate.cc
@@ -30,13 +30,14 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/speech_recognition_error.h"
 #include "content/public/common/speech_recognition_result.h"
+#include "extensions/features/features.h"
 #include "net/url_request/url_request_context_getter.h"
 
 #if defined(OS_WIN)
 #include "chrome/installer/util/wmi.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_service.h"
 #include "extensions/browser/view_type_utils.h"
 #endif
@@ -333,7 +334,7 @@
     return;
   }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   WebContents* web_contents = WebContents::FromRenderViewHost(render_view_host);
   extensions::ViewType view_type = extensions::GetViewType(web_contents);
 
diff --git a/chrome/browser/ssl/cert_report_helper.cc b/chrome/browser/ssl/cert_report_helper.cc
index 092984b..a9622b1 100644
--- a/chrome/browser/ssl/cert_report_helper.cc
+++ b/chrome/browser/ssl/cert_report_helper.cc
@@ -13,6 +13,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ssl/ssl_cert_reporter.h"
 #include "chrome/common/pref_names.h"
@@ -108,6 +109,8 @@
   std::string serialized_report;
   certificate_reporting::ErrorReport report(request_url_.host(), ssl_info_);
 
+  report.AddNetworkTimeInfo(g_browser_process->network_time_tracker());
+
   report.SetInterstitialInfo(
       interstitial_reason_, user_proceeded,
       overridable_
diff --git a/chrome/browser/ssl/chrome_security_state_model_client.cc b/chrome/browser/ssl/chrome_security_state_model_client.cc
index 49d234e..a5072e6 100644
--- a/chrome/browser/ssl/chrome_security_state_model_client.cc
+++ b/chrome/browser/ssl/chrome_security_state_model_client.cc
@@ -158,9 +158,36 @@
   if (!sb_service)
     return;
   scoped_refptr<SafeBrowsingUIManager> sb_ui_manager = sb_service->ui_manager();
+  safe_browsing::SBThreatType threat_type;
   if (sb_ui_manager->IsUrlWhitelistedOrPendingForWebContents(
-          entry->GetURL(), false, entry, web_contents, false)) {
-    state->fails_malware_check = true;
+          entry->GetURL(), false, entry, web_contents, false, &threat_type)) {
+    switch (threat_type) {
+      case safe_browsing::SB_THREAT_TYPE_SAFE:
+        break;
+      case safe_browsing::SB_THREAT_TYPE_URL_PHISHING:
+      case safe_browsing::SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL:
+        state->malicious_content_status = security_state::SecurityStateModel::
+            MALICIOUS_CONTENT_STATUS_SOCIAL_ENGINEERING;
+        break;
+      case safe_browsing::SB_THREAT_TYPE_URL_MALWARE:
+      case safe_browsing::SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL:
+        state->malicious_content_status = security_state::SecurityStateModel::
+            MALICIOUS_CONTENT_STATUS_MALWARE;
+        break;
+      case safe_browsing::SB_THREAT_TYPE_URL_UNWANTED:
+        state->malicious_content_status = security_state::SecurityStateModel::
+            MALICIOUS_CONTENT_STATUS_UNWANTED_SOFTWARE;
+        break;
+      case safe_browsing::SB_THREAT_TYPE_BINARY_MALWARE_URL:
+      case safe_browsing::SB_THREAT_TYPE_EXTENSION:
+      case safe_browsing::SB_THREAT_TYPE_BLACKLISTED_RESOURCE:
+      case safe_browsing::SB_THREAT_TYPE_API_ABUSE:
+        // These threat types are not currently associated with
+        // interstitials, and thus resources with these threat types are
+        // not ever whitelisted or pending whitelisting.
+        NOTREACHED();
+        break;
+    }
   }
 }
 
diff --git a/chrome/browser/ssl/chrome_security_state_model_client_browser_tests.cc b/chrome/browser/ssl/chrome_security_state_model_client_browser_tests.cc
index 1c98e6f..25f615ac 100644
--- a/chrome/browser/ssl/chrome_security_state_model_client_browser_tests.cc
+++ b/chrome/browser/ssl/chrome_security_state_model_client_browser_tests.cc
@@ -1156,11 +1156,11 @@
   void ClearConsoleMessages() { console_messages_.clear(); }
 
   // content::WebContentsDelegate
-  bool AddMessageToConsole(content::WebContents* source,
-                           int32_t level,
-                           const base::string16& message,
-                           int32_t line_no,
-                           const base::string16& source_id) override {
+  bool DidAddMessageToConsole(content::WebContents* source,
+                              int32_t level,
+                              const base::string16& message,
+                              int32_t line_no,
+                              const base::string16& source_id) override {
     console_messages_.push_back(message);
     if (!console_message_callback_.is_null()) {
       console_message_callback_.Run();
diff --git a/chrome/browser/ssl/ssl_blocking_page.h b/chrome/browser/ssl/ssl_blocking_page.h
index 9b99e65..8ea320d 100644
--- a/chrome/browser/ssl/ssl_blocking_page.h
+++ b/chrome/browser/ssl/ssl_blocking_page.h
@@ -18,10 +18,11 @@
 #include "chrome/browser/ssl/ssl_cert_reporter.h"
 #include "components/certificate_reporting/error_report.h"
 #include "content/public/browser/certificate_request_result_type.h"
+#include "extensions/features/features.h"
 #include "net/ssl/ssl_info.h"
 #include "url/gurl.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 namespace extensions {
 class ExperienceSamplingEvent;
 }
diff --git a/chrome/browser/ssl/ssl_browser_tests.cc b/chrome/browser/ssl/ssl_browser_tests.cc
index 8b301357..a4f806f9 100644
--- a/chrome/browser/ssl/ssl_browser_tests.cc
+++ b/chrome/browser/ssl/ssl_browser_tests.cc
@@ -3045,7 +3045,7 @@
 
   void SetUpNetworkTimeServer() {
     field_trial_test()->SetNetworkQueriesWithVariationsService(
-        true, 0.0, network_time::FieldTrialTest::FETCHES_ON_DEMAND_ONLY);
+        true, 0.0, network_time::NetworkTimeTracker::FETCHES_ON_DEMAND_ONLY);
 
     // Install the URL interceptor that serves delayed network time
     // responses.
diff --git a/chrome/browser/ssl/ssl_error_handler_unittest.cc b/chrome/browser/ssl/ssl_error_handler_unittest.cc
index 152ff53..f31e10ce 100644
--- a/chrome/browser/ssl/ssl_error_handler_unittest.cc
+++ b/chrome/browser/ssl/ssl_error_handler_unittest.cc
@@ -203,7 +203,8 @@
     ChromeRenderViewHostTestHarness::SetUp();
 
     field_trial_test()->SetNetworkQueriesWithVariationsService(
-        false, 0.0, network_time::FieldTrialTest::FETCHES_IN_BACKGROUND_ONLY);
+        false, 0.0,
+        network_time::NetworkTimeTracker::FETCHES_IN_BACKGROUND_ONLY);
     tracker_.reset(new network_time::NetworkTimeTracker(
         std::unique_ptr<base::Clock>(clock_),
         std::unique_ptr<base::TickClock>(tick_clock_), &pref_service_,
@@ -451,7 +452,7 @@
   EXPECT_TRUE(test_server()->Start());
   tracker()->SetTimeServerURLForTesting(test_server()->GetURL("/"));
   field_trial_test()->SetNetworkQueriesWithVariationsService(
-      true, 0.0, network_time::FieldTrialTest::FETCHES_ON_DEMAND_ONLY);
+      true, 0.0, network_time::NetworkTimeTracker::FETCHES_ON_DEMAND_ONLY);
   error_handler()->StartHandlingError();
 
   EXPECT_TRUE(error_handler()->IsTimerRunning());
@@ -506,7 +507,7 @@
   EXPECT_TRUE(test_server()->Start());
   tracker()->SetTimeServerURLForTesting(test_server()->GetURL("/"));
   field_trial_test()->SetNetworkQueriesWithVariationsService(
-      true, 0.0, network_time::FieldTrialTest::FETCHES_ON_DEMAND_ONLY);
+      true, 0.0, network_time::NetworkTimeTracker::FETCHES_ON_DEMAND_ONLY);
   error_handler()->StartHandlingError();
   EXPECT_TRUE(error_handler()->IsTimerRunning());
   wait_for_time_query_loop.Run();
diff --git a/chrome/browser/supervised_user/supervised_user_service.cc b/chrome/browser/supervised_user/supervised_user_service.cc
index 68d0489..724097e 100644
--- a/chrome/browser/supervised_user/supervised_user_service.cc
+++ b/chrome/browser/supervised_user/supervised_user_service.cc
@@ -51,6 +51,7 @@
 #include "components/signin/core/common/signin_switches.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/user_metrics.h"
+#include "extensions/features/features.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if !defined(OS_ANDROID)
@@ -69,7 +70,7 @@
 #include "components/user_manager/user_manager.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_util.h"
 #include "extensions/browser/extension_prefs.h"
@@ -86,14 +87,14 @@
 using base::UserMetricsAction;
 using content::BrowserThread;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 using extensions::Extension;
 using extensions::ExtensionPrefs;
 using extensions::ExtensionRegistry;
 using extensions::ExtensionSystem;
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 using extensions::ExtensionPrefs;
 #endif
 
@@ -537,12 +538,12 @@
       did_init_(false),
       did_shutdown_(false),
       blacklist_state_(BlacklistLoadState::NOT_LOADED),
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
       registry_observer_(this),
 #endif
       weak_ptr_factory_(this) {
   url_filter_context_.ui_url_filter()->AddObserver(this);
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   registry_observer_.Add(extensions::ExtensionRegistry::Get(profile));
 #endif
 }
@@ -600,7 +601,7 @@
 
   GetSettingsService()->SetActive(active_);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   SetExtensionsActive();
 #endif
 
@@ -609,7 +610,7 @@
         prefs::kDefaultSupervisedUserFilteringBehavior,
         base::Bind(&SupervisedUserService::OnDefaultFilteringBehaviorChanged,
             base::Unretained(this)));
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     pref_change_registrar_.Add(
         prefs::kSupervisedUserApprovedExtensions,
         base::Bind(&SupervisedUserService::UpdateApprovedExtensions,
@@ -637,7 +638,7 @@
     UpdateManualHosts();
     UpdateManualURLs();
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     UpdateApprovedExtensions();
 #endif
 
@@ -652,7 +653,7 @@
 
     pref_change_registrar_.Remove(
         prefs::kDefaultSupervisedUserFilteringBehavior);
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     pref_change_registrar_.Remove(prefs::kSupervisedUserApprovedExtensions);
 #endif
     pref_change_registrar_.Remove(prefs::kSupervisedUserManualHosts);
@@ -990,7 +991,7 @@
     sync_service->RemovePreferenceProvider(this);
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 SupervisedUserService::ExtensionState SupervisedUserService::GetExtensionState(
     const Extension& extension) const {
   bool was_installed_by_default = extension.was_installed_by_default();
@@ -1240,7 +1241,7 @@
     extension_system->extension_service()->CheckManagementPolicy();
   }
 }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 syncer::ModelTypeSet SupervisedUserService::GetPreferredDataTypes() const {
   if (!ProfileIsSupervised())
diff --git a/chrome/browser/supervised_user/supervised_user_service.h b/chrome/browser/supervised_user/supervised_user_service.h
index 2487767..b896f63a5 100644
--- a/chrome/browser/supervised_user/supervised_user_service.h
+++ b/chrome/browser/supervised_user/supervised_user_service.h
@@ -30,9 +30,10 @@
 #include "components/prefs/pref_change_registrar.h"
 #include "components/sync/driver/sync_service_observer.h"
 #include "components/sync/driver/sync_type_preference_provider.h"
+#include "extensions/features/features.h"
 #include "net/url_request/url_request_context_getter.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/extension_registry_observer.h"
 #include "extensions/browser/management_policy.h"
 #endif
@@ -73,7 +74,7 @@
 // (e.g. the installed content packs, the default URL filtering behavior, or
 // manual whitelist/blacklist overrides).
 class SupervisedUserService : public KeyedService,
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
                               public extensions::ExtensionRegistryObserver,
                               public extensions::ManagementPolicy::Provider,
 #endif
@@ -306,7 +307,7 @@
 
   void OnCustodianInfoChanged();
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // extensions::ManagementPolicy::Provider implementation:
   std::string GetDebugPolicyProviderName() const override;
   bool UserMayLoad(const extensions::Extension* extension,
@@ -462,7 +463,7 @@
   // Used to report inappropriate URLs to SafeSarch API.
   std::unique_ptr<SafeSearchURLReporter> url_reporter_;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   ScopedObserver<extensions::ExtensionRegistry,
                  extensions::ExtensionRegistryObserver>
       registry_observer_;
diff --git a/chrome/browser/supervised_user/supervised_user_service_factory.cc b/chrome/browser/supervised_user/supervised_user_service_factory.cc
index 0564b92..113748d 100644
--- a/chrome/browser/supervised_user/supervised_user_service_factory.cc
+++ b/chrome/browser/supervised_user/supervised_user_service_factory.cc
@@ -10,8 +10,9 @@
 #include "chrome/browser/supervised_user/supervised_user_service.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/extension_system_provider.h"
 #include "extensions/browser/extensions_browser_client.h"
 #endif
@@ -37,7 +38,7 @@
     : BrowserContextKeyedServiceFactory(
         "SupervisedUserService",
         BrowserContextDependencyManager::GetInstance()) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   DependsOn(
       extensions::ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
 #endif
diff --git a/chrome/browser/supervised_user/supervised_user_service_unittest.cc b/chrome/browser/supervised_user/supervised_user_service_unittest.cc
index c17e938a..af98de1 100644
--- a/chrome/browser/supervised_user/supervised_user_service_unittest.cc
+++ b/chrome/browser/supervised_user/supervised_user_service_unittest.cc
@@ -38,9 +38,10 @@
 #include "components/version_info/version_info.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_utils.h"
+#include "extensions/features/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_service_test_base.h"
 #include "chrome/browser/extensions/extension_util.h"
@@ -365,7 +366,7 @@
   }
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 class SupervisedUserServiceExtensionTestBase
     : public extensions::ExtensionServiceTestBase {
  public:
@@ -682,4 +683,4 @@
   EXPECT_EQ(SupervisedUserURLFilter::ALLOW,
             url_filter->GetFilteringBehaviorForURL(moose_url));
 }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/supervised_user/supervised_user_url_filter.cc b/chrome/browser/supervised_user/supervised_user_url_filter.cc
index 0fd9c7d..56fee68 100644
--- a/chrome/browser/supervised_user/supervised_user_url_filter.cc
+++ b/chrome/browser/supervised_user/supervised_user_url_filter.cc
@@ -26,13 +26,14 @@
 #include "components/url_formatter/url_fixer.h"
 #include "components/url_matcher/url_matcher.h"
 #include "content/public/browser/browser_thread.h"
+#include "extensions/features/features.h"
 #include "net/base/escape.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/base/url_util.h"
 #include "url/gurl.h"
 #include "url/url_constants.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/common/extension_urls.h"
 #endif
 
@@ -92,7 +93,7 @@
   "wss"
 };
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 const char* kCrxDownloadUrls[] = {
     "https://clients2.googleusercontent.com/crx/blobs/",
     "https://chrome.google.com/webstore/download/"
@@ -349,7 +350,7 @@
   if (!HasFilteredScheme(effective_url))
     return ALLOW;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Allow webstore crx downloads. This applies to both extension installation
   // and updates.
   if (extension_urls::GetWebstoreUpdateUrl() == Normalize(effective_url))
diff --git a/chrome/browser/supervised_user/supervised_user_url_filter_unittest.cc b/chrome/browser/supervised_user/supervised_user_url_filter_unittest.cc
index ccd2f65..d825491f 100644
--- a/chrome/browser/supervised_user/supervised_user_url_filter_unittest.cc
+++ b/chrome/browser/supervised_user/supervised_user_url_filter_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/supervised_user/supervised_user_site_list.h"
+#include "extensions/features/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -519,7 +520,7 @@
   ASSERT_EQ(expected_whitelists, actual_whitelists);
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 TEST_F(SupervisedUserURLFilterTest, ChromeWebstoreDownloadsAreAlwaysAllowed) {
   // When installing an extension from Chrome Webstore, it tries to download the
   // crx file from "https://clients2.google.com/service/update2/", which
diff --git a/chrome/browser/sync/chrome_sync_client.cc b/chrome/browser/sync/chrome_sync_client.cc
index 2aa52e9..7551d44 100644
--- a/chrome/browser/sync/chrome_sync_client.cc
+++ b/chrome/browser/sync/chrome_sync_client.cc
@@ -64,6 +64,7 @@
 #include "components/sync_preferences/pref_service_syncable.h"
 #include "components/sync_sessions/sync_sessions_client.h"
 #include "content/public/browser/browser_thread.h"
+#include "extensions/features/features.h"
 #include "ui/base/device_form_factor.h"
 
 #if BUILDFLAG(ENABLE_APP_LIST)
@@ -72,7 +73,7 @@
 #include "ui/app_list/app_list_switches.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/api/storage/settings_sync_util.h"
 #include "chrome/browser/extensions/extension_sync_service.h"
 #include "chrome/browser/sync/glue/extension_data_type_controller.h"
@@ -109,7 +110,7 @@
 #endif
 
 using content::BrowserThread;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 using browser_sync::ExtensionDataTypeController;
 using browser_sync::ExtensionSettingDataTypeController;
 #endif
@@ -329,7 +330,7 @@
     }
     case syncer::SEARCH_ENGINES:
       return TemplateURLServiceFactory::GetForProfile(profile_)->AsWeakPtr();
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     case syncer::APPS:
     case syncer::EXTENSIONS:
       return ExtensionSyncService::Get(profile_)->AsWeakPtr();
@@ -514,7 +515,7 @@
   base::Closure error_callback =
       base::Bind(&syncer::ReportUnrecoverableError, chrome::GetChannel());
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // App sync is enabled by default.  Register unless explicitly
   // disabled.
   if (!disabled_types.Has(syncer::APPS)) {
@@ -550,7 +551,7 @@
             TemplateURLServiceFactory::GetForProfile(profile_)));
   }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Extension setting sync is enabled by default.  Register unless explicitly
   // disabled.
   if (!disabled_types.Has(syncer::EXTENSION_SETTINGS)) {
diff --git a/chrome/browser/sync/glue/extensions_activity_monitor.cc b/chrome/browser/sync/glue/extensions_activity_monitor.cc
index a3c735e..60d2284a 100644
--- a/chrome/browser/sync/glue/extensions_activity_monitor.cc
+++ b/chrome/browser/sync/glue/extensions_activity_monitor.cc
@@ -6,8 +6,9 @@
 
 #include "components/sync/base/extensions_activity.h"
 #include "content/public/browser/browser_thread.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/api/bookmarks/bookmarks_api.h"
 #include "content/public/browser/notification_service.h"
@@ -26,7 +27,7 @@
   // the fly so there is no reliable object to point to (same problem if we
   // wanted to use the string name).  Thus, we use all sources and filter in
   // Observe.
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   registrar_.Add(this,
                  extensions::NOTIFICATION_EXTENSION_BOOKMARKS_API_INVOKED,
                  content::NotificationService::AllSources());
@@ -41,7 +42,7 @@
     int type,
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(extensions::NOTIFICATION_EXTENSION_BOOKMARKS_API_INVOKED, type);
   const extensions::Extension* extension =
diff --git a/chrome/browser/sync/glue/extensions_activity_monitor.h b/chrome/browser/sync/glue/extensions_activity_monitor.h
index d8d2f4f..d7f8370 100644
--- a/chrome/browser/sync/glue/extensions_activity_monitor.h
+++ b/chrome/browser/sync/glue/extensions_activity_monitor.h
@@ -9,6 +9,7 @@
 #include "base/memory/ref_counted.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
+#include "extensions/features/features.h"
 
 namespace syncer {
 class ExtensionsActivity;
@@ -32,7 +33,7 @@
  private:
   scoped_refptr<syncer::ExtensionsActivity> extensions_activity_;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Used only on UI loop.
   content::NotificationRegistrar registrar_;
 #endif
diff --git a/chrome/browser/sync/profile_sync_service_factory.cc b/chrome/browser/sync/profile_sync_service_factory.cc
index 37da702..e36d7d8 100644
--- a/chrome/browser/sync/profile_sync_service_factory.cc
+++ b/chrome/browser/sync/profile_sync_service_factory.cc
@@ -41,9 +41,10 @@
 #include "components/sync/driver/startup_controller.h"
 #include "components/sync/driver/sync_util.h"
 #include "content/public/browser/browser_thread.h"
+#include "extensions/features/features.h"
 #include "url/gurl.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/extension_system_provider.h"
 #include "extensions/browser/extensions_browser_client.h"
 #endif
@@ -121,7 +122,7 @@
   DependsOn(ThemeServiceFactory::GetInstance());
 #endif
   DependsOn(WebDataServiceFactory::GetInstance());
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   DependsOn(
       extensions::ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
 #endif
diff --git a/chrome/browser/sync/sessions/notification_service_sessions_router.cc b/chrome/browser/sync/sessions/notification_service_sessions_router.cc
index 3245919..836ce30 100644
--- a/chrome/browser/sync/sessions/notification_service_sessions_router.cc
+++ b/chrome/browser/sync/sessions/notification_service_sessions_router.cc
@@ -22,6 +22,7 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/features/features.h"
 
 #if BUILDFLAG(ANDROID_JAVA_UI)
 #include "chrome/browser/android/tab_android.h"
@@ -32,7 +33,7 @@
 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/tab_helper.h"
 #endif
 
@@ -76,7 +77,7 @@
       content::NotificationService::AllSources());
   registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
       content::NotificationService::AllSources());
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   registrar_.Add(this,
       chrome::NOTIFICATION_TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED,
       content::NotificationService::AllSources());
@@ -147,7 +148,7 @@
         return;
       break;
     }
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     case chrome::NOTIFICATION_TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED: {
       extensions::TabHelper* extension_tab_helper =
           content::Source<extensions::TabHelper>(source).ptr();
diff --git a/chrome/browser/sync/sessions/sessions_sync_manager_unittest.cc b/chrome/browser/sync/sessions/sessions_sync_manager_unittest.cc
index 11e35ef3..886c2a4 100644
--- a/chrome/browser/sync/sessions/sessions_sync_manager_unittest.cc
+++ b/chrome/browser/sync/sessions/sessions_sync_manager_unittest.cc
@@ -823,7 +823,7 @@
   CreateAndActivateFieldTrial("trial name 2", "group name", kVariationId2,
                               variations::CHROME_SYNC_SERVICE);
   CreateAndActivateFieldTrial("trial name 3", "group name", kVariationId3,
-                              variations::GOOGLE_UPDATE_SERVICE);
+                              variations::GOOGLE_WEB_PROPERTIES);
 
   sessions::SessionTab session_tab;
   manager()->SetVariationIds(&session_tab);
diff --git a/chrome/browser/tab_contents/tab_util.cc b/chrome/browser/tab_contents/tab_util.cc
index bcc22d4..fbe9e9e 100644
--- a/chrome/browser/tab_contents/tab_util.cc
+++ b/chrome/browser/tab_contents/tab_util.cc
@@ -11,9 +11,10 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/site_instance.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/features/features.h"
 #include "url/gurl.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/extension_registry.h"
 #endif
 
@@ -49,7 +50,7 @@
   if (ChromeWebUIControllerFactory::GetInstance()->UseWebUIForURL(profile, url))
     return SiteInstance::CreateForURL(profile, url);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
    if (extensions::ExtensionRegistry::Get(
        profile)->enabled_extensions().GetHostedAppByURL(url))
      return SiteInstance::CreateForURL(profile, url);
diff --git a/chrome/browser/task_manager/providers/web_contents/extension_tag.cc b/chrome/browser/task_manager/providers/web_contents/extension_tag.cc
index 63577b99..b4ce5ed 100644
--- a/chrome/browser/task_manager/providers/web_contents/extension_tag.cc
+++ b/chrome/browser/task_manager/providers/web_contents/extension_tag.cc
@@ -5,8 +5,9 @@
 #include "chrome/browser/task_manager/providers/web_contents/extension_tag.h"
 
 #include "content/public/browser/web_contents.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/process_manager.h"
 #endif
 
diff --git a/chrome/browser/task_manager/web_contents_tags.cc b/chrome/browser/task_manager/web_contents_tags.cc
index 0a4f9240a..8352b7bd 100644
--- a/chrome/browser/task_manager/web_contents_tags.cc
+++ b/chrome/browser/task_manager/web_contents_tags.cc
@@ -15,9 +15,10 @@
 #include "components/guest_view/browser/guest_view_base.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/browser/view_type_utils.h"
+#include "extensions/features/features.h"
 #include "printing/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/process_manager.h"
 #endif
 
@@ -39,7 +40,7 @@
   WebContentsTagsManager::GetInstance()->AddTag(tag);
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 
 bool IsExtensionWebContents(content::WebContents* contents) {
   DCHECK(contents);
@@ -53,7 +54,7 @@
           view_type != extensions::VIEW_TYPE_BACKGROUND_CONTENTS);
 }
 
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 }  // namespace
 #endif  // defined(ENABLE_TASK_MANAGER)
@@ -135,7 +136,7 @@
 // static
 void WebContentsTags::CreateForExtension(content::WebContents* web_contents,
                                          extensions::ViewType view_type) {
-#if defined(ENABLE_TASK_MANAGER) && defined(ENABLE_EXTENSIONS)
+#if defined(ENABLE_TASK_MANAGER) && BUILDFLAG(ENABLE_EXTENSIONS)
   DCHECK(IsExtensionWebContents(web_contents));
 
   if (!WebContentsTag::FromWebContents(web_contents)) {
@@ -143,7 +144,7 @@
                    new ExtensionTag(web_contents, view_type),
                    WebContentsTag::kTagKey);
   }
-#endif  // defined(ENABLE_TASK_MANAGER) && defined(ENABLE_EXTENSIONS)
+#endif  // defined(ENABLE_TASK_MANAGER) && BUILDFLAG(ENABLE_EXTENSIONS)
 }
 
 // static
diff --git a/chrome/browser/themes/theme_service.cc b/chrome/browser/themes/theme_service.cc
index 9c31c0f..1dad1aa5 100644
--- a/chrome/browser/themes/theme_service.cc
+++ b/chrome/browser/themes/theme_service.cc
@@ -40,6 +40,7 @@
 #include "extensions/browser/uninstall_reason.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_set.h"
+#include "extensions/features/features.h"
 #include "ui/base/layout.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/color_palette.h"
@@ -47,7 +48,7 @@
 #include "ui/native_theme/common_theme.h"
 #include "ui/native_theme/native_theme.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/extension_registry_observer.h"
 #endif
 
@@ -156,7 +157,7 @@
 
 // ThemeService::ThemeObserver ------------------------------------------------
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 class ThemeService::ThemeObserver
     : public extensions::ExtensionRegistryObserver {
  public:
@@ -205,7 +206,7 @@
 
   ThemeService* theme_service_;
 };
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 
 // ThemeService ---------------------------------------------------------------
@@ -242,7 +243,7 @@
 }
 
 void ThemeService::Shutdown() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   theme_observer_.reset();
 #endif
 }
@@ -796,7 +797,7 @@
     NotifyThemeChanged();
   }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   theme_observer_.reset(new ThemeObserver(this));
 #endif
 
diff --git a/chrome/browser/themes/theme_service.h b/chrome/browser/themes/theme_service.h
index 2330a07d..c51900a3 100644
--- a/chrome/browser/themes/theme_service.h
+++ b/chrome/browser/themes/theme_service.h
@@ -21,6 +21,7 @@
 #include "components/keyed_service/core/keyed_service.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
+#include "extensions/features/features.h"
 #include "ui/base/theme_provider.h"
 
 class CustomThemeSupplier;
@@ -320,7 +321,7 @@
 
   std::unique_ptr<ThemeSyncableService> theme_syncable_service_;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   class ThemeObserver;
   std::unique_ptr<ThemeObserver> theme_observer_;
 #endif
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index c0eee776..e0e30694 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -8,6 +8,7 @@
 import("//build/config/ui.gni")
 import("//build/split_static_library.gni")
 import("//chrome/common/features.gni")
+import("//extensions/features/features.gni")
 import("//media/media_options.gni")
 import("//printing/features/features.gni")
 
@@ -231,6 +232,8 @@
     "webui/chromeos/keyboard_overlay_ui.h",
     "webui/chromeos/login/app_launch_splash_screen_handler.cc",
     "webui/chromeos/login/app_launch_splash_screen_handler.h",
+    "webui/chromeos/login/arc_terms_of_service_screen_handler.cc",
+    "webui/chromeos/login/arc_terms_of_service_screen_handler.h",
     "webui/chromeos/login/authenticated_user_email_retriever.cc",
     "webui/chromeos/login/authenticated_user_email_retriever.h",
     "webui/chromeos/login/auto_enrollment_check_screen_handler.cc",
@@ -563,6 +566,7 @@
     "//device/base",
     "//device/bluetooth/public/interfaces:experimental_interfaces",
     "//device/usb",
+    "//extensions/features",
     "//media",
     "//net:net_with_v8",
     "//printing/features",
diff --git a/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc b/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
index 885b86a5..534ed5de 100644
--- a/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
+++ b/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
@@ -192,9 +192,6 @@
 
   ash::WmShell::Get()->GetSessionStateDelegate()->AddSessionStateObserver(this);
 
-  if (CrasAudioHandler::IsInitialized())
-    CrasAudioHandler::Get()->AddAudioObserver(this);
-
   BrowserList::AddObserver(this);
 }
 
@@ -247,9 +244,6 @@
   ash::WmShell::Get()->GetSessionStateDelegate()->RemoveSessionStateObserver(
       this);
 
-  if (CrasAudioHandler::IsInitialized())
-    CrasAudioHandler::Get()->RemoveAudioObserver(this);
-
   BrowserList::RemoveObserver(this);
   StopObservingAppWindowRegistry();
   StopObservingCustodianInfoChanges();
@@ -880,36 +874,6 @@
   GetSystemTrayNotifier()->NotifyRefreshIME();
 }
 
-// Overridden from CrasAudioHandler::AudioObserver.
-void SystemTrayDelegateChromeOS::OnOutputNodeVolumeChanged(uint64_t node_id,
-                                                           int volume) {
-  GetSystemTrayNotifier()->NotifyAudioOutputVolumeChanged(node_id, volume);
-}
-
-void SystemTrayDelegateChromeOS::OnOutputMuteChanged(bool mute_on,
-                                                     bool system_adjust) {
-  GetSystemTrayNotifier()->NotifyAudioOutputMuteChanged(mute_on, system_adjust);
-}
-
-void SystemTrayDelegateChromeOS::OnInputNodeGainChanged(uint64_t /* node_id */,
-                                                        int /* gain */) {
-}
-
-void SystemTrayDelegateChromeOS::OnInputMuteChanged(bool /* mute_on */) {
-}
-
-void SystemTrayDelegateChromeOS::OnAudioNodesChanged() {
-  GetSystemTrayNotifier()->NotifyAudioNodesChanged();
-}
-
-void SystemTrayDelegateChromeOS::OnActiveOutputNodeChanged() {
-  GetSystemTrayNotifier()->NotifyAudioActiveOutputNodeChanged();
-}
-
-void SystemTrayDelegateChromeOS::OnActiveInputNodeChanged() {
-  GetSystemTrayNotifier()->NotifyAudioActiveInputNodeChanged();
-}
-
 // Overridden from BluetoothAdapter::Observer.
 void SystemTrayDelegateChromeOS::AdapterPresentChanged(
     device::BluetoothAdapter* adapter,
diff --git a/chrome/browser/ui/ash/system_tray_delegate_chromeos.h b/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
index e76d94e..90f776c7 100644
--- a/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
+++ b/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
@@ -27,7 +27,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/supervised_user/supervised_user_service_observer.h"
 #include "chrome/browser/ui/browser_list_observer.h"
-#include "chromeos/audio/cras_audio_handler.h"
 #include "chromeos/dbus/session_manager_client.h"
 #include "components/policy/core/common/cloud/cloud_policy_store.h"
 #include "components/prefs/pref_change_registrar.h"
@@ -59,7 +58,6 @@
       public SessionManagerClient::Observer,
       public content::NotificationObserver,
       public input_method::InputMethodManager::Observer,
-      public chromeos::CrasAudioHandler::AudioObserver,
       public device::BluetoothAdapter::Observer,
       public policy::CloudPolicyStore::Observer,
       public ash::SessionStateObserver,
@@ -184,15 +182,6 @@
   void InputMethodMenuItemChanged(
       ui::ime::InputMethodMenuManager* manager) override;
 
-  // Overridden from CrasAudioHandler::AudioObserver.
-  void OnOutputNodeVolumeChanged(uint64_t node_id, int volume) override;
-  void OnOutputMuteChanged(bool mute_on, bool system_adjust) override;
-  void OnInputNodeGainChanged(uint64_t node_id, int gain) override;
-  void OnInputMuteChanged(bool mute_on) override;
-  void OnAudioNodesChanged() override;
-  void OnActiveOutputNodeChanged() override;
-  void OnActiveInputNodeChanged() override;
-
   // Overridden from BluetoothAdapter::Observer.
   void AdapterPresentChanged(device::BluetoothAdapter* adapter,
                              bool present) override;
diff --git a/chrome/browser/ui/blocked_content/app_modal_dialog_helper.cc b/chrome/browser/ui/blocked_content/app_modal_dialog_helper.cc
index 0f83dd7b..a8ab05a 100644
--- a/chrome/browser/ui/blocked_content/app_modal_dialog_helper.cc
+++ b/chrome/browser/ui/blocked_content/app_modal_dialog_helper.cc
@@ -10,15 +10,16 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "components/guest_view/browser/guest_view_base.h"
 #endif
 
 AppModalDialogHelper::AppModalDialogHelper(content::WebContents* dialog_host)
     : popup_(nullptr) {
   content::WebContents* actual_host = dialog_host;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // If the dialog was triggered via an PDF, get the actual web contents that
   // embedds the PDF.
   guest_view::GuestViewBase* guest =
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
index d7c87bb..710a3714 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
@@ -54,6 +54,7 @@
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
+#include "extensions/features/features.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -585,7 +586,7 @@
   ASSERT_EQ(popup_browser, chrome::FindLastActive());
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #define MAYBE_ModalPopUnderViaGuestView DISABLED_ModalPopUnderViaGuestView
 #else
 #define MAYBE_ModalPopUnderViaGuestView ModalPopUnderViaGuestView
diff --git a/chrome/browser/ui/bookmarks/bookmark_utils.cc b/chrome/browser/ui/bookmarks/bookmark_utils.cc
index ffebaca..d2af2ee 100644
--- a/chrome/browser/ui/bookmarks/bookmark_utils.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_utils.cc
@@ -21,10 +21,11 @@
 #include "components/url_formatter/url_formatter.h"
 #include "components/user_prefs/user_prefs.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/features/features.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/base/dragdrop/drop_target_event.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/api/commands/command_service.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension_set.h"
@@ -59,7 +60,7 @@
 // Indicates how the bookmark shortcut has been changed by extensions associated
 // with |profile|, if at all.
 BookmarkShortcutDisposition GetBookmarkShortcutDisposition(Profile* profile) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::CommandService* command_service =
       extensions::CommandService::Get(profile);
 
@@ -168,7 +169,7 @@
 }
 
 bool ShouldRemoveBookmarkOpenPagesUI(Profile* profile) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::ExtensionRegistry* registry =
       extensions::ExtensionRegistry::Get(profile);
   if (!registry)
diff --git a/chrome/browser/ui/bookmarks/bookmark_utils_desktop.cc b/chrome/browser/ui/bookmarks/bookmark_utils_desktop.cc
index 6efe792..f92c545b 100644
--- a/chrome/browser/ui/bookmarks/bookmark_utils_desktop.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_utils_desktop.cc
@@ -31,11 +31,12 @@
 #include "components/url_formatter/url_formatter.h"
 #include "components/user_prefs/user_prefs.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/features/features.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/base/dragdrop/drop_target_event.h"
 #include "ui/base/l10n/l10n_util.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/api/commands/command_service.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension_set.h"
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index b5b54d0..29fbcd6 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -203,6 +203,7 @@
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/manifest_handlers/background_info.h"
+#include "extensions/features/features.h"
 #include "net/base/filename_util.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/cookies/cookie_monster.h"
@@ -2012,7 +2013,7 @@
   }
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 ///////////////////////////////////////////////////////////////////////////////
 // Browser, extensions::ExtensionRegistryObserver implementation:
 
@@ -2066,7 +2067,7 @@
     }
   }
 }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 ///////////////////////////////////////////////////////////////////////////////
 // Browser, translate::ContentTranslateDriver::Observer implementation:
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 8be6500..6cdff54 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -45,13 +45,14 @@
 #include "content/public/browser/page_navigator.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/common/page_zoom.h"
+#include "extensions/features/features.h"
 #include "ui/base/page_transition_types.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/shell_dialogs/select_file_dialog.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/extension_registry_observer.h"
 #endif
 
@@ -115,7 +116,7 @@
                 public zoom::ZoomObserver,
                 public content::PageNavigator,
                 public content::NotificationObserver,
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
                 public extensions::ExtensionRegistryObserver,
 #endif
                 public translate::ContentTranslateDriver::Observer,
@@ -729,7 +730,7 @@
                const content::NotificationSource& source,
                const content::NotificationDetails& details) override;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Overridden from extensions::ExtensionRegistryObserver:
   void OnExtensionUninstalled(content::BrowserContext* browser_context,
                               const extensions::Extension* extension,
@@ -871,7 +872,7 @@
 
   content::NotificationRegistrar registrar_;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   ScopedObserver<extensions::ExtensionRegistry,
                  extensions::ExtensionRegistryObserver>
       extension_registry_observer_;
diff --git a/chrome/browser/ui/browser_close_unittest.cc b/chrome/browser/ui/browser_close_unittest.cc
index 27f5fa8a..d605184 100644
--- a/chrome/browser/ui/browser_close_unittest.cc
+++ b/chrome/browser/ui/browser_close_unittest.cc
@@ -18,6 +18,7 @@
 #include "components/keyed_service/core/keyed_service.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "extensions/features/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 class TestingDownloadService : public DownloadService {
@@ -46,7 +47,7 @@
     return nullptr;
   }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::ExtensionDownloadsEventRouter* GetExtensionEventRouter()
       override {
     ADD_FAILURE();
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index c612bc7..89f35b6 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -83,11 +83,12 @@
 #include "content/public/common/url_constants.h"
 #include "content/public/common/url_utils.h"
 #include "content/public/common/user_agent.h"
+#include "extensions/features/features.h"
 #include "net/base/escape.h"
 #include "printing/features/features.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/api/commands/command_service.h"
 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
 #include "chrome/browser/extensions/tab_helper.h"
@@ -143,7 +144,7 @@
            !chrome::ShouldRemoveBookmarkThisPageUI(browser->profile()));
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 bool GetBookmarkOverrideCommand(
     Profile* profile,
     const extensions::Extension** extension,
@@ -457,7 +458,7 @@
 
   GURL url = browser->profile()->GetHomePage();
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // With bookmark apps enabled, hosted apps should return to their launch page
   // when the home button is pressed.
   if (browser->is_app()) {
@@ -523,7 +524,7 @@
       TabStripModel::ADD_FORCE_INDEX | TabStripModel::ADD_INHERIT_OPENER;
   Navigate(&params);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   DCHECK(extensions::ExtensionSystem::Get(
       browser->profile())->extension_service());
   const extensions::Extension* extension =
@@ -762,7 +763,7 @@
 void BookmarkCurrentPageAllowingExtensionOverrides(Browser* browser) {
   DCHECK(!chrome::ShouldRemoveBookmarkThisPageUI(browser->profile()));
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   const extensions::Extension* extension = NULL;
   extensions::Command command;
   extensions::CommandService::ExtensionCommandType command_type;
@@ -1258,7 +1259,7 @@
           CanViewSource();
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 void CreateApplicationShortcuts(Browser* browser) {
   content::RecordAction(UserMetricsAction("CreateShortcut"));
   extensions::TabHelper::FromWebContents(
@@ -1302,6 +1303,6 @@
   contents->GetRenderViewHost()->SyncRendererPrefs();
   app_browser->window()->Show();
 }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 }  // namespace chrome
diff --git a/chrome/browser/ui/browser_focus_uitest.cc b/chrome/browser/ui/browser_focus_uitest.cc
index 04eb665..d4805c4 100644
--- a/chrome/browser/ui/browser_focus_uitest.cc
+++ b/chrome/browser/ui/browser_focus_uitest.cc
@@ -417,16 +417,11 @@
   EXPECT_TRUE(focused_browser->window()->IsActive());
 }
 
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA)
-// TODO(erg): http://crbug.com/163931
-#define MAYBE_LocationBarLockFocus DISABLED_LocationBarLockFocus
-#else
-#define MAYBE_LocationBarLockFocus LocationBarLockFocus
-#endif
-
 // Page cannot steal focus when focus is on location bar.
-IN_PROC_BROWSER_TEST_F(BrowserFocusTest, MAYBE_LocationBarLockFocus) {
+IN_PROC_BROWSER_TEST_F(BrowserFocusTest, LocationBarLockFocus) {
   ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
+  ui_test_utils::BrowserActivationWaiter waiter(browser());
+  waiter.WaitForActivation();
 
   // Open the page that steals focus.
   const GURL url = embedded_test_server()->GetURL(kStealFocusPage);
@@ -565,16 +560,11 @@
   EXPECT_TRUE(IsViewFocused(VIEW_ID_OMNIBOX));
 }
 
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA)
-// TODO(erg): http://crbug.com/163931
-#define MAYBE_FocusOnReload DISABLED_FocusOnReload
-#else
-#define MAYBE_FocusOnReload FocusOnReload
-#endif
-
 // Tests that focus goes where expected when using reload.
-IN_PROC_BROWSER_TEST_F(BrowserFocusTest, MAYBE_FocusOnReload) {
+IN_PROC_BROWSER_TEST_F(BrowserFocusTest, FocusOnReload) {
   ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
+  ui_test_utils::BrowserActivationWaiter waiter(browser());
+  waiter.WaitForActivation();
 
   // Open the new tab, reload.
   {
@@ -688,18 +678,11 @@
   EXPECT_FALSE(IsViewFocused(VIEW_ID_OMNIBOX));
 }
 
-// This functionality is currently broken. http://crbug.com/304865.
-//
-// #if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA)
-// // TODO(erg): http://crbug.com/163931
-// #define MAYBE_FocusOnNavigate DISABLED_FocusOnNavigate
-// #else
-// #define MAYBE_FocusOnNavigate FocusOnNavigate
-// #endif
-
-IN_PROC_BROWSER_TEST_F(BrowserFocusTest, DISABLED_FocusOnNavigate) {
-  // Needed on Mac.
+IN_PROC_BROWSER_TEST_F(BrowserFocusTest, FocusOnNavigate) {
   ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
+  ui_test_utils::BrowserActivationWaiter waiter(browser());
+  waiter.WaitForActivation();
+
   // Load the NTP.
   ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
   EXPECT_TRUE(IsViewFocused(VIEW_ID_OMNIBOX));
diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc
index 66a92f9..df7d5b13 100644
--- a/chrome/browser/ui/browser_navigator.cc
+++ b/chrome/browser/ui/browser_navigator.cc
@@ -37,6 +37,7 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/features/features.h"
 
 #if defined(USE_ASH)
 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h"
@@ -47,7 +48,7 @@
 #include "ui/aura/window.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "extensions/browser/extension_registry.h"
@@ -156,7 +157,7 @@
       // Make a new popup window.
       // Coerce app-style if |source| represents an app.
       std::string app_name;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
       if (!params->extension_app_id.empty()) {
         app_name = web_app::GenerateApplicationNameFromExtensionId(
             params->extension_app_id);
@@ -376,7 +377,7 @@
   // tab helpers, so the entire set of tab helpers needs to be set up
   // immediately.
   BrowserNavigatorWebContentsAdoption::AttachTabHelpers(target_contents);
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::TabHelper::FromWebContents(target_contents)->
       SetExtensionAppById(params.extension_app_id);
 #endif
@@ -415,7 +416,7 @@
   if (!AdjustNavigateParamsForURL(params))
     return;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   const extensions::Extension* extension =
     extensions::ExtensionRegistry::Get(params->initiating_profile)->
         enabled_extensions().GetExtensionOrAppByURL(params->url);
diff --git a/chrome/browser/ui/cocoa/browser/exclusive_access_controller_views.mm b/chrome/browser/ui/cocoa/browser/exclusive_access_controller_views.mm
index 6bce229..2d1860c 100644
--- a/chrome/browser/ui/cocoa/browser/exclusive_access_controller_views.mm
+++ b/chrome/browser/ui/cocoa/browser/exclusive_access_controller_views.mm
@@ -95,7 +95,8 @@
 }
 
 void ExclusiveAccessController::UpdateFullscreenToolbar() {
-  [[controller_ fullscreenToolbarController] updateToolbarStyle];
+  [[controller_ fullscreenToolbarController]
+      updateToolbarStyleExitingTabFullscreen:NO];
 }
 
 // See the Fullscreen terminology section and the (Fullscreen) interface
diff --git a/chrome/browser/ui/cocoa/browser_window_controller.h b/chrome/browser/ui/cocoa/browser_window_controller.h
index f0e37b08e..6efec53 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller.h
+++ b/chrome/browser/ui/cocoa/browser_window_controller.h
@@ -131,6 +131,7 @@
   // Lazily created view which draws the background for the floating set of bars
   // in presentation mode (for window types having a floating bar; it remains
   // nil for those which don't).
+  // TODO(spqchan): Rename this to "fullscreenToolbarBackingView"
   base::scoped_nsobject<NSView> floatingBarBackingView_;
 
   // The borderless window used in fullscreen mode when Cocoa's System
diff --git a/chrome/browser/ui/cocoa/browser_window_controller.mm b/chrome/browser/ui/cocoa/browser_window_controller.mm
index 3b0a9ce..467d5f40 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller.mm
@@ -1824,7 +1824,9 @@
 - (void)updateUIForTabFullscreen:
     (ExclusiveAccessContext::TabFullscreenState)state {
   DCHECK([self isInAnyFullscreenMode]);
-  [fullscreenToolbarController_ updateToolbarStyle];
+  [fullscreenToolbarController_
+      updateToolbarStyleExitingTabFullscreen:
+          state == ExclusiveAccessContext::STATE_EXIT_TAB_FULLSCREEN];
 }
 
 - (void)updateFullscreenExitBubble {
diff --git a/chrome/browser/ui/cocoa/browser_window_controller_private.mm b/chrome/browser/ui/cocoa/browser_window_controller_private.mm
index ec6df0e..24c805e 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller_private.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller_private.mm
@@ -970,6 +970,8 @@
     [subviews addObject:[downloadShelfController_ view]];
   if ([self tabContentArea])
     [subviews addObject:[self tabContentArea]];
+  if ([infoBarContainerController_ view])
+    [subviews addObject:[infoBarContainerController_ view]];
   if ([self placeBookmarkBarBelowInfoBar]) {
     if ([bookmarkBarController_ view])
       [subviews addObject:[bookmarkBarController_ view]];
@@ -983,8 +985,6 @@
   }
   if ([toolbarController_ view])
     [subviews addObject:[toolbarController_ view]];
-  if ([infoBarContainerController_ view])
-    [subviews addObject:[infoBarContainerController_ view]];
   if ([findBarCocoaController_ view])
     [subviews addObject:[findBarCocoaController_ view]];
 
diff --git a/chrome/browser/ui/cocoa/browser_window_layout.mm b/chrome/browser/ui/cocoa/browser_window_layout.mm
index 05165b3..fde2e8d5 100644
--- a/chrome/browser/ui/cocoa/browser_window_layout.mm
+++ b/chrome/browser/ui/cocoa/browser_window_layout.mm
@@ -279,8 +279,14 @@
 
   // Lay out the toolbar.
   if (parameters.hasToolbar) {
-    output_.toolbarFrame = NSMakeRect(
-        0, maxY - parameters_.toolbarHeight, width, parameters_.toolbarHeight);
+    CGFloat toolbarY = maxY;
+    if (parameters_.inAnyFullscreen &&
+        parameters_.toolbarStyle == FullscreenToolbarStyle::TOOLBAR_NONE) {
+      toolbarY = parameters_.windowSize.height + fullscreenYOffset_;
+    }
+
+    output_.toolbarFrame = NSMakeRect(0, toolbarY - parameters_.toolbarHeight,
+                                      width, parameters_.toolbarHeight);
     maxY = NSMinY(output_.toolbarFrame);
   } else if (parameters_.hasLocationBar) {
     CGFloat toolbarX = kLocBarLeftRightInset;
diff --git a/chrome/browser/ui/cocoa/fullscreen_toolbar_controller.h b/chrome/browser/ui/cocoa/fullscreen_toolbar_controller.h
index cb95897..691022c 100644
--- a/chrome/browser/ui/cocoa/fullscreen_toolbar_controller.h
+++ b/chrome/browser/ui/cocoa/fullscreen_toolbar_controller.h
@@ -108,7 +108,7 @@
 
 // Updates the toolbar style. If the style has changed, then the toolbar will
 // relayout.
-- (void)updateToolbarStyle;
+- (void)updateToolbarStyleExitingTabFullscreen:(BOOL)isExitingTabFullscreen;
 
 // Updates the toolbar by updating the layout, menubar and dock.
 - (void)updateToolbar;
diff --git a/chrome/browser/ui/cocoa/fullscreen_toolbar_controller.mm b/chrome/browser/ui/cocoa/fullscreen_toolbar_controller.mm
index d1f8464..323680b 100644
--- a/chrome/browser/ui/cocoa/fullscreen_toolbar_controller.mm
+++ b/chrome/browser/ui/cocoa/fullscreen_toolbar_controller.mm
@@ -60,7 +60,7 @@
   DCHECK(!inFullscreenMode_);
   inFullscreenMode_ = YES;
 
-  [self updateToolbarStyle];
+  [self updateToolbarStyleExitingTabFullscreen:NO];
 
   if ([browserController_ isInImmersiveFullscreen]) {
     immersiveFullscreenController_.reset([[ImmersiveFullscreenController alloc]
@@ -104,10 +104,11 @@
   animationController_->AnimateToolbarForTabstripChanges();
 }
 
-- (void)updateToolbarStyle {
+- (void)updateToolbarStyleExitingTabFullscreen:(BOOL)isExitingTabFullscreen {
   FullscreenToolbarStyle oldStyle = toolbarStyle_;
 
-  if ([browserController_ isFullscreenForTabContentOrExtension]) {
+  if ([browserController_ isFullscreenForTabContentOrExtension] &&
+      !isExitingTabFullscreen) {
     toolbarStyle_ = FullscreenToolbarStyle::TOOLBAR_NONE;
   } else {
     PrefService* prefs = [browserController_ profile]->GetPrefs();
diff --git a/chrome/browser/ui/fast_unload_controller.cc b/chrome/browser/ui/fast_unload_controller.cc
index d516d89..73ee5dc 100644
--- a/chrome/browser/ui/fast_unload_controller.cc
+++ b/chrome/browser/ui/fast_unload_controller.cc
@@ -22,8 +22,9 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
+#include "extensions/features/features.h"
 
-#if defined (ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/constants.h"
 #endif  // (ENABLE_EXTENSIONS)
@@ -86,7 +87,7 @@
 
 bool FastUnloadController::RunUnloadEventsHelper(
     content::WebContents* contents) {
-#if defined (ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Don't run for extensions that are disabled or uninstalled; the tabs will
   // be killed if they make any network requests, and the extension shouldn't
   // be doing any work if it's removed.
diff --git a/chrome/browser/ui/login/login_handler.cc b/chrome/browser/ui/login/login_handler.cc
index ad085a9..4382d75f 100644
--- a/chrome/browser/ui/login/login_handler.cc
+++ b/chrome/browser/ui/login/login_handler.cc
@@ -32,6 +32,7 @@
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/origin_util.h"
+#include "extensions/features/features.h"
 #include "net/base/auth.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/load_flags.h"
@@ -43,7 +44,7 @@
 #include "ui/gfx/text_elider.h"
 #include "url/origin.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "components/guest_view/browser/guest_view_base.h"
 #include "extensions/browser/view_type_utils.h"
 #endif
@@ -548,7 +549,7 @@
       handler->GetPasswordManagerForLogin();
 
   if (!password_manager) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     // A WebContents in a <webview> (a GuestView type) does not have a password
     // manager, but still needs to be able to show login prompts.
     const auto* guest =
diff --git a/chrome/browser/ui/media_utils.cc b/chrome/browser/ui/media_utils.cc
index ae54995..bae2e2e 100644
--- a/chrome/browser/ui/media_utils.cc
+++ b/chrome/browser/ui/media_utils.cc
@@ -7,15 +7,16 @@
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/constants.h"
 #endif
 
 namespace {
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 const extensions::Extension* GetExtensionForOrigin(Profile* profile,
                                              const GURL& security_origin) {
   if (!security_origin.SchemeIs(extensions::kExtensionScheme))
@@ -37,7 +38,7 @@
     const content::MediaStreamRequest& request,
     const content::MediaResponseCallback& callback) {
   const extensions::Extension* extension = NULL;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extension = GetExtensionForOrigin(profile, request.security_origin);
 #endif
   MediaCaptureDevicesDispatcher::GetInstance()->ProcessMediaAccessRequest(
@@ -47,7 +48,7 @@
 bool CheckMediaAccessPermission(content::WebContents* web_contents,
                                 const GURL& security_origin,
                                 content::MediaStreamType type) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   Profile* profile =
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
   const extensions::Extension* extension =
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_edit_controller.cc b/chrome/browser/ui/omnibox/chrome_omnibox_edit_controller.cc
index a055973..706cddb4c 100644
--- a/chrome/browser/ui/omnibox/chrome_omnibox_edit_controller.cc
+++ b/chrome/browser/ui/omnibox/chrome_omnibox_edit_controller.cc
@@ -7,8 +7,9 @@
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/command_updater.h"
 #include "components/toolbar/toolbar_model.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/ui/extensions/settings_api_bubble_helpers.h"
 #endif
 
@@ -28,7 +29,7 @@
   if (command_updater_)
     command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::MaybeShowExtensionControlledSearchNotification(
       GetWebContents(), match_type);
 #endif
diff --git a/chrome/browser/ui/prefs/prefs_tab_helper.cc b/chrome/browser/ui/prefs/prefs_tab_helper.cc
index 4a529ec..e20d4e5c 100644
--- a/chrome/browser/ui/prefs/prefs_tab_helper.cc
+++ b/chrome/browser/ui/prefs/prefs_tab_helper.cc
@@ -44,6 +44,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/renderer_preferences.h"
 #include "content/public/common/web_preferences.h"
+#include "extensions/features/features.h"
 #include "third_party/icu/source/common/unicode/uchar.h"
 #include "third_party/icu/source/common/unicode/uscript.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -66,7 +67,7 @@
 
 // The list of prefs we want to observe.
 const char* const kPrefsToObserve[] = {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   prefs::kAnimationPolicy,
 #endif
   prefs::kDataSaverEnabled,
diff --git a/chrome/browser/ui/sync/profile_signin_confirmation_helper.cc b/chrome/browser/ui/sync/profile_signin_confirmation_helper.cc
index 4211a4a..af0431b 100644
--- a/chrome/browser/ui/sync/profile_signin_confirmation_helper.cc
+++ b/chrome/browser/ui/sync/profile_signin_confirmation_helper.cc
@@ -14,10 +14,11 @@
 #include "components/browser_sync/signin_confirmation_helper.h"
 #include "components/history/core/browser/history_service.h"
 #include "content/public/browser/browser_thread.h"
+#include "extensions/features/features.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/native_theme/native_theme.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/extensions/sync_helper.h"
 #include "extensions/browser/extension_registry.h"
@@ -60,7 +61,7 @@
 }
 
 bool HasSyncedExtensions(Profile* profile) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::ExtensionRegistry* registry =
       extensions::ExtensionRegistry::Get(profile);
   if (registry) {
diff --git a/chrome/browser/ui/sync/profile_signin_confirmation_helper_unittest.cc b/chrome/browser/ui/sync/profile_signin_confirmation_helper_unittest.cc
index 895fa7b4..177966a 100644
--- a/chrome/browser/ui/sync/profile_signin_confirmation_helper_unittest.cc
+++ b/chrome/browser/ui/sync/profile_signin_confirmation_helper_unittest.cc
@@ -33,6 +33,7 @@
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_utils.h"
+#include "extensions/features/features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -42,7 +43,7 @@
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/test_extension_system.h"
 #include "chrome/common/extensions/extension_constants.h"
@@ -91,7 +92,7 @@
   PrefReadError read_error_;
 };
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #if defined(OS_WIN)
 const base::FilePath::CharType kExtensionFilePath[] =
     FILE_PATH_LITERAL("c:\\foo");
@@ -147,7 +148,7 @@
     model_ = BookmarkModelFactory::GetForBrowserContext(profile_.get());
     bookmarks::test::WaitForBookmarkModelToLoad(model_);
     ASSERT_TRUE(profile_->CreateHistoryService(true, false));
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     extensions::TestExtensionSystem* system =
         static_cast<extensions::TestExtensionSystem*>(
             extensions::ExtensionSystem::Get(profile_.get()));
@@ -202,7 +203,7 @@
               profile_.get())));
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 TEST_F(ProfileSigninConfirmationHelperTest, PromptForNewProfile_Extensions) {
   ExtensionService* extensions =
       extensions::ExtensionSystem::Get(profile_.get())->extension_service();
diff --git a/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.cc b/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.cc
index 661a83d4..636367a 100644
--- a/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.cc
+++ b/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.cc
@@ -16,8 +16,9 @@
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/tab_helper.h"
 #include "extensions/common/extension.h"
 #endif
@@ -63,7 +64,7 @@
 }
 
 std::string TabContentsSyncedTabDelegate::GetExtensionAppId() const {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   const scoped_refptr<const extensions::Extension> extension_app(
       extensions::TabHelper::FromWebContents(web_contents_)->extension_app());
   if (extension_app.get())
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index 6ffb3654..3ad2e4a 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -59,6 +59,7 @@
 #include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
 #include "components/tracing/common/tracing_switches.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/features/features.h"
 #include "printing/features/features.h"
 
 #if BUILDFLAG(ANDROID_JAVA_UI)
@@ -92,7 +93,7 @@
 #include "chrome/browser/captive_portal/captive_portal_tab_helper.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/api/web_navigation/web_navigation_api.h"
 #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
 #include "chrome/browser/extensions/tab_helper.h"
@@ -133,7 +134,7 @@
   web_contents->SetUserData(&kTabContentsAttachedTabHelpersUserDataKey,
                             new base::SupportsUserData::Data());
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Set the view type.
   extensions::SetViewType(web_contents, extensions::VIEW_TYPE_TAB_CONTENTS);
 #endif
@@ -246,7 +247,7 @@
   CaptivePortalTabHelper::CreateForWebContents(web_contents);
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::TabHelper::CreateForWebContents(web_contents);
 #endif
 
diff --git a/chrome/browser/ui/toolbar/chrome_toolbar_model_delegate.cc b/chrome/browser/ui/toolbar/chrome_toolbar_model_delegate.cc
index dd72f24..56a1347 100644
--- a/chrome/browser/ui/toolbar/chrome_toolbar_model_delegate.cc
+++ b/chrome/browser/ui/toolbar/chrome_toolbar_model_delegate.cc
@@ -98,7 +98,8 @@
   security_state::SecurityStateModel::SecurityInfo security_info;
   ChromeSecurityStateModelClient::FromWebContents(web_contents)
       ->GetSecurityInfo(&security_info);
-  return security_info.fails_malware_check;
+  return security_info.malicious_content_status !=
+         security_state::SecurityStateModel::MALICIOUS_CONTENT_STATUS_NONE;
 }
 
 content::NavigationController*
diff --git a/chrome/browser/ui/unload_controller.cc b/chrome/browser/ui/unload_controller.cc
index 55bc854..8da5988 100644
--- a/chrome/browser/ui/unload_controller.cc
+++ b/chrome/browser/ui/unload_controller.cc
@@ -17,8 +17,9 @@
 #include "content/public/browser/notification_types.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/features/features.h"
 
-#if defined (ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/constants.h"
 #endif  // (ENABLE_EXTENSIONS)
@@ -56,7 +57,7 @@
 }
 
 bool UnloadController::RunUnloadEventsHelper(content::WebContents* contents) {
-#if defined (ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Don't run for extensions that are disabled or uninstalled; the tabs will
   // be killed if they make any network requests, and the extension shouldn't
   // be doing any work if it's removed.
diff --git a/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.cc b/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.cc
index c3085c4..6f7ca81 100644
--- a/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.cc
+++ b/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.cc
@@ -16,6 +16,8 @@
       content::WebUIDataSource::Create(chrome::kChromeUIBluetoothInternalsHost);
 
   // Add required resources.
+  html_source->AddResourcePath("adapter_broker.js",
+                               IDR_BLUETOOTH_INTERNALS_ADAPTER_BROKER_JS);
   html_source->AddResourcePath("bluetooth_internals.css",
                                IDR_BLUETOOTH_INTERNALS_CSS);
   html_source->AddResourcePath("bluetooth_internals.js",
@@ -26,8 +28,7 @@
                                IDR_BLUETOOTH_INTERNALS_DEVICE_TABLE_JS);
   html_source->AddResourcePath("interfaces.js",
                                IDR_BLUETOOTH_INTERNALS_INTERFACES_JS);
-  html_source->AddResourcePath("adapter_broker.js",
-                               IDR_BLUETOOTH_INTERNALS_ADAPTER_BROKER_JS);
+
   html_source->AddResourcePath(
       "device/bluetooth/public/interfaces/adapter.mojom",
       IDR_BLUETOOTH_ADAPTER_MOJO_JS);
diff --git a/chrome/browser/ui/webui/browsing_history_handler.cc b/chrome/browser/ui/webui/browsing_history_handler.cc
index f38ea8a..6f638f7738 100644
--- a/chrome/browser/ui/webui/browsing_history_handler.cc
+++ b/chrome/browser/ui/webui/browsing_history_handler.cc
@@ -58,10 +58,11 @@
 #include "components/url_formatter/url_formatter.h"
 #include "content/public/browser/url_data_source.h"
 #include "content/public/browser/web_ui.h"
+#include "extensions/features/features.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/time_format.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/activity_log/activity_log.h"
 #endif
 
@@ -628,7 +629,7 @@
                    weak_factory_.GetWeakPtr()));
   }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // If the profile has activity logging enabled also clean up any URLs from
   // the extension activity log. The extension activity log contains URLS
   // which websites an extension has activity on so it will indirectly
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 1a8e8d8..4d43555 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -79,6 +79,7 @@
 #include "content/public/browser/web_ui.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/url_utils.h"
+#include "extensions/features/features.h"
 #include "printing/features/features.h"
 #include "ui/gfx/favicon_size.h"
 #include "ui/web_dialogs/web_dialog_ui.h"
@@ -187,7 +188,7 @@
 #include "chrome/browser/ui/webui/app_list/start_page_ui.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_web_ui.h"
 #include "chrome/browser/ui/webui/extensions/extensions_ui.h"
 #include "chrome/common/extensions/extension_constants.h"
@@ -577,7 +578,7 @@
   if (url.host() == chrome::kChromeUIAppListStartPageHost)
     return &NewWebUI<app_list::StartPageUI>;
 #endif
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (url.host() == chrome::kChromeUIExtensionsFrameHost)
     return &NewWebUI<extensions::ExtensionsUI>;
 #endif
@@ -683,7 +684,7 @@
   // overrides. This changes urls in |kChromeUIScheme| to extension urls, and
   // allows to use ExtensionWebUI::GetFaviconForURL.
   GURL url(page_url);
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   ExtensionWebUI::HandleChromeURLOverride(&url, profile);
 
   // All extensions but the bookmark manager get their favicon from the icons
@@ -806,7 +807,7 @@
       page_url.host() == chrome::kChromeUIMdSettingsHost)
     return settings_utils::GetFaviconResourceBytes(scale_factor);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (page_url.host() == chrome::kChromeUIExtensionsHost ||
       page_url.host() == chrome::kChromeUIExtensionsFrameHost)
     return extensions::ExtensionsUI::GetFaviconResourceBytes(scale_factor);
diff --git a/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc
new file mode 100644
index 0000000..d486627
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc
@@ -0,0 +1,145 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h"
+
+#include "base/i18n/timezone.h"
+#include "chrome/browser/chromeos/arc/optin/arc_optin_preference_handler.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/login/localized_values_builder.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+const char kJsScreenPath[] = "login.ArcTermsOfServiceScreen";
+
+}  // namespace
+
+namespace chromeos {
+
+ArcTermsOfServiceScreenHandler::ArcTermsOfServiceScreenHandler()
+    : BaseScreenHandler(kJsScreenPath) {
+}
+
+ArcTermsOfServiceScreenHandler::~ArcTermsOfServiceScreenHandler() {
+  if (screen_)
+    screen_->OnActorDestroyed(this);
+}
+
+void ArcTermsOfServiceScreenHandler::RegisterMessages() {
+  AddCallback("arcTermsOfServiceSkip",
+              &ArcTermsOfServiceScreenHandler::HandleSkip);
+  AddCallback("arcTermsOfServiceAccept",
+              &ArcTermsOfServiceScreenHandler::HandleAccept);
+}
+
+void ArcTermsOfServiceScreenHandler::DeclareLocalizedValues(
+    ::login::LocalizedValuesBuilder* builder) {
+  builder->Add("arcTermsOfServiceScreenHeading", IDS_ARC_OOBE_TERMS_HEADING);
+  builder->Add("arcTermsOfServiceLoading", IDS_ARC_OOBE_TERMS_LOADING);
+  builder->Add("arcTermsOfServiceError", IDS_ARC_OOBE_TERMS_LOAD_ERROR);
+  builder->Add("arcTermsOfServiceSkipButton", IDS_ARC_OOBE_TERMS_BUTTON_SKIP);
+  builder->Add("arcTermsOfServiceRetryButton", IDS_ARC_OOBE_TERMS_BUTTON_RETRY);
+  builder->Add("arcTermsOfServiceAcceptButton",
+               IDS_ARC_OOBE_TERMS_BUTTON_ACCEPT);
+  builder->Add("arcPolicyLink", IDS_ARC_OPT_IN_PRIVACY_POLICY_LINK);
+  builder->Add("arcTextBackupRestore", IDS_ARC_OPT_IN_DIALOG_BACKUP_RESTORE);
+  builder->Add("arcTextLocationService", IDS_ARC_OPT_IN_LOCATION_SETTING);
+  builder->Add("arcLearnMoreStatistics", IDS_ARC_OPT_IN_LEARN_MORE_STATISTICS);
+  builder->Add("arcLearnMoreLocationService",
+      IDS_ARC_OPT_IN_LEARN_MORE_LOCATION_SERVICES);
+  builder->Add("arcLearnMoreBackupAndRestore",
+      IDS_ARC_OPT_IN_LEARN_MORE_BACKUP_AND_RESTORE);
+  builder->Add("arcOverlayClose", IDS_ARC_OOBE_TERMS_POPUP_HELP_CLOSE_BUTTON);
+  builder->Add("arcCountryCode", base::CountryCodeForCurrentTimezone());
+}
+
+void ArcTermsOfServiceScreenHandler::OnMetricsModeChanged(bool enabled,
+                                                          bool managed) {
+  int message_id;
+  const Profile* const profile = Profile::FromBrowserContext(
+      web_ui()->GetWebContents()->GetBrowserContext());
+  CHECK(profile);
+  const bool owner_profile = chromeos::ProfileHelper::IsOwnerProfile(profile);
+  if (managed || !owner_profile) {
+    message_id = enabled ?
+        IDS_ARC_OOBE_TERMS_DIALOG_METRICS_MANAGED_ENABLED :
+        IDS_ARC_OOBE_TERMS_DIALOG_METRICS_MANAGED_DISABLED;
+  } else {
+    message_id = enabled ?
+        IDS_ARC_OOBE_TERMS_DIALOG_METRICS_ENABLED :
+        IDS_ARC_OOBE_TERMS_DIALOG_METRICS_DISABLED;
+  }
+
+  const bool checkbox_enabled = !enabled && !managed && owner_profile;
+  CallJS("setMetricsMode", l10n_util::GetStringUTF16(message_id),
+      checkbox_enabled, enabled);
+}
+
+void ArcTermsOfServiceScreenHandler::OnBackupAndRestoreModeChanged(
+    bool enabled, bool managed) {
+  CallJS("setBackupAndRestoreMode", enabled, managed);
+}
+
+void ArcTermsOfServiceScreenHandler::OnLocationServicesModeChanged(
+    bool enabled, bool managed) {
+  CallJS("setLocationServicesMode", enabled, managed);
+}
+
+void ArcTermsOfServiceScreenHandler::SetDelegate(Delegate* screen) {
+  screen_ = screen;
+}
+
+void ArcTermsOfServiceScreenHandler::Show() {
+  if (!page_is_ready()) {
+    show_on_init_ = true;
+    return;
+  }
+
+  DoShow();
+}
+
+void ArcTermsOfServiceScreenHandler::Hide() {
+  pref_handler_.reset();
+}
+
+void ArcTermsOfServiceScreenHandler::Initialize() {
+  if (!show_on_init_)
+    return;
+
+  Show();
+  show_on_init_ = false;
+}
+
+void ArcTermsOfServiceScreenHandler::DoShow() {
+  Profile* profile = Profile::FromBrowserContext(
+      web_ui()->GetWebContents()->GetBrowserContext());
+  CHECK(profile);
+
+  pref_handler_.reset(new arc::ArcOptInPreferenceHandler(
+      this, profile->GetPrefs()));
+  pref_handler_->Start();
+  ShowScreen(OobeScreen::SCREEN_ARC_TERMS_OF_SERVICE);
+}
+
+void ArcTermsOfServiceScreenHandler::HandleSkip() {
+  if (screen_)
+    screen_->OnSkip();
+}
+
+void ArcTermsOfServiceScreenHandler::HandleAccept(
+    bool enable_metrics,
+    bool enable_backup_restore,
+    bool enable_location_services) {
+  if (screen_) {
+    screen_->OnAccept(
+        enable_metrics, enable_backup_restore, enable_location_services);
+  }
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h
new file mode 100644
index 0000000..7a90b79
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h
@@ -0,0 +1,72 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ARC_TERMS_OF_SERVICE_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ARC_TERMS_OF_SERVICE_SCREEN_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/arc/optin/arc_optin_preference_handler_observer.h"
+#include "chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+
+namespace arc {
+class ArcOptInPreferenceHandler;
+}
+
+namespace chromeos {
+
+class CoreOobeActor;
+
+// The sole implementation of the ArcTermsOfServiceScreenActor, using WebUI.
+class ArcTermsOfServiceScreenHandler :
+    public BaseScreenHandler,
+    public ArcTermsOfServiceScreenActor,
+    public arc::ArcOptInPreferenceHandlerObserver {
+ public:
+  ArcTermsOfServiceScreenHandler();
+  ~ArcTermsOfServiceScreenHandler() override;
+
+  // content::WebUIMessageHandler:
+  void RegisterMessages() override;
+
+  // BaseScreenHandler:
+  void DeclareLocalizedValues(
+      ::login::LocalizedValuesBuilder* builder) override;
+
+  // ArcTermsOfServiceScreenActor:
+  void SetDelegate(Delegate* screen) override;
+  void Show() override;
+  void Hide() override;
+
+ private:
+  // BaseScreenHandler:
+  void Initialize() override;
+
+  void DoShow();
+  void HandleSkip();
+  void HandleAccept(bool enable_metrics,
+                    bool enable_backup_restore,
+                    bool enable_location_services);
+
+  // arc::ArcOptInPreferenceHandlerObserver:
+  void OnMetricsModeChanged(bool enabled, bool managed) override;
+  void OnBackupAndRestoreModeChanged(bool enabled, bool managed) override;
+  void OnLocationServicesModeChanged(bool enabled, bool managed) override;
+
+  Delegate* screen_ = nullptr;
+
+  // Whether the screen should be shown right after initialization.
+  bool show_on_init_ = false;
+
+  std::unique_ptr<arc::ArcOptInPreferenceHandler> pref_handler_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArcTermsOfServiceScreenHandler);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ARC_TERMS_OF_SERVICE_SCREEN_HANDLER_H_
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_screen.cc b/chrome/browser/ui/webui/chromeos/login/oobe_screen.cc
index cbfcf31..46f96601 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_screen.cc
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_screen.cc
@@ -30,6 +30,7 @@
     "password-changed",                // SCREEN_PASSWORD_CHANGED
     "supervised-user-creation",        // SCREEN_CREATE_SUPERVISED_USER_FLOW
     "terms-of-service",                // SCREEN_TERMS_OF_SERVICE
+    "arc-tos",                         // SCREEN_ARC_TERMS_OF_SERVICE
     "wrong-hwid",                      // SCREEN_WRONG_HWID
     "auto-enrollment-check",           // SCREEN_AUTO_ENROLLMENT_CHECK
     "app-launch-splash",               // SCREEN_APP_LAUNCH_SPLASH
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_screen.h b/chrome/browser/ui/webui/chromeos/login/oobe_screen.h
index 60c04e4..111e011 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_screen.h
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_screen.h
@@ -29,6 +29,7 @@
   SCREEN_PASSWORD_CHANGED,
   SCREEN_CREATE_SUPERVISED_USER_FLOW,
   SCREEN_TERMS_OF_SERVICE,
+  SCREEN_ARC_TERMS_OF_SERVICE,
   SCREEN_WRONG_HWID,
   SCREEN_AUTO_ENROLLMENT_CHECK,
   SCREEN_APP_LAUNCH_SPLASH,
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
index 9e1a9c04..96beb8b 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/ui/ash/ash_util.h"
 #include "chrome/browser/ui/webui/about_ui.h"
 #include "chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/auto_enrollment_check_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/controller_pairing_screen_handler.h"
@@ -67,6 +68,7 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/chrome_unscaled_resources.h"
+#include "chrome/grit/component_extension_resources.h"
 #include "chromeos/chromeos_switches.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/prefs/pref_service.h"
@@ -113,6 +115,8 @@
 const char kEnrollmentHTMLPath[] = "enrollment.html";
 const char kEnrollmentCSSPath[] = "enrollment.css";
 const char kEnrollmentJSPath[] = "enrollment.js";
+const char kArcPlaystoreCSSPath[] = "playstore.css";
+const char kArcPlaystoreJSPath[] = "playstore.js";
 
 // Creates a WebUIDataSource for chrome://oobe
 content::WebUIDataSource* CreateOobeUIDataSource(
@@ -151,6 +155,11 @@
     source->AddResourcePath(kCustomElementsUserPodHTMLPath,
                             IDR_CUSTOM_ELEMENTS_USER_POD_HTML);
   }
+
+  // Required for postprocessing of Goolge PlayStore Terms.
+  source->AddResourcePath(kArcPlaystoreCSSPath, IDR_ARC_SUPPORT_PLAYSTORE_CSS);
+  source->AddResourcePath(kArcPlaystoreJSPath, IDR_ARC_SUPPORT_PLAYSTORE_JS);
+
   source->AddResourcePath(kKeyboardUtilsJSPath, IDR_KEYBOARD_UTILS_JS);
   source->OverrideContentSecurityPolicyChildSrc(
       base::StringPrintf(
@@ -284,6 +293,11 @@
   terms_of_service_screen_actor_ = terms_of_service_screen_handler;
   AddScreenHandler(terms_of_service_screen_handler);
 
+  ArcTermsOfServiceScreenHandler* arc_terms_of_service_screen_handler =
+      new ArcTermsOfServiceScreenHandler();
+  arc_terms_of_service_screen_actor_ = arc_terms_of_service_screen_handler;
+  AddScreenHandler(arc_terms_of_service_screen_handler);
+
   UserImageScreenHandler* user_image_screen_handler =
       new UserImageScreenHandler();
   user_image_view_ = user_image_screen_handler;
@@ -408,6 +422,10 @@
   return terms_of_service_screen_actor_;
 }
 
+ArcTermsOfServiceScreenActor* OobeUI::GetArcTermsOfServiceScreenActor() {
+  return arc_terms_of_service_screen_actor_;
+}
+
 WrongHWIDScreenActor* OobeUI::GetWrongHWIDScreenActor() {
   return wrong_hwid_screen_actor_;
 }
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.h b/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
index f6f91567..853fce7 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
@@ -29,6 +29,7 @@
 
 namespace chromeos {
 class AppLaunchSplashScreenActor;
+class ArcTermsOfServiceScreenActor;
 class AutoEnrollmentCheckScreenActor;
 class BaseScreenHandler;
 class ControllerPairingScreenActor;
@@ -102,6 +103,7 @@
   KioskAutolaunchScreenActor* GetKioskAutolaunchScreenActor();
   KioskEnableScreenActor* GetKioskEnableScreenActor();
   TermsOfServiceScreenActor* GetTermsOfServiceScreenActor();
+  ArcTermsOfServiceScreenActor* GetArcTermsOfServiceScreenActor();
   UserImageView* GetUserImageView();
   ErrorScreen* GetErrorScreen();
   WrongHWIDScreenActor* GetWrongHWIDScreenActor();
@@ -214,6 +216,7 @@
   SigninScreenHandler* signin_screen_handler_ = nullptr;
 
   TermsOfServiceScreenActor* terms_of_service_screen_actor_ = nullptr;
+  ArcTermsOfServiceScreenActor* arc_terms_of_service_screen_actor_ = nullptr;
   UserImageView* user_image_view_ = nullptr;
 
   std::vector<BaseScreenHandler*> handlers_;  // Non-owning pointers.
diff --git a/chrome/browser/ui/webui/constrained_web_dialog_ui.cc b/chrome/browser/ui/webui/constrained_web_dialog_ui.cc
index 1b88b6c..7be906ff 100644
--- a/chrome/browser/ui/webui/constrained_web_dialog_ui.cc
+++ b/chrome/browser/ui/webui/constrained_web_dialog_ui.cc
@@ -16,10 +16,11 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
+#include "extensions/features/features.h"
 #include "ui/web_dialogs/web_dialog_delegate.h"
 #include "ui/web_dialogs/web_dialog_ui.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/tab_helper.h"
 #endif
 
@@ -51,7 +52,7 @@
 
 ConstrainedWebDialogUI::ConstrainedWebDialogUI(content::WebUI* web_ui)
     : WebUIController(web_ui) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::TabHelper::CreateForWebContents(web_ui->GetWebContents());
 #endif
 }
diff --git a/chrome/browser/ui/webui/cookies_tree_model_util.cc b/chrome/browser/ui/webui/cookies_tree_model_util.cc
index 51d4b6a..0559445 100644
--- a/chrome/browser/ui/webui/cookies_tree_model_util.cc
+++ b/chrome/browser/ui/webui/cookies_tree_model_util.cc
@@ -20,12 +20,13 @@
 #include "content/public/browser/cache_storage_context.h"
 #include "content/public/browser/indexed_db_context.h"
 #include "content/public/browser/service_worker_context.h"
+#include "extensions/features/features.h"
 #include "net/cookies/canonical_cookie.h"
 #include "storage/common/fileapi/file_system_types.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/text/bytes_formatting.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/common/extension_set.h"
 #endif
 
@@ -37,7 +38,7 @@
 const char kKeyType[] = "type";
 const char kKeyHasChildren[] = "hasChildren";
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 const char kKeyAppsProtectingThis[] = "appsProtectingThis";
 #endif
 const char kKeyName[] = "name";
@@ -319,7 +320,7 @@
       break;
   }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   const extensions::ExtensionSet* protecting_apps =
       node.GetModel()->ExtensionsProtectingNode(node);
   if (protecting_apps && !protecting_apps->is_empty()) {
diff --git a/chrome/browser/ui/webui/log_web_ui_url.cc b/chrome/browser/ui/webui/log_web_ui_url.cc
index 5ba33ae..fe30ba2 100644
--- a/chrome/browser/ui/webui/log_web_ui_url.cc
+++ b/chrome/browser/ui/webui/log_web_ui_url.cc
@@ -11,9 +11,10 @@
 #include "base/metrics/histogram_macros.h"
 #include "chrome/common/url_constants.h"
 #include "content/public/common/url_constants.h"
+#include "extensions/features/features.h"
 #include "url/gurl.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/common/extensions/extension_constants.h"
 #include "extensions/common/constants.h"
 #endif
@@ -26,7 +27,7 @@
   bool should_log = web_ui_url.SchemeIs(content::kChromeUIScheme) ||
                     web_ui_url.SchemeIs(content::kChromeDevToolsScheme);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (web_ui_url.SchemeIs(extensions::kExtensionScheme))
     should_log = web_ui_url.host() == extension_misc::kBookmarkManagerId;
 #endif
diff --git a/chrome/browser/ui/webui/log_web_ui_url_browsertest.cc b/chrome/browser/ui/webui/log_web_ui_url_browsertest.cc
index f5f9dbc..9d754a1 100644
--- a/chrome/browser/ui/webui/log_web_ui_url_browsertest.cc
+++ b/chrome/browser/ui/webui/log_web_ui_url_browsertest.cc
@@ -22,6 +22,7 @@
 #include "components/strings/grit/components_strings.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
+#include "extensions/features/features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
@@ -64,7 +65,7 @@
   EXPECT_THAT(GetSamples(), ElementsAre(Bucket(history_frame_url_hash, 2)));
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 IN_PROC_BROWSER_TEST_F(LogWebUIUrlTest, TestUberPage) {
   content::WebContents* tab =
       browser()->tab_strip_model()->GetActiveWebContents();
diff --git a/chrome/browser/ui/webui/log_web_ui_url_unittest.cc b/chrome/browser/ui/webui/log_web_ui_url_unittest.cc
index d221099..76a735fc 100644
--- a/chrome/browser/ui/webui/log_web_ui_url_unittest.cc
+++ b/chrome/browser/ui/webui/log_web_ui_url_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/webui/log_web_ui_url.h"
 
+#include "extensions/features/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -17,7 +18,7 @@
   // Developer tools scheme.
   EXPECT_TRUE(webui::LogWebUIUrl(GURL("chrome-devtools://devtools")));
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Bookmarks Manager (the only currently allowed extension).
   EXPECT_TRUE(webui::LogWebUIUrl(GURL(
       "chrome-extension://eemcgdkfndhakfknompkggombfjjjeno")));
diff --git a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
index 33908b7..1e821dc 100644
--- a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
+++ b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
@@ -65,6 +65,7 @@
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/browser/web_ui_message_handler.h"
+#include "extensions/features/features.h"
 #include "net/base/net_errors.h"
 #include "net/disk_cache/disk_cache.h"
 #include "net/dns/host_cache.h"
@@ -100,7 +101,7 @@
 #include "chrome/browser/net/service_providers_win.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/ui/webui/extensions/extension_basic_info.h"
 #include "extensions/browser/extension_registry.h"
@@ -406,7 +407,7 @@
   proxy_->AddRequestContextGetter(
       content::BrowserContext::GetDefaultStoragePartition(profile)->
           GetMediaURLRequestContext());
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   proxy_->AddRequestContextGetter(profile->GetRequestContextForExtensions());
 #endif
 
@@ -571,7 +572,7 @@
     const base::ListValue* list) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   auto extension_list = base::MakeUnique<base::ListValue>();
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   Profile* profile = Profile::FromWebUI(web_ui());
   extensions::ExtensionSystem* extension_system =
       extensions::ExtensionSystem::Get(profile);
diff --git a/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc b/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc
index 4e05ea2e..f679a3c 100644
--- a/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc
+++ b/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc
@@ -151,10 +151,10 @@
 
 void OfflineInternalsUIMessageHandler::HandleRequestQueueCallback(
     std::string callback_id,
-    offline_pages::RequestQueue::GetRequestsResult result,
+    offline_pages::GetRequestsResult result,
     std::vector<std::unique_ptr<offline_pages::SavePageRequest>> requests) {
   base::ListValue save_page_requests;
-  if (result == offline_pages::RequestQueue::GetRequestsResult::SUCCESS) {
+  if (result == offline_pages::GetRequestsResult::SUCCESS) {
     for (const auto& request : requests) {
       base::DictionaryValue* save_page_request = new base::DictionaryValue();
       save_page_requests.Append(save_page_request);
diff --git a/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h b/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h
index 168e28f..7452677 100644
--- a/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h
+++ b/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h
@@ -13,6 +13,10 @@
 #include "components/offline_pages/offline_store_types.h"
 #include "content/public/browser/web_ui_message_handler.h"
 
+namespace offline_pages {
+enum class GetRequestsResult;
+}
+
 namespace offline_internals {
 
 // Class acting as a controller of the chrome://offline-internals WebUI.
@@ -63,7 +67,7 @@
   // Callback for async GetRequests calls.
   void HandleRequestQueueCallback(
       std::string callback_id,
-      offline_pages::RequestQueue::GetRequestsResult result,
+      offline_pages::GetRequestsResult result,
       std::vector<std::unique_ptr<offline_pages::SavePageRequest>> requests);
 
   // Callback for DeletePage/DeleteAllPages calls.
diff --git a/chrome/browser/ui/webui/options/browser_options_handler.cc b/chrome/browser/ui/webui/options/browser_options_handler.cc
index e1e0e6c..e88c707 100644
--- a/chrome/browser/ui/webui/options/browser_options_handler.cc
+++ b/chrome/browser/ui/webui/options/browser_options_handler.cc
@@ -1482,7 +1482,7 @@
 #if defined(OS_CHROMEOS)
 void BrowserOptionsHandler::UpdateAccountPicture() {
   std::string email = user_manager::UserManager::Get()
-                          ->GetLoggedInUser()
+                          ->GetActiveUser()
                           ->GetAccountId()
                           .GetUserEmail();
   if (!email.empty()) {
diff --git a/chrome/browser/ui/webui/policy_ui_handler.cc b/chrome/browser/ui/webui/policy_ui_handler.cc
index b0a20c2..e52cd30f 100644
--- a/chrome/browser/ui/webui/policy_ui_handler.cc
+++ b/chrome/browser/ui/webui/policy_ui_handler.cc
@@ -47,6 +47,7 @@
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "components/strings/grit/components_strings.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/features/features.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/time_format.h"
@@ -64,7 +65,7 @@
 #include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/manifest.h"
@@ -427,7 +428,7 @@
           Profile::FromWebUI(web_ui())->GetOriginalProfile())->registry();
   registry->RemoveObserver(this);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::ExtensionRegistry::Get(Profile::FromWebUI(web_ui()))
       ->RemoveObserver(this);
 #endif
@@ -477,7 +478,7 @@
         connector->GetDeviceLocalAccountPolicyService();
     if (local_account_service) {
       user_status_provider_.reset(new DeviceLocalAccountPolicyStatusProvider(
-          user_manager->GetLoggedInUser()->GetAccountId().GetUserEmail(),
+          user_manager->GetActiveUser()->GetAccountId().GetUserEmail(),
           local_account_service));
     }
   } else {
@@ -511,7 +512,7 @@
   GetPolicyService()->AddObserver(policy::POLICY_DOMAIN_CHROME, this);
   GetPolicyService()->AddObserver(policy::POLICY_DOMAIN_EXTENSIONS, this);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::ExtensionRegistry::Get(Profile::FromWebUI(web_ui()))
       ->AddObserver(this);
 #endif
@@ -529,7 +530,7 @@
                  base::Unretained(this)));
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 void PolicyUIHandler::OnExtensionLoaded(
     content::BrowserContext* browser_context,
     const extensions::Extension* extension) {
@@ -584,7 +585,7 @@
   }
   names.Set("chromePolicyNames", chrome_policy_names);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Add extension policy names.
   base::DictionaryValue* extension_policy_names = new base::DictionaryValue;
 
@@ -612,7 +613,7 @@
     extension_policy_names->Set(extension->id(), extension_value);
   }
   names.Set("extensionPolicyNames", extension_policy_names);
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
   web_ui()->CallJavascriptFunctionUnsafe("policy.Page.setPolicyNames", names);
 }
@@ -625,7 +626,7 @@
   GetChromePolicyValues(chrome_policies);
   all_policies.Set("chromePolicies", chrome_policies);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Add extension policy values.
   extensions::ExtensionRegistry* registry =
       extensions::ExtensionRegistry::Get(Profile::FromWebUI(web_ui()));
diff --git a/chrome/browser/ui/webui/policy_ui_handler.h b/chrome/browser/ui/webui/policy_ui_handler.h
index 7a59b81..0b34eac 100644
--- a/chrome/browser/ui/webui/policy_ui_handler.h
+++ b/chrome/browser/ui/webui/policy_ui_handler.h
@@ -20,8 +20,9 @@
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/browser/web_ui_message_handler.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/extension_registry_observer.h"
 #endif
 
@@ -34,7 +35,7 @@
 
 // The JavaScript message handler for the chrome://policy page.
 class PolicyUIHandler : public content::WebUIMessageHandler,
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
                         public extensions::ExtensionRegistryObserver,
 #endif
                         public policy::PolicyService::Observer,
@@ -54,7 +55,7 @@
   // content::WebUIMessageHandler implementation.
   void RegisterMessages() override;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // extensions::ExtensionRegistryObserver implementation.
   void OnExtensionLoaded(content::BrowserContext* browser_context,
                          const extensions::Extension* extension) override;
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index caec04d0..13bcfe07 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -1745,40 +1745,13 @@
 
 void AddLocalizedStrings(content::WebUIDataSource* html_source,
                          Profile* profile) {
-  AddCommonStrings(html_source, profile);
-
   AddA11yStrings(html_source);
   AddAboutStrings(html_source);
-#if defined(OS_CHROMEOS)
-  AddAccountUITweaksStrings(html_source, profile);
-#endif
   AddAppearanceStrings(html_source, profile);
-#if defined(OS_CHROMEOS)
-  AddBluetoothStrings(html_source);
-#endif
-#if defined(USE_NSS_CERTS)
-  AddCertificateManagerStrings(html_source);
-#endif
   AddClearBrowsingDataStrings(html_source);
-#if !defined(OS_CHROMEOS)
-  AddDefaultBrowserStrings(html_source);
-#endif
-#if defined(OS_CHROMEOS)
-  AddDateTimeStrings(html_source);
-  AddDeviceStrings(html_source);
-#endif
+  AddCommonStrings(html_source, profile);
   AddDownloadsStrings(html_source);
-
-#if defined(OS_CHROMEOS)
-  AddEasyUnlockStrings(html_source);
-  AddImportDataStrings(html_source);
-  AddInternetStrings(html_source);
-  AddCrNetworkStrings(html_source);
-#endif
   AddLanguagesStrings(html_source);
-#if defined(OS_CHROMEOS)
-  AddMultiProfilesStrings(html_source, profile);
-#endif
   AddOnStartupStrings(html_source);
   AddPasswordsAndFormsStrings(html_source);
   AddPeopleStrings(html_source);
@@ -1789,12 +1762,28 @@
   AddSearchInSettingsStrings(html_source);
   AddSearchStrings(html_source);
   AddSiteSettingsStrings(html_source, profile);
-#if !defined(OS_CHROMEOS)
-  AddSystemStrings(html_source);
-#endif
   AddUsersStrings(html_source);
   AddWebContentStrings(html_source);
 
+#if defined(OS_CHROMEOS)
+  AddAccountUITweaksStrings(html_source, profile);
+  AddBluetoothStrings(html_source);
+  AddCrNetworkStrings(html_source);
+  AddDateTimeStrings(html_source);
+  AddDeviceStrings(html_source);
+  AddEasyUnlockStrings(html_source);
+  AddImportDataStrings(html_source);
+  AddInternetStrings(html_source);
+  AddMultiProfilesStrings(html_source, profile);
+#else
+  AddSystemStrings(html_source);
+  AddDefaultBrowserStrings(html_source);
+#endif
+
+#if defined(USE_NSS_CERTS)
+  AddCertificateManagerStrings(html_source);
+#endif
+
   policy_indicator::AddLocalizedStrings(html_source);
 
   html_source->SetJsonPath(kLocalizedStringsFile);
diff --git a/chrome/browser/ui/webui/voice_search_ui.cc b/chrome/browser/ui/webui/voice_search_ui.cc
index 88f9f1a..6f1d09e 100644
--- a/chrome/browser/ui/webui/voice_search_ui.cc
+++ b/chrome/browser/ui/webui/voice_search_ui.cc
@@ -47,6 +47,7 @@
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/extension.h"
+#include "extensions/features/features.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "v8/include/v8.h"
 
@@ -260,7 +261,7 @@
     // platforms. ENABLE_EXTENSIONS covers those platforms and hey would not
     // allow Hotwording anyways since it is an extension.
     std::string nacl_enabled = "not available";
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     nacl_enabled = "No";
     // Determine if NaCl is available.
     base::FilePath path;
diff --git a/chrome/browser/ui/webui/webui_webview_browsertest.cc b/chrome/browser/ui/webui/webui_webview_browsertest.cc
index c9e4a29..12738dc 100644
--- a/chrome/browser/ui/webui/webui_webview_browsertest.cc
+++ b/chrome/browser/ui/webui/webui_webview_browsertest.cc
@@ -16,6 +16,7 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "chrome/test/base/web_ui_browser_test.h"
 #include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/context_menu_params.h"
 #include "content/public/common/drop_data.h"
@@ -323,16 +324,20 @@
 
   // Drag url into input in webview.
 
+  // TODO(paulmeyer): The following drag-and-drop calls on
+  // render_view_host->GetWidget() will need to be targeted to specific
+  // RenderWidgetHosts in order to work with OOPIFs. See crbug.com/647249.
+
   {
     EXPECT_TRUE(content::ExecuteScript(embedder_web_contents,
                                        "console.log('step1: Drag Enter')"));
 
     WebUIMessageListener listener(embedder_web_contents->GetWebUI(),
                                   "Step1: destNode gets dragenter");
-    render_view_host->FilterDropData(&dropdata);
-    render_view_host->DragTargetDragEnter(dropdata, client_pt, screen_pt,
-                                          drag_operation_mask,
-                                          blink::WebInputEvent::LeftButtonDown);
+    render_view_host->GetWidget()->FilterDropData(&dropdata);
+    render_view_host->GetWidget()->DragTargetDragEnter(
+        dropdata,client_pt, screen_pt, drag_operation_mask,
+        blink::WebInputEvent::LeftButtonDown);
     ASSERT_TRUE(listener.Wait());
   }
 
@@ -342,9 +347,9 @@
 
     WebUIMessageListener listener(embedder_web_contents->GetWebUI(),
                                   "Step2: destNode gets dragover");
-    render_view_host->DragTargetDragOver(client_pt, screen_pt,
-                                         drag_operation_mask,
-                                         blink::WebInputEvent::LeftButtonDown);
+    render_view_host->GetWidget()->DragTargetDragOver(
+        client_pt, screen_pt, drag_operation_mask,
+        blink::WebInputEvent::LeftButtonDown);
     ASSERT_TRUE(listener.Wait());
   }
 
@@ -355,7 +360,8 @@
     DNDToInputNavigationObserver observer(embedder_web_contents);
     WebUIMessageListener listener(embedder_web_contents->GetWebUI(),
                                   "Step3: destNode gets drop");
-    render_view_host->DragTargetDrop(dropdata, client_pt, screen_pt, 0);
+    render_view_host->GetWidget()->DragTargetDrop(
+        dropdata, client_pt, screen_pt, 0);
     ASSERT_TRUE(listener.Wait());
     // Confirm no navigation
     EXPECT_FALSE(observer.Navigated());
diff --git a/chrome/browser/web_applications/web_app_unittest.cc b/chrome/browser/web_applications/web_app_unittest.cc
index d88ed6b4..7043861 100644
--- a/chrome/browser/web_applications/web_app_unittest.cc
+++ b/chrome/browser/web_applications/web_app_unittest.cc
@@ -13,6 +13,7 @@
 #include "chrome/common/render_messages.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "content/public/test/test_renderer_host.h"
+#include "extensions/features/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if defined(TOOLKIT_VIEWS)
@@ -56,7 +57,7 @@
 }
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 TEST_F(WebApplicationTest, AppDirWithId) {
   base::FilePath profile_path(FILE_PATH_LITERAL("profile"));
   base::FilePath result(
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
index 8b1ec9ba..c3ac4847 100644
--- a/chrome/chrome_paks.gni
+++ b/chrome/chrome_paks.gni
@@ -4,6 +4,7 @@
 
 import("//build/config/locales.gni")
 import("//chrome/common/features.gni")
+import("//extensions/features/features.gni")
 import("//ui/base/ui_features.gni")
 import("chrome_repack_locales.gni")
 
diff --git a/chrome/chrome_repack_locales.gni b/chrome/chrome_repack_locales.gni
index ffe401e..d5bd6f9f 100644
--- a/chrome/chrome_repack_locales.gni
+++ b/chrome/chrome_repack_locales.gni
@@ -5,6 +5,7 @@
 import("//build/config/chrome_build.gni")
 import("//build/config/features.gni")
 import("//build/config/ui.gni")
+import("//extensions/features/features.gni")
 import("//tools/grit/repack.gni")
 
 # Wraps repack_locales(), setting the source_patterns and deps required for
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 96ac6a7d..443a67c 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -6,6 +6,7 @@
 import("//build/util/process_version.gni")
 import("//chrome/common/features.gni")
 import("//chrome/process_version_rc_template.gni")  # For branding_file_path.
+import("//extensions/features/features.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 import("//tools/grit/grit_rule.gni")
 
@@ -209,6 +210,7 @@
     "//content/public/common",
     "//crypto",
     "//extensions/common:common_constants",
+    "//extensions/features",
     "//gin",
     "//google_apis",
     "//gpu/command_buffer/service",
@@ -568,6 +570,7 @@
   public_deps = [
     ":features",
     "//content/public/common:result_codes",
+    "//extensions/features",
     "//printing/features",
   ]
   deps = [
diff --git a/chrome/common/DEPS b/chrome/common/DEPS
index 3778a909..a9a6105 100644
--- a/chrome/common/DEPS
+++ b/chrome/common/DEPS
@@ -26,6 +26,7 @@
   "+components/url_formatter",
   "+components/version_info",
   "+extensions/common",
+  "+extensions/features",
   "+gin/public",  # For profiling.cc
   "+google_apis/gaia",  # For gaia_switches.h
   "+media",
diff --git a/chrome/common/chrome_content_client.cc b/chrome/common/chrome_content_client.cc
index dca409a..bb7757b 100644
--- a/chrome/common/chrome_content_client.cc
+++ b/chrome/common/chrome_content_client.cc
@@ -41,6 +41,7 @@
 #include "content/public/common/url_constants.h"
 #include "content/public/common/user_agent.h"
 #include "extensions/common/constants.h"
+#include "extensions/features/features.h"
 #include "gpu/config/gpu_info.h"
 #include "net/http/http_util.h"
 #include "ppapi/features/features.h"
@@ -65,7 +66,7 @@
 #include "components/nacl/common/nacl_sandbox_type.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/common/extensions/extension_process_policy.h"
 #include "extensions/common/features/feature_util.h"
 #endif
@@ -657,7 +658,7 @@
 
 void ChromeContentClient::AddServiceWorkerSchemes(
     std::set<std::string>* schemes) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (extensions::feature_util::ExtensionServiceWorkersEnabled())
     schemes->insert(extensions::kExtensionScheme);
 #endif
@@ -665,7 +666,7 @@
 
 bool ChromeContentClient::AllowScriptExtensionForServiceWorker(
     const GURL& script_url) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return script_url.SchemeIs(extensions::kExtensionScheme) ||
          script_url.SchemeIs(extensions::kExtensionResourceScheme);
 #else
@@ -674,7 +675,7 @@
 }
 
 bool ChromeContentClient::IsSupplementarySiteIsolationModeEnabled() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return extensions::IsIsolateExtensionsEnabled();
 #else
   return false;
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 2bdf12b..c24c915a 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/common/chrome_features.h"
 
+#include "extensions/features/features.h"
+
 namespace features {
 
 // All features in alphabetical order.
@@ -107,7 +109,7 @@
 const base::Feature kMaterialDesignBookmarks{"MaterialDesignBookmarks",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 // Enabled or disabled the Material Design version of chrome://extensions.
 const base::Feature kMaterialDesignExtensions{
     "MaterialDesignExtensions", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 2f3b458..f7f347b 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -9,6 +9,7 @@
 #define CHROME_COMMON_CHROME_FEATURES_H_
 
 #include "base/feature_list.h"
+#include "extensions/features/features.h"
 #include "printing/features/features.h"
 
 namespace features {
@@ -68,7 +69,7 @@
 
 extern const base::Feature kMaterialDesignBookmarks;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 extern const base::Feature kMaterialDesignExtensions;
 #endif
 
diff --git a/chrome/common/common_message_generator.h b/chrome/common/common_message_generator.h
index f93f822..d0341fe4 100644
--- a/chrome/common/common_message_generator.h
+++ b/chrome/common/common_message_generator.h
@@ -15,9 +15,10 @@
 #include "chrome/common/tts_messages.h"
 #include "content/public/common/common_param_traits.h"
 #include "content/public/common/common_param_traits_macros.h"
+#include "extensions/features/features.h"
 #include "printing/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/common/cast_messages.h"
 #include "chrome/common/extensions/chrome_extension_messages.h"
 #include "chrome/common/extensions/chrome_utility_extensions_messages.h"
diff --git a/chrome/common/extensions/BUILD.gn b/chrome/common/extensions/BUILD.gn
index 4045134f..52c5a394 100644
--- a/chrome/common/extensions/BUILD.gn
+++ b/chrome/common/extensions/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/features.gni")
+import("//extensions/features/features.gni")
 import("//tools/json_schema_compiler/json_features.gni")
 
 assert(enable_extensions)
diff --git a/chrome/common/extensions/api/BUILD.gn b/chrome/common/extensions/api/BUILD.gn
index eebfa42..3517db4 100644
--- a/chrome/common/extensions/api/BUILD.gn
+++ b/chrome/common/extensions/api/BUILD.gn
@@ -6,6 +6,7 @@
 import("//build/config/ui.gni")
 import("//build/json_schema_api.gni")
 import("//chrome/common/features.gni")
+import("//extensions/features/features.gni")
 import("//tools/json_schema_compiler/json_features.gni")
 
 assert(enable_extensions)
diff --git a/chrome/common/extensions/chrome_extensions_client.cc b/chrome/common/extensions/chrome_extensions_client.cc
index b999102..2fe9459e 100644
--- a/chrome/common/extensions/chrome_extensions_client.cc
+++ b/chrome/common/extensions/chrome_extensions_client.cc
@@ -232,7 +232,7 @@
   // TODO(erikkay): This seems like the wrong test.  Shouldn't we we testing
   // against the store app extent?
   GURL store_url(extension_urls::GetWebstoreLaunchURL());
-  if (url.host() == store_url.host()) {
+  if (url.DomainIs(store_url.host())) {
     if (error)
       *error = manifest_errors::kCannotScriptGallery;
     return false;
diff --git a/chrome/common/extensions/chrome_utility_extensions_messages.h b/chrome/common/extensions/chrome_utility_extensions_messages.h
index 776f9e5..fcf59fb 100644
--- a/chrome/common/extensions/chrome_utility_extensions_messages.h
+++ b/chrome/common/extensions/chrome_utility_extensions_messages.h
@@ -14,10 +14,11 @@
 #include "chrome/common/media_galleries/itunes_library.h"
 #include "chrome/common/media_galleries/metadata_types.h"
 #include "chrome/common/media_galleries/picasa_types.h"
+#include "extensions/features/features.h"
 #include "ipc/ipc_message_macros.h"
 #include "ipc/ipc_platform_file.h"
 
-#if !defined(ENABLE_EXTENSIONS)
+#if !BUILDFLAG(ENABLE_EXTENSIONS)
 #error "Extensions must be enabled"
 #endif
 
diff --git a/chrome/common/extensions/permissions/permissions_data_unittest.cc b/chrome/common/extensions/permissions/permissions_data_unittest.cc
index 3f92679..5147af4 100644
--- a/chrome/common/extensions/permissions/permissions_data_unittest.cc
+++ b/chrome/common/extensions/permissions/permissions_data_unittest.cc
@@ -769,4 +769,67 @@
   EXPECT_TRUE(ScriptAllowedExclusivelyOnTab(extension.get(), no_urls, 2));
 }
 
+// Check that the webstore url is inaccessible.
+TEST(PermissionsDataTest, ChromeWebstoreUrl) {
+  scoped_refptr<const Extension> normal_extension =
+      GetExtensionWithHostPermission("all_hosts_normal_extension",
+                                     kAllHostsPermission, Manifest::INTERNAL);
+  scoped_refptr<const Extension> policy_extension =
+      GetExtensionWithHostPermission("all_hosts_policy_extension",
+                                     kAllHostsPermission,
+                                     Manifest::EXTERNAL_POLICY);
+  scoped_refptr<const Extension> unpacked_extension =
+      GetExtensionWithHostPermission("all_hosts_unpacked_extension",
+                                     kAllHostsPermission, Manifest::UNPACKED);
+  const Extension* extensions[] = {
+      normal_extension.get(), policy_extension.get(), unpacked_extension.get(),
+  };
+  const GURL kWebstoreUrls[] = {
+      GURL("https://chrome.google.com/webstore"),
+      GURL("https://chrome.google.com./webstore"),
+      GURL("https://chrome.google.com/webstore/category/extensions"),
+      GURL("https://chrome.google.com./webstore/category/extensions"),
+      GURL("https://chrome.google.com/webstore/search/foo"),
+      GURL("https://chrome.google.com./webstore/search/foo"),
+      GURL("https://chrome.google.com/webstore/detail/"
+           "empty-new-tab-page/dpjamkmjmigaoobjbekmfgabipmfilij"),
+      GURL("https://chrome.google.com./webstore/detail/"
+           "empty-new-tab-page/dpjamkmjmigaoobjbekmfgabipmfilij"),
+  };
+
+  const int kTabId = 1;
+  std::string error;
+  URLPatternSet tab_hosts;
+  tab_hosts.AddOrigin(UserScript::ValidUserScriptSchemes(),
+                      GURL("https://chrome.google.com/webstore").GetOrigin());
+  tab_hosts.AddOrigin(UserScript::ValidUserScriptSchemes(),
+                      GURL("https://chrome.google.com./webstore").GetOrigin());
+  PermissionSet tab_permissions(APIPermissionSet(), ManifestPermissionSet(),
+                                tab_hosts, tab_hosts);
+  for (const Extension* extension : extensions) {
+    // Give the extension activeTab permissions to run on the webstore - it
+    // shouldn't make a difference.
+    extension->permissions_data()->UpdateTabSpecificPermissions(
+        kTabId, tab_permissions);
+    for (const GURL& url : kWebstoreUrls) {
+      EXPECT_EQ(PermissionsData::ACCESS_DENIED,
+                extension->permissions_data()->GetPageAccess(extension, url, -1,
+                                                             &error))
+          << extension->name() << ": " << url;
+      EXPECT_EQ(PermissionsData::ACCESS_DENIED,
+                extension->permissions_data()->GetContentScriptAccess(
+                    extension, url, -1, &error))
+          << extension->name() << ": " << url;
+      EXPECT_EQ(PermissionsData::ACCESS_DENIED,
+                extension->permissions_data()->GetPageAccess(extension, url,
+                                                             kTabId, &error))
+          << extension->name() << ": " << url;
+      EXPECT_EQ(PermissionsData::ACCESS_DENIED,
+                extension->permissions_data()->GetContentScriptAccess(
+                    extension, url, kTabId, &error))
+          << extension->name() << ": " << url;
+    }
+  }
+}
+
 }  // namespace extensions
diff --git a/chrome/common/features.gni b/chrome/common/features.gni
index 6f90abe..9ceed89 100644
--- a/chrome/common/features.gni
+++ b/chrome/common/features.gni
@@ -6,6 +6,7 @@
 import("//build/config/chromecast_build.gni")
 import("//build/config/compiler/compiler.gni")
 import("//build/config/features.gni")
+import("//extensions/features/features.gni")
 import("//net/features.gni")
 import("//printing/features/features.gni")
 
@@ -75,11 +76,17 @@
   use_vulcanize = true
 }
 
+# Use brlapi from brltty for braille display support.
+use_brlapi = is_chromeos
+
 _enable_printing = enable_basic_printing || enable_print_preview
 
+# Every grit target in //chrome should apply these defines so that the
+# proper build flags can be set.
 chrome_grit_defines = [
   "enable_app_list=$enable_app_list",
   "enable_background=$enable_background",
+  "enable_extensions=$enable_extensions",
   "enable_google_now=$enable_google_now",
   "enable_hangout_services_extension=$enable_hangout_services_extension",
   "enable_hotwording=$enable_hotwording",
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 30e94ff..6a7249dd 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -8,6 +8,7 @@
 #include "build/build_config.h"
 #include "chrome/common/features.h"
 #include "chrome/common/pref_font_webkit_names.h"
+#include "extensions/features/features.h"
 
 namespace prefs {
 
@@ -1269,7 +1270,7 @@
 // in order to use Easy Unlock.
 const char kEasyUnlockProximityRequired[] = "easy_unlock.proximity_required";
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 // Used to indicate whether or not the toolbar redesign bubble has been shown
 // and acknowledged, and the last time the bubble was shown.
 const char kToolbarIconSurfacingBubbleAcknowledged[] =
@@ -2252,7 +2253,7 @@
 const char kRegisteredSupervisedUserWhitelists[] =
     "supervised_users.whitelists";
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 // Policy that indicates how to handle animated images.
 const char kAnimationPolicy[] = "settings.a11y.animation_policy";
 #endif
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 3656858..ea495ff6 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -11,6 +11,7 @@
 
 #include "build/build_config.h"
 #include "chrome/common/features.h"
+#include "extensions/features/features.h"
 
 namespace prefs {
 
@@ -433,7 +434,7 @@
 extern const char kEasyUnlockPairing[];
 extern const char kEasyUnlockProximityRequired[];
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 extern const char kToolbarIconSurfacingBubbleAcknowledged[];
 extern const char kToolbarIconSurfacingBubbleLastShowTime[];
 extern const char kToolbarMigratedComponentActionStatus[];
@@ -817,7 +818,7 @@
 extern const char kRestartInBackground[];
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 extern const char kAnimationPolicy[];
 #endif
 
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index 588d6f0d..9dc41aea 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -10,6 +10,7 @@
 #include "build/build_config.h"
 #include "chrome/common/features.h"
 #include "content/public/common/url_constants.h"
+#include "extensions/features/features.h"
 #include "printing/features/features.h"
 #include "url/url_util.h"
 
@@ -715,7 +716,7 @@
 #if !defined(DISABLE_NACL)
     kChromeUINaClHost,
 #endif
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     kChromeUIExtensionsHost,
 #endif
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index 47d60ab..637b99d9 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -4,6 +4,7 @@
 
 import("//build/config/features.gni")
 import("//chrome/common/features.gni")
+import("//extensions/features/features.gni")
 import("//tools/grit/grit_rule.gni")
 
 grit("resources") {
@@ -115,6 +116,7 @@
     "//content/app/strings",
     "//content/public/common",
     "//content/public/renderer",
+    "//extensions/features",
     "//media",
     "//media/capture",
     "//net",
@@ -370,6 +372,7 @@
   deps = [
     ":renderer",
     "//content/test:test_support",
+    "//extensions/features",
     "//testing/gmock",
     "//testing/gtest",
   ]
diff --git a/chrome/renderer/DEPS b/chrome/renderer/DEPS
index 3b6ae25..3485904 100644
--- a/chrome/renderer/DEPS
+++ b/chrome/renderer/DEPS
@@ -43,6 +43,7 @@
   "+content/public/common",
   "+content/public/renderer",
   "+extensions/common",
+  "+extensions/features",
   "+extensions/renderer",
   "+gin",
   "+media/base",
diff --git a/chrome/renderer/autofill/page_click_tracker_browsertest.cc b/chrome/renderer/autofill/page_click_tracker_browsertest.cc
index 9e1da05..a49ea6d 100644
--- a/chrome/renderer/autofill/page_click_tracker_browsertest.cc
+++ b/chrome/renderer/autofill/page_click_tracker_browsertest.cc
@@ -13,9 +13,13 @@
 #include "third_party/WebKit/public/platform/WebSize.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
 #include "third_party/WebKit/public/web/WebInputElement.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 "ui/events/keycodes/keyboard_codes.h"
+#include "ui/native_theme/native_theme_switches.h"
+
+using blink::WebRuntimeFeatures;
 
 namespace autofill {
 
@@ -48,6 +52,9 @@
   void SetUp() override {
     ChromeRenderViewTest::SetUp();
 
+    WebRuntimeFeatures::enableOverlayScrollbars(
+        ui::IsOverlayScrollbarEnabled());
+
     // RenderView creates PageClickTracker but it doesn't keep it around.
     // Rather than make it do so for the test, we create a new object.
     page_click_tracker_.reset(new PageClickTracker(view_->GetMainRenderFrame(),
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index b9c6c05a..3e4e68b 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -92,6 +92,7 @@
 #include "content/public/renderer/render_view.h"
 #include "content/public/renderer/render_view_visitor.h"
 #include "extensions/common/constants.h"
+#include "extensions/features/features.h"
 #include "ipc/ipc_sync_channel.h"
 #include "net/base/net_errors.h"
 #include "ppapi/c/private/ppb_pdf.h"
@@ -124,7 +125,7 @@
 #include "components/nacl/renderer/nacl_helper.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/common/extensions/chrome_extensions_client.h"
 #include "chrome/renderer/extensions/chrome_extensions_renderer_client.h"
 #include "extensions/common/extension_urls.h"
@@ -260,7 +261,7 @@
 }
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 bool IsStandaloneExtensionProcess() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
       extensions::switches::kExtensionProcess);
@@ -294,7 +295,7 @@
 
 ChromeContentRendererClient::ChromeContentRendererClient()
     : main_entry_time_(base::TimeTicks::Now()) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::ExtensionsClient::Set(
       extensions::ChromeExtensionsClient::GetInstance());
   extensions::ExtensionsRendererClient::Set(
@@ -327,7 +328,7 @@
   chrome_observer_.reset(new ChromeRenderThreadObserver());
   web_cache_impl_.reset(new web_cache::WebCacheImpl());
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   ChromeExtensionsRendererClient::GetInstance()->RenderThreadStarted();
 #endif
 
@@ -444,7 +445,7 @@
       base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kInstantProcess);
   extensions::Dispatcher* ext_dispatcher = NULL;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   ext_dispatcher =
       ChromeExtensionsRendererClient::GetInstance()->extension_dispatcher();
 #endif
@@ -455,7 +456,7 @@
         chrome_observer_->content_setting_rules());
   }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   ChromeExtensionsRendererClient::GetInstance()->RenderFrameCreated(
       render_frame);
 #endif
@@ -517,7 +518,7 @@
 
 void ChromeContentRendererClient::RenderViewCreated(
     content::RenderView* render_view) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   ChromeExtensionsRendererClient::GetInstance()->RenderViewCreated(render_view);
 #endif
 #if BUILDFLAG(ENABLE_PRINTING)
@@ -556,7 +557,7 @@
     const WebPluginParams& params,
     WebPlugin** plugin) {
   std::string orig_mime_type = params.mimeType.utf8();
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (!ChromeExtensionsRendererClient::GetInstance()->OverrideCreatePlugin(
           render_frame, params)) {
     return false;
@@ -691,7 +692,7 @@
       }
       case ChromeViewHostMsg_GetPluginInfo_Status::kAllowed:
       case ChromeViewHostMsg_GetPluginInfo_Status::kPlayImportantContent: {
-#if !defined(DISABLE_NACL) && defined(ENABLE_EXTENSIONS)
+#if !defined(DISABLE_NACL) && BUILDFLAG(ENABLE_EXTENSIONS)
         const bool is_nacl_plugin =
             info.name == ASCIIToUTF16(nacl::kNaClPluginName);
         const bool is_nacl_mime_type =
@@ -750,7 +751,7 @@
             break;
           }
         }
-#endif  // !defined(DISABLE_NACL) && defined(ENABLE_EXTENSIONS)
+#endif  // !defined(DISABLE_NACL) && BUILDFLAG(ENABLE_EXTENSIONS)
 
         // Delay loading plugins if prerendering.
         // TODO(mmenke):  In the case of prerendering, feed into
@@ -926,7 +927,7 @@
   bool is_invoked_by_webstore_installed_extension = false;
   bool is_extension_unrestricted = false;
   bool is_extension_force_installed = false;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   bool is_extension_from_webstore = extension && extension->from_webstore();
 
   bool is_invoked_by_extension = app_url.SchemeIs("chrome-extension");
@@ -944,7 +945,7 @@
   // Allow extensions force installed by admin policy.
   is_extension_force_installed = extension &&
        extensions::Manifest::IsPolicyLocation(extension->location());
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
   // Allow NaCl under any of the following circumstances:
   //  1) An app or URL is explictly whitelisted above.
@@ -1041,7 +1042,7 @@
 }
 
 bool ChromeContentRendererClient::RunIdleHandlerWhenWidgetsHidden() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return !IsStandaloneExtensionProcess();
 #else
   return true;
@@ -1058,7 +1059,7 @@
 }
 
 bool ChromeContentRendererClient::AllowPopup() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return ChromeExtensionsRendererClient::GetInstance()->AllowPopup();
 #else
   return false;
@@ -1105,12 +1106,12 @@
     return true;
   }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   bool should_fork = ChromeExtensionsRendererClient::ShouldFork(
       frame, url, is_initial_navigation, is_server_redirect, send_referrer);
   if (should_fork)
     return true;
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
   return false;
 }
@@ -1122,7 +1123,7 @@
     GURL* new_url) {
 // Check whether the request should be allowed. If not allowed, we reset the
 // URL to something invalid to prevent the request and cause an error.
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (ChromeExtensionsRendererClient::GetInstance()->WillSendRequest(
           frame, transition_type, url, new_url)) {
     return true;
@@ -1208,7 +1209,7 @@
   return module_name == "Native Client";
 }
 
-#if defined(ENABLE_PLUGINS) && defined(ENABLE_EXTENSIONS)
+#if defined(ENABLE_PLUGINS) && BUILDFLAG(ENABLE_EXTENSIONS)
 bool ChromeContentRendererClient::IsExtensionOrSharedModuleWhitelisted(
     const GURL& url, const std::set<std::string>& whitelist) {
   const extensions::ExtensionSet* extension_set =
@@ -1249,7 +1250,7 @@
 
 bool ChromeContentRendererClient::ShouldReportDetailedMessageForSource(
     const base::string16& source) const {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return extensions::IsSourceFromAnExtension(source);
 #else
   return false;
@@ -1262,7 +1263,7 @@
   // TODO(nick): https://crbug.com/268640 Gather stats for extension processes
   // too; we would need to check the extension's manifest to know which sites
   // it's allowed to access.
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   return !command_line->HasSwitch(extensions::switches::kExtensionProcess);
 #else
@@ -1297,7 +1298,7 @@
 
 bool ChromeContentRendererClient::IsPluginAllowedToUseCameraDeviceAPI(
     const GURL& url) {
-#if defined(ENABLE_PLUGINS) && defined(ENABLE_EXTENSIONS)
+#if defined(ENABLE_PLUGINS) && BUILDFLAG(ENABLE_EXTENSIONS)
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kEnablePepperTesting))
     return true;
@@ -1311,7 +1312,7 @@
 
 bool ChromeContentRendererClient::IsPluginAllowedToUseCompositorAPI(
     const GURL& url) {
-#if defined(ENABLE_PLUGINS) && defined(ENABLE_EXTENSIONS)
+#if defined(ENABLE_PLUGINS) && BUILDFLAG(ENABLE_EXTENSIONS)
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kEnablePepperTesting))
     return true;
@@ -1330,7 +1331,7 @@
     content::RenderFrame* render_frame,
     const std::string& mime_type,
     const GURL& original_url) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return ChromeExtensionsRendererClient::CreateBrowserPluginDelegate(
     render_frame, mime_type, original_url);
 #else
@@ -1369,7 +1370,7 @@
 
 void ChromeContentRendererClient::RunScriptsAtDocumentStart(
     content::RenderFrame* render_frame) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   ChromeExtensionsRendererClient::GetInstance()->RunScriptsAtDocumentStart(
       render_frame);
   // |render_frame| might be dead by now.
@@ -1378,7 +1379,7 @@
 
 void ChromeContentRendererClient::RunScriptsAtDocumentEnd(
     content::RenderFrame* render_frame) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   ChromeExtensionsRendererClient::GetInstance()->RunScriptsAtDocumentEnd(
       render_frame);
   // |render_frame| might be dead by now.
@@ -1390,7 +1391,7 @@
         v8::Local<v8::Context> context,
         int64_t service_worker_version_id,
         const GURL& url) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   ChromeExtensionsRendererClient::GetInstance()
       ->extension_dispatcher()
       ->DidInitializeServiceWorkerContextOnWorkerThread(
@@ -1402,7 +1403,7 @@
     v8::Local<v8::Context> context,
     int64_t service_worker_version_id,
     const GURL& url) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::Dispatcher::WillDestroyServiceWorkerContextOnWorkerThread(
       context, service_worker_version_id, url);
 #endif
@@ -1413,7 +1414,7 @@
 // information. Also, the enforcement of sending and binding UDP is already done
 // by chrome extension permission model.
 bool ChromeContentRendererClient::ShouldEnforceWebRTCRoutingPreferences() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return !IsStandaloneExtensionProcess();
 #else
   return true;
diff --git a/chrome/renderer/chrome_content_renderer_client.h b/chrome/renderer/chrome_content_renderer_client.h
index 36d6af5..1cf88d9 100644
--- a/chrome/renderer/chrome_content_renderer_client.h
+++ b/chrome/renderer/chrome_content_renderer_client.h
@@ -18,6 +18,7 @@
 #include "base/strings/string16.h"
 #include "components/rappor/public/interfaces/rappor_recorder.mojom.h"
 #include "content/public/renderer/content_renderer_client.h"
+#include "extensions/features/features.h"
 #include "ipc/ipc_channel_proxy.h"
 #include "printing/features/features.h"
 #include "v8/include/v8.h"
@@ -199,7 +200,7 @@
       const ChromeViewHostMsg_GetPluginInfo_Output& output);
 #endif
 
-#if defined(ENABLE_PLUGINS) && defined(ENABLE_EXTENSIONS)
+#if defined(ENABLE_PLUGINS) && BUILDFLAG(ENABLE_EXTENSIONS)
   static bool IsExtensionOrSharedModuleWhitelisted(
       const GURL& url, const std::set<std::string>& whitelist);
 #endif
diff --git a/chrome/renderer/chrome_content_renderer_client_unittest.cc b/chrome/renderer/chrome_content_renderer_client_unittest.cc
index 67856128..ce3ad61 100644
--- a/chrome/renderer/chrome_content_renderer_client_unittest.cc
+++ b/chrome/renderer/chrome_content_renderer_client_unittest.cc
@@ -17,12 +17,13 @@
 #include "chrome/renderer/searchbox/search_bouncer.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
 #include "content/public/common/webplugininfo.h"
+#include "extensions/features/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/WebKit/public/platform/WebString.h"
 #include "third_party/WebKit/public/platform/WebURLResponse.h"
 #include "url/gurl.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_builder.h"
 #include "extensions/common/manifest_constants.h"
@@ -52,7 +53,7 @@
 const bool kExtensionFromWebStore = true;
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 const bool kNotHostedApp = false;
 const bool kHostedApp = true;
 #endif
@@ -103,7 +104,7 @@
 typedef testing::Test ChromeContentRendererClientTest;
 
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 scoped_refptr<const extensions::Extension> CreateTestExtension(
     extensions::Manifest::Location location, bool is_from_webstore,
     bool is_hosted_app, const std::string& app_url) {
@@ -146,7 +147,7 @@
                              kHostedApp,
                              app_url);
 }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 TEST_F(ChromeContentRendererClientTest, NaClRestriction) {
   // Unknown content types have no NaCl module.
diff --git a/chrome/renderer/chrome_mock_render_thread.cc b/chrome/renderer/chrome_mock_render_thread.cc
index 683dacc..e560e18 100644
--- a/chrome/renderer/chrome_mock_render_thread.cc
+++ b/chrome/renderer/chrome_mock_render_thread.cc
@@ -5,9 +5,10 @@
 #include "chrome/renderer/chrome_mock_render_thread.h"
 
 #include "base/single_thread_task_runner.h"
+#include "extensions/features/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/common/extension_messages.h"
 #endif
 
@@ -32,7 +33,7 @@
     return true;
 
   // Some messages we do special handling.
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(ChromeMockRenderThread, msg)
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToExtension,
@@ -45,7 +46,7 @@
 #endif
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 void ChromeMockRenderThread::OnOpenChannelToExtension(
     int routing_id,
     const ExtensionMsg_ExternalConnectionInfo& info,
diff --git a/chrome/renderer/chrome_mock_render_thread.h b/chrome/renderer/chrome_mock_render_thread.h
index bc7901e4..a7e01c2 100644
--- a/chrome/renderer/chrome_mock_render_thread.h
+++ b/chrome/renderer/chrome_mock_render_thread.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "content/public/test/mock_render_thread.h"
+#include "extensions/features/features.h"
 
 struct ExtensionMsg_ExternalConnectionInfo;
 
@@ -33,7 +34,7 @@
   bool OnMessageReceived(const IPC::Message& msg) override;
 
  private:
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // The callee expects to be returned a valid channel_id.
   void OnOpenChannelToExtension(int routing_id,
                                 const ExtensionMsg_ExternalConnectionInfo& info,
diff --git a/chrome/renderer/chrome_render_thread_observer.cc b/chrome/renderer/chrome_render_thread_observer.cc
index fca7896..1f2faf8 100644
--- a/chrome/renderer/chrome_render_thread_observer.cc
+++ b/chrome/renderer/chrome_render_thread_observer.cc
@@ -42,6 +42,7 @@
 #include "content/public/renderer/render_thread.h"
 #include "content/public/renderer/render_view.h"
 #include "content/public/renderer/render_view_visitor.h"
+#include "extensions/features/features.h"
 #include "media/base/media_resources.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "net/base/net_errors.h"
@@ -53,7 +54,7 @@
 #include "third_party/WebKit/public/web/WebSecurityPolicy.h"
 #include "third_party/WebKit/public/web/WebView.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/renderer/extensions/extension_localization_peer.h"
 #endif
 
@@ -99,7 +100,7 @@
       std::unique_ptr<content::RequestPeer> current_peer,
       const std::string& mime_type,
       const GURL& url) override {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     return ExtensionLocalizationPeer::CreateExtensionLocalizationPeer(
         std::move(current_peer), RenderThread::Get(), mime_type, url);
 #else
diff --git a/chrome/renderer/chrome_render_view_observer.cc b/chrome/renderer/chrome_render_view_observer.cc
index ce76b98..b90084854 100644
--- a/chrome/renderer/chrome_render_view_observer.cc
+++ b/chrome/renderer/chrome_render_view_observer.cc
@@ -27,11 +27,12 @@
 #include "content/public/renderer/content_renderer_client.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_view.h"
+#include "extensions/features/features.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebView.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/common/extensions/chrome_extension_messages.h"
 #endif
 
@@ -55,7 +56,7 @@
 #if !defined(OS_ANDROID)
     IPC_MESSAGE_HANDLER(ChromeViewMsg_WebUIJavaScript, OnWebUIJavaScript)
 #endif
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     IPC_MESSAGE_HANDLER(ChromeViewMsg_SetVisuallyDeemphasized,
                         OnSetVisuallyDeemphasized)
 #endif
@@ -142,7 +143,7 @@
     web_cache_impl_->ExecutePendingClearCache();
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 void ChromeRenderViewObserver::OnSetVisuallyDeemphasized(bool deemphasized) {
   if (webview_visually_deemphasized_ == deemphasized)
     return;
diff --git a/chrome/renderer/chrome_render_view_observer.h b/chrome/renderer/chrome_render_view_observer.h
index 6d0e671..6f55ba9b 100644
--- a/chrome/renderer/chrome_render_view_observer.h
+++ b/chrome/renderer/chrome_render_view_observer.h
@@ -14,6 +14,7 @@
 #include "build/build_config.h"
 #include "content/public/common/browser_controls_state.h"
 #include "content/public/renderer/render_view_observer.h"
+#include "extensions/features/features.h"
 #include "url/gurl.h"
 
 class ContentSettingsObserver;
@@ -49,7 +50,7 @@
 #if !defined(OS_ANDROID)
   void OnWebUIJavaScript(const base::string16& javascript);
 #endif
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   void OnSetVisuallyDeemphasized(bool deemphasized);
 #endif
 #if defined(OS_ANDROID)
diff --git a/chrome/renderer/content_settings_observer.cc b/chrome/renderer/content_settings_observer.cc
index 76f37495..43850b2 100644
--- a/chrome/renderer/content_settings_observer.cc
+++ b/chrome/renderer/content_settings_observer.cc
@@ -10,6 +10,7 @@
 #include "content/public/renderer/document_state.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_view.h"
+#include "extensions/features/features.h"
 #include "third_party/WebKit/public/platform/URLConversion.h"
 #include "third_party/WebKit/public/platform/WebContentSettingCallbacks.h"
 #include "third_party/WebKit/public/platform/WebSecurityOrigin.h"
@@ -22,7 +23,7 @@
 #include "url/origin.h"
 #include "url/url_constants.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/permissions/api_permission.h"
@@ -88,7 +89,7 @@
     : content::RenderFrameObserver(render_frame),
       content::RenderFrameObserverTracker<ContentSettingsObserver>(
           render_frame),
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
       extension_dispatcher_(extension_dispatcher),
 #endif
       allow_running_insecure_content_(false),
@@ -348,7 +349,7 @@
 
 bool ContentSettingsObserver::allowReadFromClipboard(bool default_value) {
   bool allowed = default_value;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::ScriptContext* current_context =
       extension_dispatcher_->script_context_set().GetCurrent();
   if (current_context) {
@@ -361,7 +362,7 @@
 
 bool ContentSettingsObserver::allowWriteToClipboard(bool default_value) {
   bool allowed = default_value;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // All blessed extension pages could historically write to the clipboard, so
   // preserve that for compatibility.
   extensions::ScriptContext* current_context =
@@ -471,7 +472,7 @@
 }
 
 bool ContentSettingsObserver::IsPlatformApp() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   WebFrame* frame = render_frame()->GetWebFrame();
   WebSecurityOrigin origin = frame->document().getSecurityOrigin();
   const extensions::Extension* extension = GetExtension(origin);
@@ -481,7 +482,7 @@
 #endif
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 const extensions::Extension* ContentSettingsObserver::GetExtension(
     const WebSecurityOrigin& origin) const {
   if (!base::EqualsASCII(base::StringPiece16(origin.protocol()),
@@ -526,7 +527,7 @@
   if (base::EqualsASCII(protocol, content::kChromeDevToolsScheme))
     return true;  // DevTools UI elements should still work.
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (base::EqualsASCII(protocol, extensions::kExtensionScheme))
     return true;
 #endif
diff --git a/chrome/renderer/content_settings_observer.h b/chrome/renderer/content_settings_observer.h
index 6da3a29..0d284d9 100644
--- a/chrome/renderer/content_settings_observer.h
+++ b/chrome/renderer/content_settings_observer.h
@@ -16,6 +16,7 @@
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "content/public/renderer/render_frame_observer.h"
 #include "content/public/renderer/render_frame_observer_tracker.h"
+#include "extensions/features/features.h"
 #include "third_party/WebKit/public/web/WebContentSettingsClient.h"
 #include "url/gurl.h"
 
@@ -111,7 +112,7 @@
   // Whether the observed RenderFrame is for a platform app.
   bool IsPlatformApp();
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // If |origin| corresponds to an installed extension, returns that extension.
   // Otherwise returns NULL.
   const extensions::Extension* GetExtension(
@@ -126,7 +127,7 @@
       const blink::WebSecurityOrigin& origin,
       const GURL& document_url);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Owned by ChromeContentRendererClient and outlive us.
   extensions::Dispatcher* const extension_dispatcher_;
 #endif
diff --git a/chrome/renderer/content_settings_observer_unittest.cc b/chrome/renderer/content_settings_observer_unittest.cc
index 121dcd58..7614ddf 100644
--- a/chrome/renderer/content_settings_observer_unittest.cc
+++ b/chrome/renderer/content_settings_observer_unittest.cc
@@ -6,12 +6,13 @@
 
 #include "chrome/common/url_constants.h"
 #include "content/public/common/url_constants.h"
+#include "extensions/features/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/WebKit/public/platform/WebSecurityOrigin.h"
 #include "third_party/WebKit/public/platform/WebURL.h"
 #include "url/gurl.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/common/constants.h"
 #endif
 
@@ -34,7 +35,7 @@
       WebSecurityOrigin::create(chrome_dev_tools_url),
       GURL()));
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   GURL extension_url =
       GURL(std::string(extensions::kExtensionScheme).append(end_url));
   EXPECT_TRUE(ContentSettingsObserver::IsWhitelistedForContentSettings(
diff --git a/chrome/renderer/pepper/pepper_uma_host.cc b/chrome/renderer/pepper/pepper_uma_host.cc
index d9bd7a41..56bffc8 100644
--- a/chrome/renderer/pepper/pepper_uma_host.cc
+++ b/chrome/renderer/pepper/pepper_uma_host.cc
@@ -18,6 +18,7 @@
 #include "content/public/renderer/pepper_plugin_instance.h"
 #include "content/public/renderer/render_thread.h"
 #include "content/public/renderer/renderer_ppapi_host.h"
+#include "extensions/features/features.h"
 #include "ppapi/c/pp_errors.h"
 #include "ppapi/features/features.h"
 #include "ppapi/host/dispatch_host_message.h"
@@ -27,10 +28,10 @@
 
 #include "widevine_cdm_version.h"  // In SHARED_INTERMEDIATE_DIR.
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 namespace {
 
@@ -106,7 +107,7 @@
 }
 
 bool PepperUMAHost::IsPluginWhitelisted() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return ChromeContentRendererClient::IsExtensionOrSharedModuleWhitelisted(
       document_url_, allowed_origins_);
 #else
diff --git a/chrome/renderer/printing/chrome_print_web_view_helper_delegate.cc b/chrome/renderer/printing/chrome_print_web_view_helper_delegate.cc
index d20b8aa..07b03dc 100644
--- a/chrome/renderer/printing/chrome_print_web_view_helper_delegate.cc
+++ b/chrome/renderer/printing/chrome_print_web_view_helper_delegate.cc
@@ -14,16 +14,17 @@
 #include "chrome/renderer/prerender/prerender_helper.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_view.h"
+#include "extensions/features/features.h"
 #include "ipc/ipc_message.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
 #include "third_party/WebKit/public/web/WebElement.h"
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/common/extensions/extension_constants.h"
 #include "extensions/common/constants.h"
 #include "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.h"
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 ChromePrintWebViewHelperDelegate::~ChromePrintWebViewHelperDelegate(){
 }
@@ -41,7 +42,7 @@
 // Return the PDF object element if |frame| is the out of process PDF extension.
 blink::WebElement ChromePrintWebViewHelperDelegate::GetPdfElement(
         blink::WebLocalFrame* frame) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   GURL url = frame->document().url();
   bool inside_print_preview = url.GetOrigin() == chrome::kChromeUIPrintURL;
   bool inside_pdf_extension = url.SchemeIs(extensions::kExtensionScheme) &&
@@ -55,7 +56,7 @@
     }
     NOTREACHED();
   }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
   return blink::WebElement();
 }
 
@@ -66,7 +67,7 @@
 
 bool ChromePrintWebViewHelperDelegate::OverridePrint(
     blink::WebLocalFrame* frame) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (!frame->document().isPluginDocument())
     return false;
 
@@ -83,6 +84,6 @@
     mime_handlers.front()->PostMessageFromValue(message);
     return true;
   }
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
   return false;
 }
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index bda31843..90c11475 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -11,6 +11,7 @@
 import("//chrome/chrome_repack_locales.gni")
 import("//chrome/test/base/js2gtest.gni")
 import("//components/os_crypt/features.gni")
+import("//extensions/features/features.gni")
 import("//ppapi/features/features.gni")
 import("//remoting/remoting_enable.gni")
 import("//services/service_manager/public/service_manifest.gni")
@@ -121,6 +122,7 @@
     "//components/zoom:test_support",
     "//content/public/app:both",
     "//content/test:test_support",
+    "//extensions/features",
     "//net",
     "//net:test_support",
     "//printing/features",
@@ -487,6 +489,7 @@
       "//content/test:test_support",
       "//crypto:platform",
       "//crypto:test_support",
+      "//extensions/features",
       "//google_apis:test_support",
       "//net",
       "//net:net_resources",
@@ -1226,6 +1229,7 @@
     ":test_support",
     "//base",
     "//components/sync:test_support_model",
+    "//extensions/features",
     "//printing/features",
     "//third_party/WebKit/public:features",
   ]
@@ -3351,19 +3355,6 @@
     "../../third_party/zlib/google/compression_utils_unittest.cc",
     "../../third_party/zlib/google/zip_reader_unittest.cc",
     "../../third_party/zlib/google/zip_unittest.cc",
-    "../../tools/json_schema_compiler/test/additional_properties_unittest.cc",
-    "../../tools/json_schema_compiler/test/any_unittest.cc",
-    "../../tools/json_schema_compiler/test/arrays_unittest.cc",
-    "../../tools/json_schema_compiler/test/callbacks_unittest.cc",
-    "../../tools/json_schema_compiler/test/choices_unittest.cc",
-    "../../tools/json_schema_compiler/test/crossref_unittest.cc",
-    "../../tools/json_schema_compiler/test/enums_unittest.cc",
-    "../../tools/json_schema_compiler/test/error_generation_unittest.cc",
-    "../../tools/json_schema_compiler/test/functions_as_parameters_unittest.cc",
-    "../../tools/json_schema_compiler/test/functions_on_types_unittest.cc",
-    "../../tools/json_schema_compiler/test/idl_schemas_unittest.cc",
-    "../../tools/json_schema_compiler/test/objects_unittest.cc",
-    "../../tools/json_schema_compiler/test/simple_api_unittest.cc",
   ]
 
   configs += [ "//build/config:precompiled_headers" ]
@@ -3434,6 +3425,7 @@
     "//components/subresource_filter/core/browser:test_support",
     "//components/version_info:generate_version_info",
     "//courgette:courgette_lib",
+    "//extensions/features",
     "//google_apis",
     "//ppapi/features",
     "//skia",
@@ -3471,7 +3463,6 @@
       "//third_party/libjingle",
       "//third_party/libphonenumber",
       "//third_party/webrtc/modules/desktop_capture",
-      "//tools/json_schema_compiler/test:schema_test",
       "//ui/gl",
       "//v8",
     ]
@@ -4022,7 +4013,10 @@
       "//extensions:extensions_resources",
       "//extensions/strings",
       "//media/cast:test_support",
-      "//tools/json_schema_compiler/test:features_generation_test",
+
+      # This will add all of the unit tests for the schema compiler to this
+      # target.
+      "//tools/json_schema_compiler/test:unit_tests",
     ]
     if (enable_service_discovery) {
       sources += [
@@ -4974,6 +4968,7 @@
       "//components/about_handler",
       "//components/autofill/content/renderer:test_support",
       "//content/test:test_support",
+      "//extensions/features",
       "//media/cast:test_support",
       "//testing/gmock",
       "//testing/gtest",
@@ -5036,7 +5031,6 @@
 
 if (is_chromeos) {
   service_manifest("mash_browser_tests_manifest") {
-    type = "exe"
     name = "mash_browser_tests"
 
     source = "base/mash_browser_tests_manifest.json"
diff --git a/chrome/test/base/chrome_render_view_test.cc b/chrome/test/base/chrome_render_view_test.cc
index 94eb027..1e3cb36 100644
--- a/chrome/test/base/chrome_render_view_test.cc
+++ b/chrome/test/base/chrome_render_view_test.cc
@@ -20,6 +20,7 @@
 #include "content/public/browser/native_web_keyboard_event.h"
 #include "content/public/common/renderer_preferences.h"
 #include "content/public/renderer/render_view.h"
+#include "extensions/features/features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/WebKit/public/platform/WebURLRequest.h"
 #include "third_party/WebKit/public/web/WebFrame.h"
@@ -29,7 +30,7 @@
 #include "third_party/WebKit/public/web/WebScriptSource.h"
 #include "third_party/WebKit/public/web/WebView.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.h"
 #include "chrome/renderer/extensions/chrome_extensions_renderer_client.h"
 #include "extensions/browser/extension_function_dispatcher.h"
@@ -128,7 +129,7 @@
 
 void ChromeRenderViewTest::TearDown() {
   base::RunLoop().RunUntilIdle();
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   ChromeExtensionsRendererClient* ext_client =
       ChromeExtensionsRendererClient::GetInstance();
   ext_client->GetExtensionDispatcherForTest()->OnRenderProcessShutdown();
@@ -162,7 +163,7 @@
 
 void ChromeRenderViewTest::InitChromeContentRendererClient(
     ChromeContentRendererClient* client) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extension_dispatcher_delegate_.reset(
       new ChromeExtensionsDispatcherDelegate());
   ChromeExtensionsRendererClient* ext_client =
diff --git a/chrome/test/base/chrome_render_view_test.h b/chrome/test/base/chrome_render_view_test.h
index 6ee89e1..1f1067b3 100644
--- a/chrome/test/base/chrome_render_view_test.h
+++ b/chrome/test/base/chrome_render_view_test.h
@@ -10,6 +10,7 @@
 
 #include "chrome/renderer/chrome_mock_render_thread.h"
 #include "content/public/test/render_view_test.h"
+#include "extensions/features/features.h"
 
 class ChromeContentRendererClient;
 
@@ -53,7 +54,7 @@
   // class.
   virtual ChromeMockRenderThread* CreateMockRenderThread();
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   std::unique_ptr<extensions::DispatcherDelegate>
       extension_dispatcher_delegate_;
 #endif
diff --git a/chrome/test/base/chrome_unit_test_suite.cc b/chrome/test/base/chrome_unit_test_suite.cc
index 7a8a513..5e16658 100644
--- a/chrome/test/base/chrome_unit_test_suite.cc
+++ b/chrome/test/base/chrome_unit_test_suite.cc
@@ -19,6 +19,7 @@
 #include "components/component_updater/component_updater_paths.h"
 #include "components/update_client/update_query_params.h"
 #include "content/public/common/content_paths.h"
+#include "extensions/features/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/resource/resource_handle.h"
@@ -29,7 +30,7 @@
 #include "chromeos/chromeos_paths.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/common/extensions/chrome_extensions_client.h"
 #include "extensions/common/extension_paths.h"
 #endif
@@ -134,7 +135,7 @@
   chromeos::RegisterPathProvider();
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::RegisterPathProvider();
 
   extensions::ExtensionsClient::Set(
diff --git a/chrome/test/base/mash_browser_tests_manifest.json b/chrome/test/base/mash_browser_tests_manifest.json
index 23536e38..ef16fbf7 100644
--- a/chrome/test/base/mash_browser_tests_manifest.json
+++ b/chrome/test/base/mash_browser_tests_manifest.json
@@ -1,5 +1,5 @@
 {
-  "name": "exe:mash_browser_tests",
+  "name": "service:mash_browser_tests",
   "display_name": "Mash Browser Tests",
   "interface_provider_specs": {
     "service_manager:connector": {
diff --git a/chrome/test/base/mojo_test_connector.cc b/chrome/test/base/mojo_test_connector.cc
index 1f8107c7..9c26a724 100644
--- a/chrome/test/base/mojo_test_connector.cc
+++ b/chrome/test/base/mojo_test_connector.cc
@@ -30,7 +30,7 @@
 
 namespace {
 
-const char kTestRunnerName[] = "exe:mash_browser_tests";
+const char kTestRunnerName[] = "service:mash_browser_tests";
 const char kTestName[] = "service:content_browser";
 
 // BackgroundTestState maintains all the state necessary to bind the test to
diff --git a/chrome/test/base/testing_browser_process.cc b/chrome/test/base/testing_browser_process.cc
index 84aa188..72409a87 100644
--- a/chrome/test/base/testing_browser_process.cc
+++ b/chrome/test/base/testing_browser_process.cc
@@ -24,6 +24,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/subresource_filter/core/browser/ruleset_service.h"
 #include "content/public/browser/notification_service.h"
+#include "extensions/features/features.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "printing/features/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -33,7 +34,7 @@
 #include "chrome/browser/background/background_mode_manager.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/chrome_extensions_browser_client.h"
 #include "chrome/browser/media_galleries/media_file_system_registry.h"
 #include "chrome/browser/ui/apps/chrome_app_window_client.h"
@@ -74,7 +75,7 @@
       system_request_context_(nullptr),
       rappor_service_(nullptr),
       platform_part_(new TestingBrowserProcessPlatformPart()) {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions_browser_client_.reset(
       new extensions::ChromeExtensionsBrowserClient);
   extensions::AppWindowClient::Set(ChromeAppWindowClient::GetInstance());
@@ -85,7 +86,7 @@
 TestingBrowserProcess::~TestingBrowserProcess() {
   EXPECT_FALSE(local_state_);
   ShutdownBrowserPolicyConnector();
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::ExtensionsBrowserClient::Set(nullptr);
 #endif
 
diff --git a/chrome/test/base/testing_browser_process.h b/chrome/test/base/testing_browser_process.h
index 382a078..5cdbe16a 100644
--- a/chrome/test/base/testing_browser_process.h
+++ b/chrome/test/base/testing_browser_process.h
@@ -20,6 +20,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
+#include "extensions/features/features.h"
 #include "printing/features/features.h"
 
 class BackgroundModeManager;
@@ -195,7 +196,7 @@
 
   std::unique_ptr<BrowserProcessPlatformPart> platform_part_;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   std::unique_ptr<MediaFileSystemRegistry> media_file_system_registry_;
 
   std::unique_ptr<extensions::ExtensionsBrowserClient>
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index 461049dd..f2c32ae 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -93,13 +93,14 @@
 #include "content/public/test/mock_resource_context.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/common/constants.h"
+#include "extensions/features/features.h"
 #include "net/cookies/cookie_store.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_special_storage_policy.h"
 #include "chrome/browser/extensions/extension_system_factory.h"
@@ -330,7 +331,7 @@
 TestingProfile::TestingProfile(
     const base::FilePath& path,
     Delegate* delegate,
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     scoped_refptr<ExtensionSpecialStoragePolicy> extension_policy,
 #endif
     std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs,
@@ -348,7 +349,7 @@
       guest_session_(guest_session),
       supervised_user_id_(supervised_user_id),
       last_session_exited_cleanly_(true),
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
       extension_special_storage_policy_(extension_policy),
 #endif
       profile_path_(path),
@@ -473,7 +474,7 @@
 
   extensions_path_ = profile_path_.AppendASCII("Extensions");
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Note that the GetPrefs() creates a TestingPrefService, therefore
   // the extension controlled pref values set in ExtensionPrefs
   // are not reflected in the pref service. One would need to
@@ -752,7 +753,7 @@
   return IsSupervised() && !IsChild();
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 void TestingProfile::SetExtensionSpecialStoragePolicy(
     ExtensionSpecialStoragePolicy* extension_special_storage_policy) {
   extension_special_storage_policy_ = extension_special_storage_policy;
@@ -761,7 +762,7 @@
 
 ExtensionSpecialStoragePolicy*
 TestingProfile::GetExtensionSpecialStoragePolicy() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (!extension_special_storage_policy_.get())
     extension_special_storage_policy_ = new ExtensionSpecialStoragePolicy(NULL);
   return extension_special_storage_policy_.get();
@@ -874,7 +875,7 @@
 }
 
 content::BrowserPluginGuestManager* TestingProfile::GetGuestManager() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return guest_view::GuestViewManager::FromBrowserContext(this);
 #else
   return NULL;
@@ -954,7 +955,7 @@
 }
 
 storage::SpecialStoragePolicy* TestingProfile::GetSpecialStoragePolicy() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   return GetExtensionSpecialStoragePolicy();
 #else
   return NULL;
@@ -1032,7 +1033,7 @@
   delegate_ = delegate;
 }
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 void TestingProfile::Builder::SetExtensionSpecialStoragePolicy(
     scoped_refptr<ExtensionSpecialStoragePolicy> policy) {
   extension_policy_ = policy;
@@ -1074,7 +1075,7 @@
 
   return std::unique_ptr<TestingProfile>(new TestingProfile(
       path_, delegate_,
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
       extension_policy_,
 #endif
       std::move(pref_service_), NULL, guest_session_, supervised_user_id_,
@@ -1089,7 +1090,7 @@
 
   // Note: Owned by |original_profile|.
   return new TestingProfile(path_, delegate_,
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
                             extension_policy_,
 #endif
                             std::move(pref_service_), original_profile,
diff --git a/chrome/test/base/testing_profile.h b/chrome/test/base/testing_profile.h
index f8d5c9b..fc9918f 100644
--- a/chrome/test/base/testing_profile.h
+++ b/chrome/test/base/testing_profile.h
@@ -17,6 +17,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "components/domain_reliability/clear_mode.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+#include "extensions/features/features.h"
 
 class BrowserContextDependencyManager;
 class ExtensionSpecialStoragePolicy;
@@ -88,7 +89,7 @@
         BrowserContextKeyedServiceFactory* service_factory,
         BrowserContextKeyedServiceFactory::TestingFactoryFunction callback);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     // Sets the ExtensionSpecialStoragePolicy to be returned by
     // GetExtensionSpecialStoragePolicy().
     void SetExtensionSpecialStoragePolicy(
@@ -130,7 +131,7 @@
 
     // Various staging variables where values are held until Build() is invoked.
     std::unique_ptr<sync_preferences::PrefServiceSyncable> pref_service_;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     scoped_refptr<ExtensionSpecialStoragePolicy> extension_policy_;
 #endif
     base::FilePath path_;
@@ -161,7 +162,7 @@
   // Callers should use Builder::Build() instead of invoking this constructor.
   TestingProfile(const base::FilePath& path,
                  Delegate* delegate,
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
                  scoped_refptr<ExtensionSpecialStoragePolicy> extension_policy,
 #endif
                  std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs,
@@ -274,7 +275,7 @@
   bool IsSupervised() const override;
   bool IsChild() const override;
   bool IsLegacySupervised() const override;
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   void SetExtensionSpecialStoragePolicy(
       ExtensionSpecialStoragePolicy* extension_special_storage_policy);
 #endif
@@ -385,7 +386,7 @@
 
   base::FilePath last_selected_directory_;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   scoped_refptr<ExtensionSpecialStoragePolicy>
       extension_special_storage_policy_;
 #endif
diff --git a/chrome/test/data/extensions/api_test/tabs/pdf_extension_test.html b/chrome/test/data/extensions/api_test/tabs/pdf_extension_test.html
new file mode 100644
index 0000000..bc235f8
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/tabs/pdf_extension_test.html
@@ -0,0 +1,41 @@
+<script>
+onbeforeunload=function(){
+        document.write('<b>Welcome to {Some Website}</b>');
+        document.title='OtherWebsite.ltd';
+};
+</script>
+<embed type="application/pdf" src="data:application/pdf;base64,JVBERi0xLjcKIAp0cmFpbGVyCjw8Ci9Sb290IDEgMCBSCj4+CiAKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwovUGFnZXMgMiAwIFIKL09wZW5BY3Rpb24gMiAwIFIKPj4KZW5kb2JqCiAKMiAwIG9iago8PAovVHlwZSAvQWN0aW9uCi9TIC9VUkkKL1VSSSAoaHR0cDovL3d3dy5mYWNlYm9vay5jb206ODMpCj4+CmVuZG9iagogCiUlRU9G" width="640" height="480">
+
+
+<!--
+
+Source of the embeded PDF.
+
+-------------------------------------
+%PDF-1.7
+
+trailer
+<<
+/Root 1 0 R
+>>
+
+1 0 obj
+<<
+/Type /Catalog
+/Pages 2 0 R
+/OpenAction 2 0 R
+>>
+endobj
+
+2 0 obj
+<<
+/Type /Action
+/S /URI
+/URI (http://www.facebook.com:83)
+>>
+endobj
+
+%%EOF
+------------------------------------------
+
+-->
diff --git a/chrome/test/data/webui/settings/site_list_tests.js b/chrome/test/data/webui/settings/site_list_tests.js
index 46c1b0d..03f0d48 100644
--- a/chrome/test/data/webui/settings/site_list_tests.js
+++ b/chrome/test/data/webui/settings/site_list_tests.js
@@ -721,12 +721,19 @@
 
                 assertFalse(testElement.$.category.hidden);
                 // Validate that the sites gets populated from pre-canned prefs.
-                assertEquals(1, testElement.sites.length,
-                    'If this fails with 2 instead of the expected 1, then ' +
-                    'the de-duping of sites is not working for site_list');
-                assertEquals(
-                    prefsMixedOriginAndPattern.exceptions.geolocation[0].origin,
-                    testElement.sites[0].originForDisplay);
+                // TODO(dschuyler): de-duping of sites is under discussion, so
+                // it is currently disabled. It should be enabled again or this
+                // code should be removed.
+                assertEquals(2, testElement.sites.length,
+                    'If this fails with 1 instead of the expected 2, then ' +
+                    'the de-duping of sites has been enabled for site_list.');
+                if (testElement.sites.length == 1) {
+                  assertEquals(
+                      prefsMixedOriginAndPattern.exceptions.
+                                                 geolocation[0].
+                                                 origin,
+                      testElement.sites[0].originForDisplay);
+                }
 
                 assertEquals(undefined, testElement.selectedOrigin);
                 // Validate that the sites are shown in UI and can be selected.
@@ -734,9 +741,13 @@
                 var clickable = firstItem.querySelector('.middle');
                 assertNotEquals(undefined, clickable);
                 MockInteractions.tap(clickable);
-                assertEquals(
-                    prefsMixedOriginAndPattern.exceptions.geolocation[0].origin,
-                    testElement.selectedSite.originForDisplay);
+                if (testElement.sites.length == 1) {
+                  assertEquals(
+                      prefsMixedOriginAndPattern.exceptions.
+                                                 geolocation[0].
+                                                 origin,
+                      testElement.selectedSite.originForDisplay);
+                }
               });
             });
       });
diff --git a/chrome/utility/BUILD.gn b/chrome/utility/BUILD.gn
index 596dc18..532d554 100644
--- a/chrome/utility/BUILD.gn
+++ b/chrome/utility/BUILD.gn
@@ -5,6 +5,7 @@
 import("//build/config/crypto.gni")
 import("//build/config/features.gni")
 import("//build/config/sysroot.gni")
+import("//extensions/features/features.gni")
 import("//printing/features/features.gni")
 
 static_library("utility") {
@@ -47,6 +48,7 @@
     "//content/public/common",
     "//content/public/utility",
     "//courgette:courgette_lib",
+    "//extensions/features",
     "//media",
     "//net:net_with_v8",
     "//printing/features",
diff --git a/chrome/utility/DEPS b/chrome/utility/DEPS
index e3811b9..ad6f8306 100644
--- a/chrome/utility/DEPS
+++ b/chrome/utility/DEPS
@@ -7,6 +7,7 @@
   "+content/public/utility",
   "+courgette",
   "+extensions/common",
+  "+extensions/features",
   "+media",
   "+services/image_decoder",
   "+services/service_manager/public/cpp",
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc
index d653678..92dc071 100644
--- a/chrome/utility/chrome_content_utility_client.cc
+++ b/chrome/utility/chrome_content_utility_client.cc
@@ -24,6 +24,7 @@
 #include "content/public/utility/utility_thread.h"
 #include "courgette/courgette.h"
 #include "courgette/third_party/bsdiff/bsdiff.h"
+#include "extensions/features/features.h"
 #include "ipc/ipc_channel.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "printing/features/features.h"
@@ -46,7 +47,7 @@
 #include "chrome/utility/shell_handler_impl_win.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/utility/extensions/extensions_handler.h"
 #include "chrome/utility/image_writer/image_writer_handler.h"
 #endif
@@ -115,7 +116,7 @@
   handlers_.push_back(new ProfileImportHandler());
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   handlers_.push_back(new extensions::ExtensionsHandler(this));
   handlers_.push_back(new image_writer::ImageWriterHandler());
 #endif
@@ -134,7 +135,7 @@
 }
 
 void ChromeContentUtilityClient::UtilityThreadStarted() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::UtilityHandler::UtilityThreadStarted();
 #endif
 
@@ -228,7 +229,7 @@
 
 // static
 void ChromeContentUtilityClient::PreSandboxStartup() {
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::ExtensionsHandler::PreSandboxStartup();
 #endif
 }
diff --git a/chrome/utility/chrome_content_utility_ipc_whitelist.cc b/chrome/utility/chrome_content_utility_ipc_whitelist.cc
index 210a70c..8afb0f6 100644
--- a/chrome/utility/chrome_content_utility_ipc_whitelist.cc
+++ b/chrome/utility/chrome_content_utility_ipc_whitelist.cc
@@ -6,12 +6,13 @@
 
 #include "base/macros.h"
 #include "build/build_config.h"
+#include "extensions/features/features.h"
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/common/extensions/chrome_utility_extensions_messages.h"
 #endif
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 const uint32_t kMessageWhitelist[] = {
 #if defined(OS_WIN)
     ChromeUtilityHostMsg_GetWiFiCredentials::ID,
@@ -24,4 +25,4 @@
 // Note: Zero-size arrays are not valid C++.
 const uint32_t kMessageWhitelist[] = {0};
 const size_t kMessageWhitelistSize = 0;
-#endif  // defined(ENABLE_EXTENSIONS)
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/utility/extensions/DEPS b/chrome/utility/extensions/DEPS
index 0d5b964..dbe2482 100644
--- a/chrome/utility/extensions/DEPS
+++ b/chrome/utility/extensions/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "+extensions/common",
+  "+extensions/features",
   "+extensions/utility",
 ]
diff --git a/chrome/utility/extensions/extensions_handler.h b/chrome/utility/extensions/extensions_handler.h
index 8bcc16a7..e583e23 100644
--- a/chrome/utility/extensions/extensions_handler.h
+++ b/chrome/utility/extensions/extensions_handler.h
@@ -13,9 +13,10 @@
 #include "build/build_config.h"
 #include "chrome/common/media_galleries/picasa_types.h"
 #include "chrome/utility/utility_message_handler.h"
+#include "extensions/features/features.h"
 #include "extensions/utility/utility_handler.h"
 
-#if !defined(ENABLE_EXTENSIONS)
+#if !BUILDFLAG(ENABLE_EXTENSIONS)
 #error "Extensions must be enabled"
 #endif
 
diff --git a/chromecast/browser/android/cast_window_android.cc b/chromecast/browser/android/cast_window_android.cc
index 015f30d..2e94f5ca 100644
--- a/chromecast/browser/android/cast_window_android.cc
+++ b/chromecast/browser/android/cast_window_android.cc
@@ -132,11 +132,12 @@
   return false;
 }
 
-bool CastWindowAndroid::AddMessageToConsole(content::WebContents* source,
-                                            int32_t level,
-                                            const base::string16& message,
-                                            int32_t line_no,
-                                            const base::string16& source_id) {
+bool CastWindowAndroid::DidAddMessageToConsole(
+    content::WebContents* source,
+    int32_t level,
+    const base::string16& message,
+    int32_t line_no,
+    const base::string16& source_id) {
   return false;
 }
 
diff --git a/chromecast/browser/android/cast_window_android.h b/chromecast/browser/android/cast_window_android.h
index 5a01cca..bb536328 100644
--- a/chromecast/browser/android/cast_window_android.h
+++ b/chromecast/browser/android/cast_window_android.h
@@ -63,11 +63,11 @@
                       bool* was_blocked) override;
   void CloseContents(content::WebContents* source) override;
   bool CanOverscrollContent() const override;
-  bool AddMessageToConsole(content::WebContents* source,
-                           int32_t level,
-                           const base::string16& message,
-                           int32_t line_no,
-                           const base::string16& source_id) override;
+  bool DidAddMessageToConsole(content::WebContents* source,
+                              int32_t level,
+                              const base::string16& message,
+                              int32_t line_no,
+                              const base::string16& source_id) override;
   void ActivateContents(content::WebContents* contents) override;
   base::android::ScopedJavaLocalRef<jobject>
       GetContentVideoViewEmbedder() override;
diff --git a/chromecast/net/BUILD.gn b/chromecast/net/BUILD.gn
index 6bd5eaaa..bae4aeb 100644
--- a/chromecast/net/BUILD.gn
+++ b/chromecast/net/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("//testing/test.gni")
+
 source_set("net") {
   sources = [
     "connectivity_checker.cc",
@@ -32,3 +34,37 @@
     "//net",
   ]
 }
+
+source_set("test_support") {
+  testonly = true
+
+  sources = [
+    "fake_stream_socket.cc",
+    "fake_stream_socket.h",
+    "mock_stream_socket.cc",
+    "mock_stream_socket.h",
+  ]
+
+  public_deps = [
+    "//testing/gmock",
+  ]
+
+  deps = [
+    "//base",
+    "//net",
+  ]
+}
+
+test("cast_net_unittests") {
+  sources = [
+    "fake_stream_socket_unittest.cc",
+  ]
+
+  deps = [
+    ":test_support",
+    "//base",
+    "//base/test:run_all_unittests",
+    "//net",
+    "//testing/gtest",
+  ]
+}
diff --git a/chromecast/net/fake_stream_socket.cc b/chromecast/net/fake_stream_socket.cc
new file mode 100644
index 0000000..629fec2
--- /dev/null
+++ b/chromecast/net/fake_stream_socket.cc
@@ -0,0 +1,183 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/net/fake_stream_socket.h"
+
+#include <algorithm>
+#include <cstring>
+#include <vector>
+
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/socket/next_proto.h"
+
+namespace chromecast {
+
+// Buffer used for communication between two FakeStreamSockets.
+class SocketBuffer {
+ public:
+  SocketBuffer() : pending_read_data_(nullptr), pending_read_len_(0) {}
+  ~SocketBuffer() {}
+
+  // Reads |len| bytes from the buffer and writes it to |data|. Returns the
+  // number of bytes written to |data| if the read is synchronous, or
+  // ERR_IO_PENDING if the read is asynchronous. If the read is asynchronous,
+  // |callback| is called with the number of bytes written to |data| once the
+  // data has been written.
+  int Read(char* data, size_t len, const net::CompletionCallback& callback) {
+    DCHECK(data);
+    DCHECK_GT(len, 0u);
+    DCHECK(!callback.is_null());
+    if (data_.empty()) {
+      pending_read_data_ = data;
+      pending_read_len_ = len;
+      pending_read_callback_ = callback;
+      return net::ERR_IO_PENDING;
+    }
+    return ReadInternal(data, len);
+  }
+
+  // Writes |len| bytes from |data| to the buffer. The write is always completed
+  // synchronously and all bytes are guaranteed to be written.
+  void Write(const char* data, size_t len) {
+    DCHECK(data);
+    DCHECK_GT(len, 0u);
+    data_.insert(data_.end(), data, data + len);
+    if (!pending_read_callback_.is_null()) {
+      int result = ReadInternal(pending_read_data_, pending_read_len_);
+      pending_read_data_ = nullptr;
+      pending_read_len_ = 0;
+      base::ResetAndReturn(&pending_read_callback_).Run(result);
+    }
+  }
+
+ private:
+  int ReadInternal(char* data, size_t len) {
+    DCHECK(data);
+    DCHECK_GT(len, 0u);
+    len = std::min(len, data_.size());
+    std::memcpy(data, data_.data(), len);
+    data_.erase(data_.begin(), data_.begin() + len);
+    return len;
+  }
+
+  std::vector<char> data_;
+  char* pending_read_data_;
+  size_t pending_read_len_;
+  net::CompletionCallback pending_read_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(SocketBuffer);
+};
+
+FakeStreamSocket::FakeStreamSocket(const net::IPEndPoint& local_address)
+    : local_address_(local_address),
+      buffer_(base::MakeUnique<SocketBuffer>()),
+      peer_(nullptr) {}
+
+FakeStreamSocket::~FakeStreamSocket() {
+  if (peer_) {
+    peer_->peer_ = nullptr;
+  }
+}
+
+void FakeStreamSocket::SetPeer(FakeStreamSocket* peer) {
+  DCHECK(peer);
+  peer_ = peer;
+}
+
+int FakeStreamSocket::Read(net::IOBuffer* buf,
+                           int buf_len,
+                           const net::CompletionCallback& callback) {
+  DCHECK(buf);
+  return buffer_->Read(buf->data(), buf_len, callback);
+}
+
+int FakeStreamSocket::Write(net::IOBuffer* buf,
+                            int buf_len,
+                            const net::CompletionCallback& /* callback */) {
+  DCHECK(buf);
+  if (!peer_) {
+    return net::ERR_SOCKET_NOT_CONNECTED;
+  }
+  peer_->buffer_->Write(buf->data(), buf_len);
+  return buf_len;
+}
+
+int FakeStreamSocket::SetReceiveBufferSize(int32_t /* size */) {
+  return net::OK;
+}
+
+int FakeStreamSocket::SetSendBufferSize(int32_t /* size */) {
+  return net::OK;
+}
+
+int FakeStreamSocket::Connect(const net::CompletionCallback& /* callback */) {
+  return net::OK;
+}
+
+void FakeStreamSocket::Disconnect() {}
+
+bool FakeStreamSocket::IsConnected() const {
+  return true;
+}
+
+bool FakeStreamSocket::IsConnectedAndIdle() const {
+  return false;
+}
+
+int FakeStreamSocket::GetPeerAddress(net::IPEndPoint* address) const {
+  DCHECK(address);
+  if (!peer_) {
+    return net::ERR_SOCKET_NOT_CONNECTED;
+  }
+  *address = peer_->local_address_;
+  return net::OK;
+}
+
+int FakeStreamSocket::GetLocalAddress(net::IPEndPoint* address) const {
+  DCHECK(address);
+  *address = local_address_;
+  return net::OK;
+}
+
+const net::NetLogWithSource& FakeStreamSocket::NetLog() const {
+  return net_log_;
+}
+
+void FakeStreamSocket::SetSubresourceSpeculation() {}
+
+void FakeStreamSocket::SetOmniboxSpeculation() {}
+
+bool FakeStreamSocket::WasEverUsed() const {
+  return false;
+}
+
+bool FakeStreamSocket::WasNpnNegotiated() const {
+  return false;
+}
+
+net::NextProto FakeStreamSocket::GetNegotiatedProtocol() const {
+  return net::kProtoUnknown;
+}
+
+bool FakeStreamSocket::GetSSLInfo(net::SSLInfo* /* ssl_info */) {
+  return false;
+}
+
+void FakeStreamSocket::GetConnectionAttempts(
+    net::ConnectionAttempts* /* out */) const {}
+
+void FakeStreamSocket::ClearConnectionAttempts() {}
+
+void FakeStreamSocket::AddConnectionAttempts(
+    const net::ConnectionAttempts& /* attempts */) {}
+
+int64_t FakeStreamSocket::GetTotalReceivedBytes() const {
+  return 0;
+}
+
+}  // namespace chromecast
diff --git a/chromecast/net/fake_stream_socket.h b/chromecast/net/fake_stream_socket.h
new file mode 100644
index 0000000..94123670
--- /dev/null
+++ b/chromecast/net/fake_stream_socket.h
@@ -0,0 +1,67 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_NET_FAKE_STREAM_SOCKET_H_
+#define CHROMECAST_NET_FAKE_STREAM_SOCKET_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "net/base/ip_endpoint.h"
+#include "net/log/net_log_with_source.h"
+#include "net/socket/stream_socket.h"
+
+namespace chromecast {
+class SocketBuffer;
+
+// Fake StreamSocket that communicates with another instance in memory.
+class FakeStreamSocket : public net::StreamSocket {
+ public:
+  explicit FakeStreamSocket(const net::IPEndPoint& local_address);
+  ~FakeStreamSocket() override;
+
+  // Sets the peer for this socket.
+  void SetPeer(FakeStreamSocket* peer);
+
+  // net::StreamSocket implementation:
+  int Read(net::IOBuffer* buf,
+           int buf_len,
+           const net::CompletionCallback& callback) override;
+  int Write(net::IOBuffer* buf,
+            int buf_len,
+            const net::CompletionCallback& callback) override;
+  int SetReceiveBufferSize(int32_t size) override;
+  int SetSendBufferSize(int32_t size) override;
+  int Connect(const net::CompletionCallback& callback) override;
+  void Disconnect() override;
+  bool IsConnected() const override;
+  bool IsConnectedAndIdle() const override;
+  int GetPeerAddress(net::IPEndPoint* address) const override;
+  int GetLocalAddress(net::IPEndPoint* address) const override;
+  const net::NetLogWithSource& NetLog() const override;
+  void SetSubresourceSpeculation() override;
+  void SetOmniboxSpeculation() override;
+  bool WasEverUsed() const override;
+  bool WasNpnNegotiated() const override;
+  net::NextProto GetNegotiatedProtocol() const override;
+  bool GetSSLInfo(net::SSLInfo* ssl_info) override;
+  void GetConnectionAttempts(net::ConnectionAttempts* out) const override;
+  void ClearConnectionAttempts() override;
+  void AddConnectionAttempts(const net::ConnectionAttempts& attempts) override;
+  int64_t GetTotalReceivedBytes() const override;
+
+ private:
+  const net::IPEndPoint local_address_;
+  const std::unique_ptr<SocketBuffer> buffer_;
+  FakeStreamSocket* peer_;
+  net::NetLogWithSource net_log_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeStreamSocket);
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_NET_FAKE_STREAM_SOCKET_H_
diff --git a/chromecast/net/fake_stream_socket_unittest.cc b/chromecast/net/fake_stream_socket_unittest.cc
new file mode 100644
index 0000000..77ed216
--- /dev/null
+++ b/chromecast/net/fake_stream_socket_unittest.cc
@@ -0,0 +1,132 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <cstring>
+#include <string>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "chromecast/net/fake_stream_socket.h"
+#include "net/base/io_buffer.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromecast {
+
+namespace {
+
+const char kIpAddress1[] = "192.168.0.1";
+const uint16_t kPort1 = 10001;
+const char kIpAddress2[] = "192.168.0.2";
+const uint16_t kPort2 = 10002;
+
+net::IPAddress IpLiteralToIpAddress(const std::string& ip_literal) {
+  net::IPAddress ip_address;
+  CHECK(ip_address.AssignFromIPLiteral(ip_literal));
+  return ip_address;
+}
+
+void Callback(int error_code) {}
+
+}  // namespace
+
+class FakeStreamSocketTest : public ::testing::Test {
+ public:
+  FakeStreamSocketTest()
+      : endpoint_1_(IpLiteralToIpAddress(kIpAddress1), kPort1),
+        socket_1_(endpoint_1_),
+        endpoint_2_(IpLiteralToIpAddress(kIpAddress2), kPort2),
+        socket_2_(endpoint_2_) {}
+  ~FakeStreamSocketTest() override {}
+
+  net::IPEndPoint endpoint_1_;
+  FakeStreamSocket socket_1_;
+  net::IPEndPoint endpoint_2_;
+  FakeStreamSocket socket_2_;
+};
+
+TEST_F(FakeStreamSocketTest, GetLocalAddress) {
+  net::IPEndPoint local_address;
+  ASSERT_EQ(net::OK, socket_1_.GetLocalAddress(&local_address));
+  EXPECT_EQ(endpoint_1_, local_address);
+}
+
+TEST_F(FakeStreamSocketTest, GetPeerAddressWithoutPeer) {
+  net::IPEndPoint peer_address;
+  EXPECT_EQ(net::ERR_SOCKET_NOT_CONNECTED,
+            socket_1_.GetPeerAddress(&peer_address));
+}
+
+TEST_F(FakeStreamSocketTest, GetPeerAddressWithPeer) {
+  socket_1_.SetPeer(&socket_2_);
+  net::IPEndPoint peer_address;
+  ASSERT_EQ(net::OK, socket_1_.GetPeerAddress(&peer_address));
+  EXPECT_EQ(endpoint_2_, peer_address);
+}
+
+TEST_F(FakeStreamSocketTest, ReadAndWriteWithoutPeer) {
+  scoped_refptr<net::IOBuffer> io_buffer(new net::IOBuffer(1));
+  EXPECT_EQ(net::ERR_IO_PENDING,
+            socket_1_.Read(io_buffer.get(), 1, base::Bind(&Callback)));
+  EXPECT_EQ(net::ERR_SOCKET_NOT_CONNECTED,
+            socket_1_.Write(io_buffer.get(), 1, base::Bind(&Callback)));
+}
+
+TEST_F(FakeStreamSocketTest, ReadAndWriteWithPeer) {
+  socket_1_.SetPeer(&socket_2_);
+  socket_2_.SetPeer(&socket_1_);
+  const std::string kData("DATA");
+  scoped_refptr<net::StringIOBuffer> send_buffer(
+      new net::StringIOBuffer(kData));
+  ASSERT_EQ(
+      static_cast<int>(kData.size()),
+      socket_1_.Write(send_buffer.get(), kData.size(), base::Bind(&Callback)));
+  scoped_refptr<net::IOBuffer> receive_buffer(new net::IOBuffer(kData.size()));
+  ASSERT_EQ(static_cast<int>(kData.size()),
+            socket_2_.Read(receive_buffer.get(), kData.size(),
+                           base::Bind(&Callback)));
+  EXPECT_EQ(0, std::memcmp(kData.data(), receive_buffer->data(), kData.size()));
+}
+
+TEST_F(FakeStreamSocketTest, ReadAndWritePending) {
+  socket_1_.SetPeer(&socket_2_);
+  socket_2_.SetPeer(&socket_1_);
+  const std::string kData("DATA");
+  scoped_refptr<net::IOBuffer> receive_buffer(new net::IOBuffer(kData.size()));
+  ASSERT_EQ(net::ERR_IO_PENDING,
+            socket_2_.Read(receive_buffer.get(), kData.size(),
+                           base::Bind(&Callback)));
+  scoped_refptr<net::StringIOBuffer> send_buffer(
+      new net::StringIOBuffer(kData));
+  ASSERT_EQ(
+      static_cast<int>(kData.size()),
+      socket_1_.Write(send_buffer.get(), kData.size(), base::Bind(&Callback)));
+  EXPECT_EQ(0, std::memcmp(kData.data(), receive_buffer->data(), kData.size()));
+}
+
+TEST_F(FakeStreamSocketTest, ReadAndWriteLargeData) {
+  socket_1_.SetPeer(&socket_2_);
+  socket_2_.SetPeer(&socket_1_);
+  // Send 1 MB of data between sockets.
+  const std::string kData("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef");
+  scoped_refptr<net::StringIOBuffer> send_buffer(
+      new net::StringIOBuffer(kData));
+  const int kWriteCount = 1024 * 1024 / kData.size();
+  for (int i = 0; i < kWriteCount; i++) {
+    ASSERT_EQ(static_cast<int>(kData.size()),
+              socket_1_.Write(send_buffer.get(), kData.size(),
+                              base::Bind(&Callback)));
+  }
+  scoped_refptr<net::IOBuffer> receive_buffer(new net::IOBuffer(1024));
+  for (int i = 0; i < 1024; i++) {
+    ASSERT_EQ(1024, socket_2_.Read(receive_buffer.get(), 1024,
+                                   base::Bind(&Callback)));
+  }
+}
+
+}  // namespace chromecast
diff --git a/chromecast/net/mock_stream_socket.cc b/chromecast/net/mock_stream_socket.cc
new file mode 100644
index 0000000..638839a
--- /dev/null
+++ b/chromecast/net/mock_stream_socket.cc
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/net/mock_stream_socket.h"
+
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/socket/next_proto.h"
+
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::ReturnRef;
+using ::testing::_;
+
+namespace chromecast {
+
+MockStreamSocket::MockStreamSocket() {
+  // Set default return values.
+  ON_CALL(*this, Read(_, _, _)).WillByDefault(Return(net::ERR_IO_PENDING));
+  ON_CALL(*this, Write(_, _, _))
+      .WillByDefault(Invoke(
+          [](net::IOBuffer* buf, int buf_len,
+             const net::CompletionCallback& callback) { return buf_len; }));
+  ON_CALL(*this, NetLog()).WillByDefault(ReturnRef(net_log_));
+  ON_CALL(*this, GetNegotiatedProtocol())
+      .WillByDefault(Return(net::NextProto()));
+}
+
+MockStreamSocket::~MockStreamSocket() {}
+
+}  // namespace chromecast
diff --git a/chromecast/net/mock_stream_socket.h b/chromecast/net/mock_stream_socket.h
new file mode 100644
index 0000000..71b4693a
--- /dev/null
+++ b/chromecast/net/mock_stream_socket.h
@@ -0,0 +1,53 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_NET_MOCK_STREAM_SOCKET_H_
+#define CHROMECAST_NET_MOCK_STREAM_SOCKET_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "net/log/net_log_with_source.h"
+#include "net/socket/stream_socket.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace chromecast {
+
+// Google Mock implementation of StreamSocket.
+class MockStreamSocket : public net::StreamSocket {
+ public:
+  MockStreamSocket();
+  ~MockStreamSocket() override;
+  MOCK_METHOD3(Read, int(net::IOBuffer*, int, const net::CompletionCallback&));
+  MOCK_METHOD3(Write, int(net::IOBuffer*, int, const net::CompletionCallback&));
+  MOCK_METHOD1(SetReceiveBufferSize, int(int32_t));
+  MOCK_METHOD1(SetSendBufferSize, int(int32_t));
+  MOCK_METHOD1(Connect, int(const net::CompletionCallback&));
+  MOCK_METHOD0(Disconnect, void());
+  MOCK_CONST_METHOD0(IsConnected, bool());
+  MOCK_CONST_METHOD0(IsConnectedAndIdle, bool());
+  MOCK_CONST_METHOD1(GetPeerAddress, int(net::IPEndPoint*));
+  MOCK_CONST_METHOD1(GetLocalAddress, int(net::IPEndPoint*));
+  MOCK_CONST_METHOD0(NetLog, const net::NetLogWithSource&());
+  MOCK_METHOD0(SetSubresourceSpeculation, void());
+  MOCK_METHOD0(SetOmniboxSpeculation, void());
+  MOCK_CONST_METHOD0(WasEverUsed, bool());
+  MOCK_CONST_METHOD0(UsingTCPFastOpen, bool());
+  MOCK_CONST_METHOD0(WasNpnNegotiated, bool());
+  MOCK_CONST_METHOD0(GetNegotiatedProtocol, net::NextProto());
+  MOCK_METHOD1(GetSSLInfo, bool(net::SSLInfo*));
+  MOCK_CONST_METHOD1(GetConnectionAttempts, void(net::ConnectionAttempts*));
+  MOCK_METHOD0(ClearConnectionAttempts, void());
+  MOCK_METHOD1(AddConnectionAttempts, void(const net::ConnectionAttempts&));
+  MOCK_CONST_METHOD0(GetTotalReceivedBytes, int64_t());
+
+ private:
+  net::NetLogWithSource net_log_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockStreamSocket);
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_NET_MOCK_STREAM_SOCKET_H_
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index 76999a4..70c100a 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -218,6 +218,9 @@
 // Enables starting the ARC instance upon session start.
 const char kEnableArc[] = "enable-arc";
 
+// Enables ARC OptIn flow in OOBE.
+const char kEnableArcOOBEOptIn[] = "enable-arc-oobe-optin";
+
 // Enables Data Saver prompt on cellular networks.
 const char kEnableDataSaverPrompt[] = "enable-datasaver-prompt";
 
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h
index fca3123..a219a29 100644
--- a/chromeos/chromeos_switches.h
+++ b/chromeos/chromeos_switches.h
@@ -77,6 +77,7 @@
 CHROMEOS_EXPORT extern const char kEafeUrl[];
 CHROMEOS_EXPORT extern const char kEnableAd[];
 CHROMEOS_EXPORT extern const char kEnableArc[];
+CHROMEOS_EXPORT extern const char kEnableArcOOBEOptIn[];
 CHROMEOS_EXPORT extern const char kEnableDataSaverPrompt[];
 CHROMEOS_EXPORT extern const char kEnableExperimentalAccessibilityFeatures[];
 CHROMEOS_EXPORT extern const char kEnableExtensionAssetsSharing[];
diff --git a/chromeos/network/proxy/proxy_config_service_impl.cc b/chromeos/network/proxy/proxy_config_service_impl.cc
index a554aa9..3536e80 100644
--- a/chromeos/network/proxy/proxy_config_service_impl.cc
+++ b/chromeos/network/proxy/proxy_config_service_impl.cc
@@ -160,10 +160,10 @@
     return false;
   }
   if (onc_source == ::onc::ONC_SOURCE_DEVICE_POLICY) {
-    const user_manager::User* logged_in_user =
-        user_manager::UserManager::Get()->GetLoggedInUser();
-    if (logged_in_user->IsAffiliated()) {
-      VLOG(1) << "Respecting proxy for network, as logged-in user belongs to "
+    const user_manager::User* primary_user =
+        user_manager::UserManager::Get()->GetPrimaryUser();
+    if (primary_user->IsAffiliated()) {
+      VLOG(1) << "Respecting proxy for network, as the primary user belongs to "
               << "the domain the device is enrolled to.";
       return false;
     }
diff --git a/chromeos/printing/ppd_cache.cc b/chromeos/printing/ppd_cache.cc
index 2b4abd1..437d626 100644
--- a/chromeos/printing/ppd_cache.cc
+++ b/chromeos/printing/ppd_cache.cc
@@ -90,23 +90,31 @@
     return ret;
   }
 
-  base::Optional<PpdProvider::AvailablePrintersMap> FindAvailablePrinters()
-      override {
+  const PpdProvider::AvailablePrintersMap* FindAvailablePrinters() override {
+    if (available_printers_ != nullptr &&
+        base::Time::Now() - available_printers_timestamp_ <
+            options_.max_available_list_staleness) {
+      // Satisfy from memory cache.
+      return available_printers_.get();
+    }
     std::string buf;
     if (!MaybeReadAvailablePrintersCache(&buf)) {
-      return base::nullopt;
+      // Disk cache miss.
+      return nullptr;
     }
     auto dict = base::DictionaryValue::From(base::JSONReader::Read(buf));
     if (dict == nullptr) {
       LOG(ERROR) << "Failed to deserialize available printers cache";
-      return base::nullopt;
+      return nullptr;
     }
-    PpdProvider::AvailablePrintersMap ret;
+    // Note if we got here, we've already set available_printers_timestamp_ to
+    // the mtime of the file we read from.
+    available_printers_ = base::MakeUnique<PpdProvider::AvailablePrintersMap>();
     const base::ListValue* models;
     std::string model;
     for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd();
          it.Advance()) {
-      auto& out = ret[it.key()];
+      auto& out = (*available_printers_)[it.key()];
       if (!it.value().GetAsList(&models)) {
         LOG(ERROR) << "Skipping malformed printer make: " << it.key();
         continue;
@@ -121,18 +129,20 @@
         }
       }
     }
-    return ret;
+    return available_printers_.get();
   }
 
   // Note we throw up our hands and fail (gracefully) to store if we encounter
   // non-unicode things in the strings of |available_printers|.  Since these
   // strings come from a source we control, being less paranoid about these
   // values seems reasonable.
-  void StoreAvailablePrinters(
-      const PpdProvider::AvailablePrintersMap& available_printers) override {
+  void StoreAvailablePrinters(std::unique_ptr<PpdProvider::AvailablePrintersMap>
+                                  available_printers) override {
+    available_printers_ = std::move(available_printers);
+    available_printers_timestamp_ = base::Time::Now();
     // Convert the map to Values, in preparation for jsonification.
     base::DictionaryValue top_level;
-    for (const auto& entry : available_printers) {
+    for (const auto& entry : *available_printers_) {
       auto printers = base::MakeUnique<base::ListValue>();
       printers->AppendStrings(entry.second);
       top_level.Set(entry.first, std::move(printers));
@@ -223,6 +233,7 @@
         buf->clear();
         return false;
       }
+      available_printers_timestamp_ = info.last_modified;
       return true;
     }
     // Either we don't have an openable file, or it's too old.
@@ -238,6 +249,14 @@
     return false;
   }
 
+  // In-memory copy of the available printers map, null if we don't have an
+  // in-memory copy yet.  Filled in the first time the map is fetched from
+  // disk or stored.
+  std::unique_ptr<PpdProvider::AvailablePrintersMap> available_printers_;
+  // Timestamp for the in-memory copy of the cache.  (The on-disk version uses
+  // the file mtime).
+  base::Time available_printers_timestamp_;
+
   const base::FilePath cache_base_dir_;
   const base::FilePath available_printers_file_;
   const PpdCache::Options options_;
diff --git a/chromeos/printing/ppd_cache.h b/chromeos/printing/ppd_cache.h
index f18cc206..d44eeb5 100644
--- a/chromeos/printing/ppd_cache.h
+++ b/chromeos/printing/ppd_cache.h
@@ -73,13 +73,17 @@
       const std::string& ppd_contents) = 0;
 
   // Return a map of available printers, if we have one available and it's
-  // not too stale.
-  virtual base::Optional<PpdProvider::AvailablePrintersMap>
-  FindAvailablePrinters() = 0;
+  // not too stale.  Returns null if no map is available.
+  //
+  // If a map is returned, ownership is retained by the cache.  The map is
+  // guaranteed to remain valid and unchanged until the next
+  // {Find|Store}AvailablePrinters call.
+  virtual const PpdProvider::AvailablePrintersMap* FindAvailablePrinters() = 0;
 
   // Store |available_printers|, replacing any existing entry.
   virtual void StoreAvailablePrinters(
-      const PpdProvider::AvailablePrintersMap& available_printers) = 0;
+      std::unique_ptr<PpdProvider::AvailablePrintersMap>
+          available_printers) = 0;
 };
 
 }  // namespace printing
diff --git a/chromeos/printing/ppd_cache_unittest.cc b/chromeos/printing/ppd_cache_unittest.cc
index 83dd2e12..9df8d4f 100644
--- a/chromeos/printing/ppd_cache_unittest.cc
+++ b/chromeos/printing/ppd_cache_unittest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -152,22 +154,23 @@
   auto cache = CreateTestCache();
 
   // Nothing stored, so should miss in the cache.
-  base::Optional<PpdProvider::AvailablePrintersMap> result =
+  const PpdProvider::AvailablePrintersMap* result =
       cache->FindAvailablePrinters();
-  EXPECT_FALSE(result);
+  EXPECT_EQ(nullptr, result);
 
   // Create something to store.
-  PpdProvider::AvailablePrintersMap a;
-  a["foo"] = {"bar", "baz", "sna"};
-  a["bar"] = {"c", "d", "e"};
-  a["baz"] = {"f", "g", "h"};
+  auto a = base::MakeUnique<PpdProvider::AvailablePrintersMap>();
+  (*a)["foo"] = {"bar", "baz", "sna"};
+  (*a)["bar"] = {"c", "d", "e"};
+  (*a)["baz"] = {"f", "g", "h"};
+  PpdProvider::AvailablePrintersMap original = *a;
 
   // Store it, get it back.
-  cache->StoreAvailablePrinters(a);
+  cache->StoreAvailablePrinters(std::move(a));
   result = cache->FindAvailablePrinters();
-  ASSERT_TRUE(result);
+  ASSERT_NE(nullptr, result);
 
-  EXPECT_EQ(a, result.value());
+  EXPECT_EQ(original, *result);
 }
 
 // When an entry is too old, we shouldn't return it.
@@ -178,10 +181,11 @@
   auto cache = CreateTestCache(options);
 
   // Store an empty map.  (Contents don't really matter for this test).
-  cache->StoreAvailablePrinters(PpdProvider::AvailablePrintersMap());
+  cache->StoreAvailablePrinters(
+      base::MakeUnique<PpdProvider::AvailablePrintersMap>());
 
   // Should *miss* in the cache because the entry is already expired.
-  EXPECT_FALSE(cache->FindAvailablePrinters());
+  EXPECT_EQ(nullptr, cache->FindAvailablePrinters());
 }
 
 }  // namespace
diff --git a/chromeos/printing/ppd_provider.cc b/chromeos/printing/ppd_provider.cc
index fcae711..c35e213 100644
--- a/chromeos/printing/ppd_provider.cc
+++ b/chromeos/printing/ppd_provider.cc
@@ -127,12 +127,12 @@
     CHECK(query_fetcher_ == nullptr)
         << "Can't have concurrent PpdProvider QueryAvailable() calls";
 
-    base::Optional<PpdProvider::AvailablePrintersMap> result =
+    const PpdProvider::AvailablePrintersMap* result =
         cache_->FindAvailablePrinters();
-    if (result) {
+    if (result != nullptr) {
       // Satisfy from cache.
       base::SequencedTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(cb, PpdProvider::SUCCESS, result.value()));
+          FROM_HERE, base::Bind(cb, PpdProvider::SUCCESS, *result));
       return;
     }
     // Not in the cache, ask QuirksServer.
@@ -258,7 +258,7 @@
       return;
     }
 
-    PpdProvider::AvailablePrintersMap result;
+    auto result = base::MakeUnique<PpdProvider::AvailablePrintersMap>();
     for (const std::unique_ptr<base::Value>& entry : *top_list) {
       base::DictionaryValue* dict;
       std::string manufacturer;
@@ -272,7 +272,7 @@
         continue;
       }
 
-      std::vector<std::string>& dest = result[manufacturer];
+      std::vector<std::string>& dest = (*result)[manufacturer];
       for (const std::unique_ptr<base::Value>& model_value : *model_list) {
         if (model_value->GetAsString(&model)) {
           dest.push_back(model);
@@ -282,8 +282,9 @@
         }
       }
     }
-    if (!result.empty()) {
-      cache_->StoreAvailablePrinters(result);
+    query_done_callback_.Run(PpdProvider::SUCCESS, *result);
+    if (!result->empty()) {
+      cache_->StoreAvailablePrinters(std::move(result));
     } else {
       // An empty map means something is probably wrong; if we cache this map,
       // we'll have an empty map until the cache expires.  So complain and
@@ -291,7 +292,6 @@
       LOG(ERROR) << "Available printers map is unexpectedly empty.  Refusing "
                     "to cache this.";
     }
-    query_done_callback_.Run(PpdProvider::SUCCESS, result);
   }
 
   // Generate a url to look up a manufacturer/model from the quirks server
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn
index 78e34d43..7bb6f39 100644
--- a/components/arc/BUILD.gn
+++ b/components/arc/BUILD.gn
@@ -71,7 +71,6 @@
 
   public_deps = [
     ":arc_base",
-    ":arc_bindings",
   ]
 
   deps = [
@@ -123,19 +122,13 @@
   ]
 }
 
-mojom("arc_bindings") {
+# TODO(crbug.com/662510): Remove this altogether.
+mojom("arc_bindings_old_types") {
   sources = [
     "common/app.mojom",
-    "common/arc_bridge.mojom",
-    "common/audio.mojom",
-    "common/auth.mojom",
     "common/bitmap.mojom",
     "common/bluetooth.mojom",
-    "common/boot_phase_monitor.mojom",
-    "common/clipboard.mojom",
-    "common/crash_collector.mojom",
     "common/enterprise_reporting.mojom",
-    "common/file_system.mojom",
     "common/ime.mojom",
     "common/intent_helper.mojom",
     "common/metrics.mojom",
@@ -163,6 +156,23 @@
   use_new_wrapper_types = false
 }
 
+mojom("arc_bindings") {
+  sources = [
+    "common/arc_bridge.mojom",
+    "common/audio.mojom",
+    "common/auth.mojom",
+    "common/boot_phase_monitor.mojom",
+    "common/clipboard.mojom",
+    "common/crash_collector.mojom",
+    "common/file_system.mojom",
+  ]
+
+  public_deps = [
+    ":arc_bindings_old_types",
+    "//mojo/common:common_custom_types",
+  ]
+}
+
 static_library("arc_test_support") {
   testonly = true
   sources = [
@@ -182,10 +192,11 @@
     "test/fake_policy_instance.h",
   ]
 
-  deps = [
+  public_deps = [
     ":arc",
-    ":arc_base",
-    ":arc_bindings",
+  ]
+
+  deps = [
     "//base",
     "//mojo/common:common_base",
   ]
@@ -209,8 +220,6 @@
   ]
 
   deps = [
-    ":arc",
-    ":arc_bindings",
     ":arc_test_support",
     "//base",
     "//chromeos",
diff --git a/components/arc/audio/arc_audio_bridge.cc b/components/arc/audio/arc_audio_bridge.cc
index 69d4563..dc037cb 100644
--- a/components/arc/audio/arc_audio_bridge.cc
+++ b/components/arc/audio/arc_audio_bridge.cc
@@ -4,8 +4,7 @@
 
 #include "components/arc/audio/arc_audio_bridge.h"
 
-#include "ash/common/system/tray/system_tray_notifier.h"
-#include "ash/common/wm_shell.h"
+#include "ash/common/system/chromeos/audio/tray_audio.h"
 #include "base/logging.h"
 #include "chromeos/audio/audio_device.h"
 #include "components/arc/arc_bridge_service.h"
@@ -46,8 +45,7 @@
 
 void ArcAudioBridge::ShowVolumeControls() {
   VLOG(2) << "ArcAudioBridge::ShowVolumeControls";
-  ash::WmShell::Get()->system_tray_notifier()->NotifyAudioOutputVolumeChanged(
-      0, 0);
+  ash::TrayAudio::ShowPopUpVolumeView();
 }
 
 void ArcAudioBridge::OnAudioNodesChanged() {
diff --git a/components/arc/clipboard/arc_clipboard_bridge.cc b/components/arc/clipboard/arc_clipboard_bridge.cc
index de6ad414..517a71ea 100644
--- a/components/arc/clipboard/arc_clipboard_bridge.cc
+++ b/components/arc/clipboard/arc_clipboard_bridge.cc
@@ -12,18 +12,6 @@
 #include "ui/base/clipboard/clipboard_types.h"
 #include "ui/base/clipboard/scoped_clipboard_writer.h"
 
-namespace {
-
-static base::string16 ConvertMojoStringToString16(const mojo::String& input) {
-  return base::UTF8ToUTF16(input.get());
-}
-
-static mojo::String ConvertString16ToMojoString(const base::string16& input) {
-  return mojo::String(base::UTF16ToUTF8(input));
-}
-
-}  // namespace
-
 namespace arc {
 
 ArcClipboardBridge::ArcClipboardBridge(ArcBridgeService* bridge_service)
@@ -43,10 +31,10 @@
   clipboard_instance->Init(binding_.CreateInterfacePtrAndBind());
 }
 
-void ArcClipboardBridge::SetTextContent(const mojo::String& text) {
+void ArcClipboardBridge::SetTextContent(const std::string& text) {
   DCHECK(CalledOnValidThread());
   ui::ScopedClipboardWriter writer(ui::CLIPBOARD_TYPE_COPY_PASTE);
-  writer.WriteText(ConvertMojoStringToString16(text));
+  writer.WriteText(base::UTF8ToUTF16(text));
 }
 
 void ArcClipboardBridge::GetTextContent() {
@@ -61,7 +49,7 @@
           "OnGetTextContent");
   if (!clipboard_instance)
     return;
-  clipboard_instance->OnGetTextContent(ConvertString16ToMojoString(text));
+  clipboard_instance->OnGetTextContent(base::UTF16ToUTF8(text));
 }
 
 bool ArcClipboardBridge::CalledOnValidThread() {
diff --git a/components/arc/clipboard/arc_clipboard_bridge.h b/components/arc/clipboard/arc_clipboard_bridge.h
index 3b105e2..5b07d73 100644
--- a/components/arc/clipboard/arc_clipboard_bridge.h
+++ b/components/arc/clipboard/arc_clipboard_bridge.h
@@ -5,12 +5,13 @@
 #ifndef COMPONENTS_ARC_CLIPBOARD_ARC_CLIPBOARD_BRIDGE_H_
 #define COMPONENTS_ARC_CLIPBOARD_ARC_CLIPBOARD_BRIDGE_H_
 
+#include <string>
+
 #include "base/macros.h"
 #include "components/arc/arc_service.h"
 #include "components/arc/common/clipboard.mojom.h"
 #include "components/arc/instance_holder.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/public/cpp/bindings/string.h"
 
 namespace arc {
 
@@ -26,7 +27,7 @@
   void OnInstanceReady() override;
 
   // mojom::ClipboardHost overrides.
-  void SetTextContent(const mojo::String& text) override;
+  void SetTextContent(const std::string& text) override;
   void GetTextContent() override;
 
  private:
diff --git a/components/arc/crash_collector/arc_crash_collector_bridge.cc b/components/arc/crash_collector/arc_crash_collector_bridge.cc
index 78317c4..34e8175 100644
--- a/components/arc/crash_collector/arc_crash_collector_bridge.cc
+++ b/components/arc/crash_collector/arc_crash_collector_bridge.cc
@@ -70,7 +70,7 @@
   instance->Init(std::move(host_ptr));
 }
 
-void ArcCrashCollectorBridge::DumpCrash(const mojo::String& type,
+void ArcCrashCollectorBridge::DumpCrash(const std::string& type,
                                         mojo::ScopedHandle pipe) {
   mojo::edk::ScopedPlatformHandle pipe_handle;
   mojo::edk::PassWrappedPlatformHandle(pipe.release().value(), &pipe_handle);
@@ -80,12 +80,12 @@
                             base::Passed(std::move(pipe_handle))));
 }
 
-void ArcCrashCollectorBridge::SetBuildProperties(const mojo::String& device,
-                                                 const mojo::String& board,
-                                                 const mojo::String& cpu_abi) {
-  device_ = device.get();
-  board_ = board.get();
-  cpu_abi_ = cpu_abi.get();
+void ArcCrashCollectorBridge::SetBuildProperties(const std::string& device,
+                                                 const std::string& board,
+                                                 const std::string& cpu_abi) {
+  device_ = device;
+  board_ = board;
+  cpu_abi_ = cpu_abi;
 }
 
 }  // namespace arc
diff --git a/components/arc/crash_collector/arc_crash_collector_bridge.h b/components/arc/crash_collector/arc_crash_collector_bridge.h
index 6bc78c49..ec4fcdf 100644
--- a/components/arc/crash_collector/arc_crash_collector_bridge.h
+++ b/components/arc/crash_collector/arc_crash_collector_bridge.h
@@ -13,7 +13,6 @@
 #include "components/arc/common/crash_collector.mojom.h"
 #include "components/arc/instance_holder.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/public/cpp/bindings/string.h"
 
 namespace base {
 class TaskRunner;
@@ -37,11 +36,11 @@
   void OnInstanceReady() override;
 
   // mojom::CrashCollectorHost overrides.
-  void DumpCrash(const mojo::String& type, mojo::ScopedHandle pipe) override;
+  void DumpCrash(const std::string& type, mojo::ScopedHandle pipe) override;
 
-  void SetBuildProperties(const mojo::String& device,
-                          const mojo::String& board,
-                          const mojo::String& cpu_abi) override;
+  void SetBuildProperties(const std::string& device,
+                          const std::string& board,
+                          const std::string& cpu_abi) override;
 
  private:
   scoped_refptr<base::TaskRunner> blocking_task_runner_;
diff --git a/components/certificate_reporting/BUILD.gn b/components/certificate_reporting/BUILD.gn
index eee31993..74d4887 100644
--- a/components/certificate_reporting/BUILD.gn
+++ b/components/certificate_reporting/BUILD.gn
@@ -20,6 +20,7 @@
   ]
   deps = [
     "//base",
+    "//components/network_time",
     "//crypto",
     "//net",
     "//url",
@@ -48,6 +49,9 @@
   deps = [
     ":certificate_reporting",
     "//base",
+    "//components/network_time",
+    "//components/network_time:network_time_test_support",
+    "//components/prefs:test_support",
     "//net:test_support",
     "//testing/gtest",
   ]
diff --git a/components/certificate_reporting/DEPS b/components/certificate_reporting/DEPS
index 8078553..e864d38a 100644
--- a/components/certificate_reporting/DEPS
+++ b/components/certificate_reporting/DEPS
@@ -1,4 +1,6 @@
 include_rules = [
+  "+components/network_time",
+  "+components/prefs",
   "+crypto",
   "+net"
 ]
\ No newline at end of file
diff --git a/components/certificate_reporting/cert_logger.proto b/components/certificate_reporting/cert_logger.proto
index 13dfda1..644ba99 100644
--- a/components/certificate_reporting/cert_logger.proto
+++ b/components/certificate_reporting/cert_logger.proto
@@ -44,6 +44,28 @@
   optional bool overridable = 3;
 }
 
+// Contains information about features that are enabled/disabled that
+// might affect certificate validation.
+message CertLoggerFeaturesInfo {
+  message NetworkTimeQueryingInfo {
+    // True if the network time querying feature is enabled.
+    optional bool network_time_queries_enabled = 1;
+
+    // The experimental parameter controlling the behavior of network time
+    // queries (whether they happen on-demand when a certificate date error is
+    // encountered, in the background, or both).
+    enum NetworkTimeFetchBehavior {
+      NETWORK_TIME_FETCHES_UNKNOWN = 0;
+      NETWORK_TIME_FETCHES_BACKGROUND_ONLY = 1;
+      NETWORK_TIME_FETCHES_ON_DEMAND_ONLY = 2;
+      NETWORK_TIME_FETCHES_IN_BACKGROUND_AND_ON_DEMAND = 3;
+    }
+    optional NetworkTimeFetchBehavior network_time_query_behavior = 2;
+  }
+
+  optional NetworkTimeQueryingInfo network_time_querying_info = 1;
+}
+
 message CertLoggerRequest {
   // The hostname being accessed (required as the cert could be valid for
   // multiple hosts, e.g. a wildcard or a SubjectAltName.
@@ -98,4 +120,8 @@
   // validation library built a trusted chain (i.e. the Chrome net stack set the
   // error, not the library).
   optional bool is_issued_by_known_root = 9;
+
+  // Information about features that were enabled or disabled for the
+  // user that might affect certificate validation.
+  optional CertLoggerFeaturesInfo features_info = 10;
 };
diff --git a/components/certificate_reporting/error_report.cc b/components/certificate_reporting/error_report.cc
index 18145e70..366932d 100644
--- a/components/certificate_reporting/error_report.cc
+++ b/components/certificate_reporting/error_report.cc
@@ -10,10 +10,13 @@
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
 #include "components/certificate_reporting/cert_logger.pb.h"
+#include "components/network_time/network_time_tracker.h"
 #include "net/cert/cert_status_flags.h"
 #include "net/cert/x509_certificate.h"
 #include "net/ssl/ssl_info.h"
 
+using network_time::NetworkTimeTracker;
+
 namespace certificate_reporting {
 
 namespace {
@@ -120,6 +123,40 @@
   interstitial_info->set_overridable(overridable == INTERSTITIAL_OVERRIDABLE);
 }
 
+void ErrorReport::AddNetworkTimeInfo(
+    const NetworkTimeTracker* network_time_tracker) {
+  CertLoggerFeaturesInfo* features_info = cert_report_->mutable_features_info();
+  CertLoggerFeaturesInfo::NetworkTimeQueryingInfo* network_time_info =
+      features_info->mutable_network_time_querying_info();
+  network_time_info->set_network_time_queries_enabled(
+      network_time_tracker->AreTimeFetchesEnabled());
+  NetworkTimeTracker::FetchBehavior behavior =
+      network_time_tracker->GetFetchBehavior();
+  CertLoggerFeaturesInfo::NetworkTimeQueryingInfo::NetworkTimeFetchBehavior
+      report_behavior = CertLoggerFeaturesInfo::NetworkTimeQueryingInfo::
+          NETWORK_TIME_FETCHES_UNKNOWN;
+
+  switch (behavior) {
+    case NetworkTimeTracker::FETCH_BEHAVIOR_UNKNOWN:
+      report_behavior = CertLoggerFeaturesInfo::NetworkTimeQueryingInfo::
+          NETWORK_TIME_FETCHES_UNKNOWN;
+      break;
+    case NetworkTimeTracker::FETCHES_IN_BACKGROUND_ONLY:
+      report_behavior = CertLoggerFeaturesInfo::NetworkTimeQueryingInfo::
+          NETWORK_TIME_FETCHES_BACKGROUND_ONLY;
+      break;
+    case NetworkTimeTracker::FETCHES_ON_DEMAND_ONLY:
+      report_behavior = CertLoggerFeaturesInfo::NetworkTimeQueryingInfo::
+          NETWORK_TIME_FETCHES_ON_DEMAND_ONLY;
+      break;
+    case NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND:
+      report_behavior = CertLoggerFeaturesInfo::NetworkTimeQueryingInfo::
+          NETWORK_TIME_FETCHES_IN_BACKGROUND_AND_ON_DEMAND;
+      break;
+  }
+  network_time_info->set_network_time_query_behavior(report_behavior);
+}
+
 const std::string& ErrorReport::hostname() const {
   return cert_report_->hostname();
 }
diff --git a/components/certificate_reporting/error_report.h b/components/certificate_reporting/error_report.h
index 0200dcd4..214fb7e 100644
--- a/components/certificate_reporting/error_report.h
+++ b/components/certificate_reporting/error_report.h
@@ -8,6 +8,9 @@
 #include <memory>
 #include <string>
 
+namespace network_time {
+class NetworkTimeTracker;
+}  // namespace network_time
 
 namespace net {
 class SSLInfo;
@@ -61,6 +64,9 @@
                            const ProceedDecision& proceed_decision,
                            const Overridable& overridable);
 
+  void AddNetworkTimeInfo(
+      const network_time::NetworkTimeTracker* network_time_tracker);
+
   // Gets the hostname to which this report corresponds.
   const std::string& hostname() const;
 
diff --git a/components/certificate_reporting/error_report_unittest.cc b/components/certificate_reporting/error_report_unittest.cc
index c49b544..df0dbef2 100644
--- a/components/certificate_reporting/error_report_unittest.cc
+++ b/components/certificate_reporting/error_report_unittest.cc
@@ -10,11 +10,17 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/path_service.h"
+#include "base/threading/thread.h"
+#include "base/time/default_clock.h"
+#include "base/time/default_tick_clock.h"
 #include "components/certificate_reporting/cert_logger.pb.h"
+#include "components/network_time/network_time_test_utils.h"
+#include "components/prefs/testing_pref_service.h"
 #include "net/cert/cert_status_flags.h"
 #include "net/ssl/ssl_info.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/test_data_directory.h"
+#include "net/url_request/url_request_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -178,6 +184,50 @@
       VerifyErrorReportSerialization(report_known, ssl_info, cert_errors));
 }
 
+// Tests that information about relevant features are included in the
+// report.
+TEST(ErrorReportTest, FeatureInfo) {
+  base::Thread io_thread("IO thread");
+  base::Thread::Options thread_options;
+  thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
+  EXPECT_TRUE(io_thread.StartWithOptions(thread_options));
+
+  std::unique_ptr<network_time::FieldTrialTest> field_trial_test(
+      network_time::FieldTrialTest::CreateForUnitTest());
+  field_trial_test->SetNetworkQueriesWithVariationsService(
+      true, 0.0, network_time::NetworkTimeTracker::FETCHES_ON_DEMAND_ONLY);
+
+  TestingPrefServiceSimple pref_service;
+  network_time::NetworkTimeTracker::RegisterPrefs(pref_service.registry());
+  network_time::NetworkTimeTracker network_time_tracker(
+      base::MakeUnique<base::DefaultClock>(),
+      base::MakeUnique<base::DefaultTickClock>(), &pref_service,
+      new net::TestURLRequestContextGetter(io_thread.task_runner()));
+
+  // Serialize a report containing information about the network time querying
+  // feature.
+  SSLInfo ssl_info;
+  ASSERT_NO_FATAL_FAILURE(
+      GetTestSSLInfo(INCLUDE_UNVERIFIED_CERT_CHAIN, &ssl_info, kCertStatus));
+  ErrorReport report(kDummyHostname, ssl_info);
+  report.AddNetworkTimeInfo(&network_time_tracker);
+  std::string serialized_report;
+  ASSERT_TRUE(report.Serialize(&serialized_report));
+
+  // Check that the report contains the network time querying feature
+  // information.
+  CertLoggerRequest parsed;
+  ASSERT_TRUE(parsed.ParseFromString(serialized_report));
+  EXPECT_TRUE(parsed.features_info()
+                  .network_time_querying_info()
+                  .network_time_queries_enabled());
+  EXPECT_EQ(CertLoggerFeaturesInfo::NetworkTimeQueryingInfo::
+                NETWORK_TIME_FETCHES_ON_DEMAND_ONLY,
+            parsed.features_info()
+                .network_time_querying_info()
+                .network_time_query_behavior());
+}
+
 }  // namespace
 
 }  // namespace certificate_reporting
diff --git a/components/content_settings/core/browser/BUILD.gn b/components/content_settings/core/browser/BUILD.gn
index 536cd3d..3dec4a13 100644
--- a/components/content_settings/core/browser/BUILD.gn
+++ b/components/content_settings/core/browser/BUILD.gn
@@ -51,6 +51,7 @@
     "//components/pref_registry:pref_registry",
     "//components/prefs",
     "//components/url_formatter",
+    "//extensions/features",
     "//net",
     "//url",
   ]
@@ -85,6 +86,7 @@
     "//components/content_settings/core/test:test_support",
     "//components/pref_registry:test_support",
     "//components/prefs",
+    "//extensions/features",
     "//testing/gtest",
     "//url",
   ]
diff --git a/components/content_settings/core/browser/DEPS b/components/content_settings/core/browser/DEPS
index 485e8ed..dc52ba40 100644
--- a/components/content_settings/core/browser/DEPS
+++ b/components/content_settings/core/browser/DEPS
@@ -4,6 +4,7 @@
   "+components/keyed_service/core",
   "+components/pref_registry",
   "+components/url_formatter",
+  "+extensions/features",
   "+net/base",
   "+net/cookies",
 ]
diff --git a/components/content_settings/core/browser/cookie_settings.cc b/components/content_settings/core/browser/cookie_settings.cc
index 2d5730ef..b5db52bd 100644
--- a/components/content_settings/core/browser/cookie_settings.cc
+++ b/components/content_settings/core/browser/cookie_settings.cc
@@ -12,6 +12,7 @@
 #include "components/content_settings/core/common/pref_names.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
+#include "extensions/features/features.h"
 #include "net/base/net_errors.h"
 #include "net/base/static_cookie_policy.h"
 #include "url/gurl.h"
@@ -129,7 +130,7 @@
   if (url.SchemeIsCryptographic() && first_party_url.SchemeIs(kChromeUIScheme))
     return CONTENT_SETTING_ALLOW;
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (url.SchemeIs(kExtensionScheme) &&
       first_party_url.SchemeIs(kExtensionScheme)) {
     return CONTENT_SETTING_ALLOW;
diff --git a/components/content_settings/core/browser/cookie_settings_unittest.cc b/components/content_settings/core/browser/cookie_settings_unittest.cc
index 77560e1..79a2f738 100644
--- a/components/content_settings/core/browser/cookie_settings_unittest.cc
+++ b/components/content_settings/core/browser/cookie_settings_unittest.cc
@@ -8,6 +8,7 @@
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/content_settings/core/common/pref_names.h"
 #include "components/pref_registry/testing_pref_service_syncable.h"
+#include "extensions/features/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -55,7 +56,7 @@
   EXPECT_FALSE(cookie_settings_->IsReadingCookieAllowed(kHttpSite, kChromeURL));
   EXPECT_TRUE(cookie_settings_->IsReadingCookieAllowed(kHttpsSite, kChromeURL));
   EXPECT_TRUE(cookie_settings_->IsReadingCookieAllowed(kChromeURL, kHttpSite));
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   EXPECT_TRUE(
       cookie_settings_->IsReadingCookieAllowed(kExtensionURL, kExtensionURL));
 #else
@@ -212,7 +213,7 @@
 TEST_F(CookieSettingsTest, ExtensionsOwnCookies) {
   cookie_settings_->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);
 
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Extensions can always use cookies (and site data) in their own origin.
   EXPECT_TRUE(
       cookie_settings_->IsReadingCookieAllowed(kExtensionURL, kExtensionURL));
diff --git a/components/exo/wayland/clients/motion_events.cc b/components/exo/wayland/clients/motion_events.cc
index f32b130..6d913b4 100644
--- a/components/exo/wayland/clients/motion_events.cc
+++ b/components/exo/wayland/clients/motion_events.cc
@@ -11,7 +11,9 @@
 #include <wayland-client-core.h>
 #include <wayland-client-protocol.h>
 
+#include <deque>
 #include <iostream>
+#include <string>
 #include <vector>
 
 #include "base/at_exit.h"
@@ -21,6 +23,7 @@
 #include "base/memory/shared_memory.h"
 #include "base/scoped_generic.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
 #include "base/time/time.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
@@ -39,6 +42,7 @@
 #if defined(OZONE_PLATFORM_GBM)
 #include <drm_fourcc.h>
 #include <gbm.h>
+#include <xf86drm.h>
 #endif
 
 // Convenient macro that is used to define default deleters for object
@@ -90,12 +94,12 @@
 const size_t kBytesPerPixel = 4;
 
 #if defined(OZONE_PLATFORM_GBM)
-// DRI render node path.
-const char kDriRenderNode[] = "/dev/dri/renderD128";
+// DRI render node path template.
+const char kDriRenderNodeTemplate[] = "/dev/dri/renderD%u";
 #endif
 
 // Number of buffers.
-const size_t kBuffers = 3;
+const size_t kBuffers = 8;
 
 // Rotation speed (degrees/second).
 const double kRotationSpeed = 360.0;
@@ -297,29 +301,30 @@
                size_t height,
                int scale,
                size_t num_rects,
-               bool use_drm,
-               bool fullscreen)
+               size_t max_frames_pending,
+               bool fullscreen,
+               const std::string* use_drm)
       : width_(width),
         height_(height),
         scale_(scale),
         num_rects_(num_rects),
-        use_drm_(use_drm),
-        fullscreen_(fullscreen) {}
+        max_frames_pending_(max_frames_pending),
+        fullscreen_(fullscreen),
+        use_drm_(use_drm) {}
 
   // Initialize and run client main loop.
   int Run();
 
  private:
   bool CreateBuffer(Buffer* buffer);
-  size_t stride() const { return width_ * kBytesPerPixel; }
-  size_t buffer_size() const { return stride() * height_; }
 
   const size_t width_;
   const size_t height_;
   const int scale_;
   const size_t num_rects_;
-  const bool use_drm_;
+  const size_t max_frames_pending_;
   const bool fullscreen_;
+  const std::string* use_drm_;
 
   Globals globals_;
   std::unique_ptr<wl_display> display_;
@@ -351,10 +356,11 @@
   gl_surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size());
   gl_context_ =
       gl::init::CreateGLContext(nullptr,  // share_group
-                                gl_surface_.get(), gl::PreferIntegratedGpu);
+                                gl_surface_.get(), gl::GLContextAttribs());
 
   make_current_.reset(
       new ui::ScopedMakeCurrent(gl_context_.get(), gl_surface_.get()));
+
   wl_registry_listener registry_listener = {RegistryHandler, RegistryRemover};
 
   wl_registry* registry = wl_display_get_registry(display_.get());
@@ -383,24 +389,59 @@
     return 1;
   }
 
+  EGLenum egl_sync_type = 0;
 #if defined(OZONE_PLATFORM_GBM)
-  drm_fd_.reset(open(kDriRenderNode, O_RDWR));
-  if (drm_fd_.get() < 0) {
-    LOG(ERROR) << "Can't open drm device '" << kDriRenderNode << "'";
-    return 1;
+  if (use_drm_) {
+    // Number of files to look for when discovering DRM devices.
+    const uint32_t kDrmMaxMinor = 15;
+    const uint32_t kRenderNodeStart = 128;
+    const uint32_t kRenderNodeEnd = kRenderNodeStart + kDrmMaxMinor;
+
+    for (uint32_t i = kRenderNodeStart; i < kRenderNodeEnd; i++) {
+      std::string dri_render_node(
+          base::StringPrintf(kDriRenderNodeTemplate, i));
+      base::ScopedFD drm_fd(open(dri_render_node.c_str(), O_RDWR));
+      if (drm_fd.get() < 0)
+        continue;
+      drmVersionPtr drm_version = drmGetVersion(drm_fd.get());
+      if (!drm_version) {
+        LOG(ERROR) << "Can't get version for device: '" << dri_render_node
+                   << "'";
+        return 1;
+      }
+      if (strstr(drm_version->name, use_drm_->c_str())) {
+        drm_fd_ = std::move(drm_fd);
+        break;
+      }
+    }
+
+    if (drm_fd_.get() < 0) {
+      LOG_IF(ERROR, use_drm_) << "Can't find drm device: '" << *use_drm_ << "'";
+      LOG_IF(ERROR, !use_drm_) << "Can't find drm device";
+      return 1;
+    }
+
+    device_.reset(gbm_create_device(drm_fd_.get()));
+    if (!device_) {
+      LOG(ERROR) << "Can't create gbm device";
+      return 1;
+    }
   }
 
-  device_.reset(gbm_create_device(drm_fd_.get()));
-  if (!device_) {
-    LOG(ERROR) << "Can't create gbm device";
-    return 1;
-  }
   sk_sp<const GrGLInterface> native_interface(GrGLCreateNativeInterface());
   DCHECK(native_interface);
   gr_context_ = sk_sp<GrContext>(GrContext::Create(
       kOpenGL_GrBackend,
       reinterpret_cast<GrBackendContext>(native_interface.get())));
   DCHECK(gr_context_);
+
+  if (gl::GLSurfaceEGL::HasEGLExtension("EGL_EXT_image_flush_external") ||
+      gl::GLSurfaceEGL::HasEGLExtension("EGL_ARM_implicit_external_sync")) {
+    egl_sync_type = EGL_SYNC_FENCE_KHR;
+  }
+  if (gl::GLSurfaceEGL::HasEGLExtension("EGL_ANDROID_native_fence_sync")) {
+    egl_sync_type = EGL_SYNC_NATIVE_FENCE_ANDROID;
+  }
 #endif
 
   wl_buffer_listener buffer_listener = {BufferRelease};
@@ -482,89 +523,131 @@
   Frame frame;
   std::unique_ptr<wl_callback> frame_callback;
   wl_callback_listener frame_listener = {FrameCallback};
+  std::deque<wl_buffer*> pending_frames;
 
   uint32_t frames = 0;
   base::TimeTicks benchmark_time = base::TimeTicks::Now();
   base::TimeDelta benchmark_interval =
       base::TimeDelta::FromSeconds(kBenchmarkInterval);
+  base::TimeDelta benchmark_wall_time;
+  base::TimeDelta benchmark_cpu_time;
 
+  int dispatch_status = 0;
   do {
-    if (frame.callback_pending)
-      continue;
-
-    Buffer* buffer =
-        std::find_if(std::begin(buffers_), std::end(buffers_),
-                     [](const Buffer& buffer) { return !buffer.busy; });
-    if (buffer == std::end(buffers_))
-      continue;
-
-    base::TimeTicks current_time = base::TimeTicks::Now();
-    if ((current_time - benchmark_time) > benchmark_interval) {
-      std::cout << frames << " frames in " << benchmark_interval.InSeconds()
-                << " seconds: "
-                << static_cast<double>(frames) / benchmark_interval.InSeconds()
-                << " fps" << std::endl;
-      benchmark_time = current_time;
-      frames = 0;
-    }
-
-    SkCanvas* canvas = buffer->sk_surface->getCanvas();
-    canvas->save();
-
-    if (event_times.empty()) {
-      canvas->clear(SK_ColorBLACK);
-    } else {
-      // Split buffer into one horizontal rectangle for each event received
-      // since last frame. Latest event at the top.
-      int y = 0;
-      // Note: Rounding up to ensure we cover the whole canvas.
-      int h = (height_ + (event_times.size() / 2)) / event_times.size();
-      while (!event_times.empty()) {
-        SkIRect rect = SkIRect::MakeXYWH(0, y, width_, h);
-        SkPaint paint;
-        paint.setColor(SkColorSetRGB((event_times.back() & 0x0000ff) >> 0,
-                                     (event_times.back() & 0x00ff00) >> 8,
-                                     (event_times.back() & 0xff0000) >> 16));
-        canvas->drawIRect(rect, paint);
-        event_times.pop_back();
-        y += h;
+    bool enqueue_frame = frame.callback_pending
+                             ? pending_frames.size() < max_frames_pending_
+                             : pending_frames.empty();
+    if (enqueue_frame) {
+      Buffer* buffer =
+          std::find_if(std::begin(buffers_), std::end(buffers_),
+                       [](const Buffer& buffer) { return !buffer.busy; });
+      if (buffer == std::end(buffers_)) {
+        LOG(ERROR) << "Can't find free buffer";
+        return 1;
       }
+
+      base::TimeTicks wall_time_start = base::TimeTicks::Now();
+      if ((wall_time_start - benchmark_time) > benchmark_interval) {
+        // Print benchmark statistics for the frames produced.
+        // Note: frames produced is not necessarily the same as frames
+        // displayed.
+        std::cout << frames << " frames in " << benchmark_interval.InSeconds()
+                  << " seconds: " << frames / benchmark_interval.InSecondsF()
+                  << " fps (wall="
+                  << benchmark_wall_time.InMillisecondsF() / frames
+                  << " cpu=" << benchmark_cpu_time.InMillisecondsF() / frames
+                  << ")" << std::endl;
+
+        frames = 0;
+        benchmark_time = wall_time_start;
+        benchmark_wall_time = base::TimeDelta();
+        benchmark_cpu_time = base::TimeDelta();
+      }
+
+      base::ThreadTicks cpu_time_start = base::ThreadTicks::Now();
+
+      SkCanvas* canvas = buffer->sk_surface->getCanvas();
+      canvas->save();
+
+      if (event_times.empty()) {
+        canvas->clear(SK_ColorBLACK);
+      } else {
+        // Split buffer into one horizontal rectangle for each event received
+        // since last frame. Latest event at the top.
+        int y = 0;
+        // Note: Rounding up to ensure we cover the whole canvas.
+        int h = (height_ + (event_times.size() / 2)) / event_times.size();
+        while (!event_times.empty()) {
+          SkIRect rect = SkIRect::MakeXYWH(0, y, width_, h);
+          SkPaint paint;
+          paint.setColor(SkColorSetRGB((event_times.back() & 0x0000ff) >> 0,
+                                       (event_times.back() & 0x00ff00) >> 8,
+                                       (event_times.back() & 0xff0000) >> 16));
+          canvas->drawIRect(rect, paint);
+          event_times.pop_back();
+          y += h;
+        }
+      }
+
+      // Draw rotating rects.
+      SkScalar half_width = SkScalarHalf(width_);
+      SkScalar half_height = SkScalarHalf(height_);
+      SkIRect rect = SkIRect::MakeXYWH(-SkScalarHalf(half_width),
+                                       -SkScalarHalf(half_height), half_width,
+                                       half_height);
+      SkScalar rotation = SkScalarMulDiv(frame.time, kRotationSpeed, 1000);
+      SkPaint paint;
+      canvas->translate(half_width, half_height);
+      for (size_t i = 0; i < num_rects_; ++i) {
+        const SkColor kColors[] = {SK_ColorBLUE, SK_ColorGREEN,
+                                   SK_ColorRED,  SK_ColorYELLOW,
+                                   SK_ColorCYAN, SK_ColorMAGENTA};
+        paint.setColor(SkColorSetA(kColors[i % arraysize(kColors)], 0xA0));
+        canvas->rotate(rotation / num_rects_);
+        canvas->drawIRect(rect, paint);
+      }
+
+      canvas->restore();
+      if (gr_context_) {
+        gr_context_->flush();
+        glFlush();
+
+        if (egl_sync_type) {
+          EGLSyncKHR sync =
+              eglCreateSyncKHR(eglGetCurrentDisplay(), egl_sync_type, nullptr);
+          DCHECK(sync != EGL_NO_SYNC_KHR);
+          eglClientWaitSyncKHR(eglGetCurrentDisplay(), sync,
+                               EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
+                               EGL_FOREVER_KHR);
+          eglDestroySyncKHR(eglGetCurrentDisplay(), sync);
+        }
+      }
+
+      buffer->busy = true;
+      pending_frames.push_back(buffer->buffer.get());
+
+      ++frames;
+      benchmark_wall_time += base::TimeTicks::Now() - wall_time_start;
+      benchmark_cpu_time += base::ThreadTicks::Now() - cpu_time_start;
+      continue;
     }
 
-    // Draw rotating rects.
-    SkScalar half_width = SkScalarHalf(width_);
-    SkScalar half_height = SkScalarHalf(height_);
-    SkIRect rect =
-        SkIRect::MakeXYWH(-SkScalarHalf(half_width), -SkScalarHalf(half_height),
-                          half_width, half_height);
-    SkScalar rotation = SkScalarMulDiv(frame.time, kRotationSpeed, 1000);
-    SkPaint paint;
-    canvas->translate(half_width, half_height);
-    for (size_t i = 0; i < num_rects_; ++i) {
-      const SkColor kColors[] = {SK_ColorBLUE, SK_ColorGREEN,
-                                 SK_ColorRED,  SK_ColorYELLOW,
-                                 SK_ColorCYAN, SK_ColorMAGENTA};
-      paint.setColor(SkColorSetA(kColors[i % arraysize(kColors)], 0xA0));
-      canvas->rotate(rotation / num_rects_);
-      canvas->drawIRect(rect, paint);
+    if (!frame.callback_pending) {
+      DCHECK_GT(pending_frames.size(), 0u);
+      wl_surface_set_buffer_scale(surface.get(), scale_);
+      wl_surface_attach(surface.get(), pending_frames.front(), 0, 0);
+      pending_frames.pop_front();
+
+      frame_callback.reset(wl_surface_frame(surface.get()));
+      wl_callback_add_listener(frame_callback.get(), &frame_listener, &frame);
+      frame.callback_pending = true;
+      wl_surface_commit(surface.get());
+      wl_display_flush(display_.get());
+      continue;
     }
-    canvas->restore();
-    if (gr_context_) {
-      gr_context_->flush();
-      glFinish();
-    }
-    wl_surface_set_buffer_scale(surface.get(), scale_);
-    wl_surface_attach(surface.get(), buffer->buffer.get(), 0, 0);
-    buffer->busy = true;
 
-    frame_callback.reset(wl_surface_frame(surface.get()));
-    wl_callback_add_listener(frame_callback.get(), &frame_listener, &frame);
-    frame.callback_pending = true;
-
-    ++frames;
-
-    wl_surface_commit(surface.get());
-  } while (wl_display_dispatch(display_.get()) != -1);
+    dispatch_status = wl_display_dispatch(display_.get());
+  } while (dispatch_status != -1);
 
   return 0;
 }
@@ -586,8 +669,9 @@
         zwp_linux_dmabuf_v1_create_params(globals_.linux_dmabuf.get()));
     zwp_linux_buffer_params_v1_add_listener(buffer->params.get(),
                                             &params_listener, buffer);
-    zwp_linux_buffer_params_v1_add(buffer->params.get(), fd.get(), 0, 0,
-                                   stride(), 0, 0);
+    uint32_t stride = gbm_bo_get_stride(buffer->bo.get());
+    zwp_linux_buffer_params_v1_add(buffer->params.get(), fd.get(), 0, 0, stride,
+                                   0, 0);
     zwp_linux_buffer_params_v1_create(buffer->params.get(), width_, height_,
                                       kDrmFormat, 0);
 
@@ -600,7 +684,7 @@
                                 EGL_LINUX_DRM_FOURCC_EXT,
                                 kDrmFormat,
                                 EGL_DMA_BUF_PLANE0_PITCH_EXT,
-                                stride(),
+                                stride,
                                 EGL_DMA_BUF_PLANE0_OFFSET_EXT,
                                 0,
                                 EGL_NONE};
@@ -634,13 +718,15 @@
     return true;
   }
 #endif
-  buffer->shared_memory.CreateAndMapAnonymous(buffer_size());
+
+  size_t stride = width_ * kBytesPerPixel;
+  buffer->shared_memory.CreateAndMapAnonymous(stride * height_);
   buffer->shm_pool.reset(
       wl_shm_create_pool(globals_.shm.get(), buffer->shared_memory.handle().fd,
                          buffer->shared_memory.requested_size()));
 
   buffer->buffer.reset(static_cast<wl_buffer*>(wl_shm_pool_create_buffer(
-      buffer->shm_pool.get(), 0, width_, height_, stride(), kShmFormat)));
+      buffer->shm_pool.get(), 0, width_, height_, stride, kShmFormat)));
   if (!buffer->buffer) {
     LOG(ERROR) << "Can't create buffer";
     return false;
@@ -648,7 +734,7 @@
 
   buffer->sk_surface = SkSurface::MakeRasterDirect(
       SkImageInfo::Make(width_, height_, kColorType, kOpaque_SkAlphaType),
-      static_cast<uint8_t*>(buffer->shared_memory.memory()), stride());
+      static_cast<uint8_t*>(buffer->shared_memory.memory()), stride);
   DCHECK(buffer->sk_surface);
   return true;
 }
@@ -668,12 +754,15 @@
 // Specifies the number of rotating rects to draw.
 const char kNumRects[] = "num-rects";
 
-// Use drm buffer instead of shared memory.
-const char kUseDrm[] = "use-drm";
+// Specifies the maximum number of pending frames.
+const char kMaxFramesPending[] = "max-frames-pending";
 
 // Specifies if client should be fullscreen.
 const char kFullscreen[] = "fullscreen";
 
+// Use drm buffer instead of shared memory.
+const char kUseDrm[] = "use-drm";
+
 }  // namespace switches
 
 int main(int argc, char* argv[]) {
@@ -707,10 +796,23 @@
     return 1;
   }
 
-  bool use_drm = command_line->HasSwitch(switches::kUseDrm);
-  bool fullscreen = command_line->HasSwitch(switches::kFullscreen);
+  size_t max_frames_pending = 0;
+  if (command_line->HasSwitch(switches::kMaxFramesPending) &&
+      (!base::StringToSizeT(
+          command_line->GetSwitchValueASCII(switches::kMaxFramesPending),
+          &max_frames_pending))) {
+    LOG(ERROR) << "Invalid value for " << switches::kMaxFramesPending;
+    return 1;
+  }
 
-  exo::wayland::clients::MotionEvents client(width, height, scale, num_rects,
-                                             use_drm, fullscreen);
+  std::unique_ptr<std::string> use_drm;
+  if (command_line->HasSwitch(switches::kUseDrm)) {
+    use_drm.reset(
+        new std::string(command_line->GetSwitchValueASCII(switches::kUseDrm)));
+  }
+
+  exo::wayland::clients::MotionEvents client(
+      width, height, scale, num_rects, max_frames_pending,
+      command_line->HasSwitch(switches::kFullscreen), use_drm.get());
   return client.Run();
 }
diff --git a/components/filesystem/BUILD.gn b/components/filesystem/BUILD.gn
index 617f312..85c78cc 100644
--- a/components/filesystem/BUILD.gn
+++ b/components/filesystem/BUILD.gn
@@ -88,7 +88,6 @@
 }
 
 service_manifest("test_manifest") {
-  type = "exe"
   name = "filesystem_service_unittests"
   source = "test_manifest.json"
 }
diff --git a/components/filesystem/files_test_base.cc b/components/filesystem/files_test_base.cc
index 1868241..ba240f74 100644
--- a/components/filesystem/files_test_base.cc
+++ b/components/filesystem/files_test_base.cc
@@ -13,7 +13,7 @@
 namespace filesystem {
 
 FilesTestBase::FilesTestBase()
-    : ServiceTest("exe:filesystem_service_unittests") {
+    : ServiceTest("service:filesystem_service_unittests") {
 }
 
 FilesTestBase::~FilesTestBase() {
diff --git a/components/filesystem/test_manifest.json b/components/filesystem/test_manifest.json
index a24c68397..9c15723 100644
--- a/components/filesystem/test_manifest.json
+++ b/components/filesystem/test_manifest.json
@@ -1,5 +1,5 @@
 {
-  "name": "exe:filesystem_service_unittests",
+  "name": "service:filesystem_service_unittests",
   "display_name": "Filesystem Service Unittests",
   "interface_provider_specs": {
     "service_manager:connector": {
diff --git a/components/leveldb/BUILD.gn b/components/leveldb/BUILD.gn
index 16c1054..db4f708 100644
--- a/components/leveldb/BUILD.gn
+++ b/components/leveldb/BUILD.gn
@@ -87,7 +87,6 @@
 }
 
 service_manifest("test_manifest") {
-  type = "exe"
   name = "leveldb_service_unittests"
   source = "test_manifest.json"
 }
diff --git a/components/leveldb/leveldb_service_unittest.cc b/components/leveldb/leveldb_service_unittest.cc
index c7c3612..02c8884a 100644
--- a/components/leveldb/leveldb_service_unittest.cc
+++ b/components/leveldb/leveldb_service_unittest.cc
@@ -105,7 +105,7 @@
 
 class LevelDBServiceTest : public service_manager::test::ServiceTest {
  public:
-  LevelDBServiceTest() : ServiceTest("exe:leveldb_service_unittests") {}
+  LevelDBServiceTest() : ServiceTest("service:leveldb_service_unittests") {}
   ~LevelDBServiceTest() override {}
 
  protected:
diff --git a/components/leveldb/remote_iterator_unittest.cc b/components/leveldb/remote_iterator_unittest.cc
index e0c1b0a..2ac4390 100644
--- a/components/leveldb/remote_iterator_unittest.cc
+++ b/components/leveldb/remote_iterator_unittest.cc
@@ -41,7 +41,7 @@
 
 class RemoteIteratorTest : public service_manager::test::ServiceTest {
  public:
-  RemoteIteratorTest() : ServiceTest("exe:leveldb_service_unittests") {}
+  RemoteIteratorTest() : ServiceTest("service:leveldb_service_unittests") {}
   ~RemoteIteratorTest() override {}
 
  protected:
diff --git a/components/leveldb/test_manifest.json b/components/leveldb/test_manifest.json
index 696c4bb..02fd58a 100644
--- a/components/leveldb/test_manifest.json
+++ b/components/leveldb/test_manifest.json
@@ -1,5 +1,5 @@
 {
-  "name": "exe:leveldb_service_unittests",
+  "name": "service:leveldb_service_unittests",
   "display_name": "LevelDB Service Unittests",
   "interface_provider_specs": {
     "service_manager:connector": {
diff --git a/components/network_time/BUILD.gn b/components/network_time/BUILD.gn
index ef46b4ac..2befc5e 100644
--- a/components/network_time/BUILD.gn
+++ b/components/network_time/BUILD.gn
@@ -48,6 +48,7 @@
   ]
 
   deps = [
+    ":network_time",
     "//base",
     "//base/test:test_support",
     "//components/variations",
diff --git a/components/network_time/network_time_test_utils.cc b/components/network_time/network_time_test_utils.cc
index 38267b2..b8203a4e 100644
--- a/components/network_time/network_time_test_utils.cc
+++ b/components/network_time/network_time_test_utils.cc
@@ -66,7 +66,7 @@
 void FieldTrialTest::SetNetworkQueriesWithVariationsService(
     bool enable,
     float query_probability,
-    FetchBehavior fetch_behavior) {
+    NetworkTimeTracker::FetchBehavior fetch_behavior) {
   const std::string kTrialName = "Trial";
   const std::string kGroupName = "group";
   const base::Feature kFeature{"NetworkTimeServiceQuerying",
@@ -80,13 +80,17 @@
   params["CheckTimeIntervalSeconds"] = base::Int64ToString(360);
   std::string fetch_behavior_param;
   switch (fetch_behavior) {
-    case FETCHES_IN_BACKGROUND_ONLY:
+    case NetworkTimeTracker::FETCH_BEHAVIOR_UNKNOWN:
+      NOTREACHED();
+      fetch_behavior_param = "unknown";
+      break;
+    case NetworkTimeTracker::FETCHES_IN_BACKGROUND_ONLY:
       fetch_behavior_param = "background-only";
       break;
-    case FETCHES_ON_DEMAND_ONLY:
+    case NetworkTimeTracker::FETCHES_ON_DEMAND_ONLY:
       fetch_behavior_param = "on-demand-only";
       break;
-    case FETCHES_IN_BACKGROUND_AND_ON_DEMAND:
+    case NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND:
       fetch_behavior_param = "background-and-on-demand";
       break;
   }
diff --git a/components/network_time/network_time_test_utils.h b/components/network_time/network_time_test_utils.h
index c7aa18c..51b3c4f7 100644
--- a/components/network_time/network_time_test_utils.h
+++ b/components/network_time/network_time_test_utils.h
@@ -9,6 +9,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "components/network_time/network_time_tracker.h"
 
 namespace base {
 namespace test {
@@ -52,12 +53,6 @@
 // Allows tests to configure the network time queries field trial.
 class FieldTrialTest {
  public:
-  enum FetchBehavior {
-    FETCHES_IN_BACKGROUND_ONLY,
-    FETCHES_ON_DEMAND_ONLY,
-    FETCHES_IN_BACKGROUND_AND_ON_DEMAND,
-  };
-
   virtual ~FieldTrialTest();
 
   // A FieldTrialList exists as a global singleton. Use
@@ -67,9 +62,10 @@
   static FieldTrialTest* CreateForUnitTest();
   static FieldTrialTest* CreateForBrowserTest();
 
-  void SetNetworkQueriesWithVariationsService(bool enable,
-                                              float query_probability,
-                                              FetchBehavior fetch_behavior);
+  void SetNetworkQueriesWithVariationsService(
+      bool enable,
+      float query_probability,
+      NetworkTimeTracker::FetchBehavior fetch_behavior);
 
  private:
   FieldTrialTest();
diff --git a/components/network_time/network_time_tracker.cc b/components/network_time/network_time_tracker.cc
index 3b142e4..89d2ad7 100644
--- a/components/network_time/network_time_tracker.cc
+++ b/components/network_time/network_time_tracker.cc
@@ -157,26 +157,6 @@
   size_t limit_;
 };
 
-bool BackgroundQueriesEnabled() {
-  if (!base::FeatureList::IsEnabled(kNetworkTimeServiceQuerying)) {
-    return false;
-  }
-
-  const std::string param = variations::GetVariationParamValueByFeature(
-      kNetworkTimeServiceQuerying, kVariationsServiceFetchBehavior);
-  return param == "background-only" || param == "background-and-on-demand";
-}
-
-bool OnDemandQueriesEnabled() {
-  if (!base::FeatureList::IsEnabled(kNetworkTimeServiceQuerying)) {
-    return false;
-  }
-
-  const std::string param = variations::GetVariationParamValueByFeature(
-      kNetworkTimeServiceQuerying, kVariationsServiceFetchBehavior);
-  return param == "on-demand-only" || param == "background-and-on-demand";
-}
-
 base::TimeDelta CheckTimeInterval() {
   int64_t seconds;
   const std::string param = variations::GetVariationParamValueByFeature(
@@ -308,6 +288,23 @@
   pref_service_->Set(prefs::kNetworkTimeMapping, time_mapping);
 }
 
+bool NetworkTimeTracker::AreTimeFetchesEnabled() const {
+  return base::FeatureList::IsEnabled(kNetworkTimeServiceQuerying);
+}
+
+NetworkTimeTracker::FetchBehavior NetworkTimeTracker::GetFetchBehavior() const {
+  const std::string param = variations::GetVariationParamValueByFeature(
+      kNetworkTimeServiceQuerying, kVariationsServiceFetchBehavior);
+  if (param == "background-only") {
+    return FETCHES_IN_BACKGROUND_ONLY;
+  } else if (param == "on-demand-only") {
+    return FETCHES_ON_DEMAND_ONLY;
+  } else if (param == "background-and-on-demand") {
+    return FETCHES_IN_BACKGROUND_AND_ON_DEMAND;
+  }
+  return FETCH_BEHAVIOR_UNKNOWN;
+}
+
 void NetworkTimeTracker::SetTimeServerURLForTesting(const GURL& url) {
   server_url_ = url;
 }
@@ -412,7 +409,9 @@
 
 bool NetworkTimeTracker::StartTimeFetch(const base::Closure& closure) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (!OnDemandQueriesEnabled()) {
+  FetchBehavior behavior = GetFetchBehavior();
+  if (behavior != FETCHES_ON_DEMAND_ONLY &&
+      behavior != FETCHES_IN_BACKGROUND_AND_ON_DEMAND) {
     return false;
   }
 
@@ -572,14 +571,16 @@
 
 void NetworkTimeTracker::QueueCheckTime(base::TimeDelta delay) {
   // Check if the user is opted in to background time fetches.
-  if (BackgroundQueriesEnabled()) {
+  FetchBehavior behavior = GetFetchBehavior();
+  if (behavior == FETCHES_IN_BACKGROUND_ONLY ||
+      behavior == FETCHES_IN_BACKGROUND_AND_ON_DEMAND) {
     timer_.Start(FROM_HERE, delay, this, &NetworkTimeTracker::CheckTime);
   }
 }
 
 bool NetworkTimeTracker::ShouldIssueTimeQuery() {
   // Do not query the time service if not enabled via Variations Service.
-  if (!base::FeatureList::IsEnabled(kNetworkTimeServiceQuerying)) {
+  if (!AreTimeFetchesEnabled()) {
     return false;
   }
 
diff --git a/components/network_time/network_time_tracker.h b/components/network_time/network_time_tracker.h
index e296043..ea5bbc5 100644
--- a/components/network_time/network_time_tracker.h
+++ b/components/network_time/network_time_tracker.h
@@ -77,6 +77,19 @@
     NETWORK_TIME_SUBSEQUENT_SYNC_PENDING,
   };
 
+  // Describes the behavior of fetches to the network time service.
+  enum FetchBehavior {
+    // Only used in case of an unrecognize Finch experiment parameter.
+    FETCH_BEHAVIOR_UNKNOWN,
+    // Time queries will be issued in the background as needed.
+    FETCHES_IN_BACKGROUND_ONLY,
+    // Time queries will not be issued except when StartTimeFetch() is called.
+    FETCHES_ON_DEMAND_ONLY,
+    // Time queries will be issued both in the background as needed and also
+    // on-demand.
+    FETCHES_IN_BACKGROUND_AND_ON_DEMAND,
+  };
+
   static void RegisterPrefs(PrefRegistrySimple* registry);
 
   // Constructor.  Arguments may be stubbed out for tests.  |getter|, if not
@@ -124,6 +137,9 @@
                          base::TimeDelta latency,
                          base::TimeTicks post_time);
 
+  bool AreTimeFetchesEnabled() const;
+  FetchBehavior GetFetchBehavior() const;
+
   void SetMaxResponseSizeForTesting(size_t limit);
 
   void SetPublicKeyForTesting(const base::StringPiece& key);
diff --git a/components/network_time/network_time_tracker_unittest.cc b/components/network_time/network_time_tracker_unittest.cc
index b01469a8..391d87a 100644
--- a/components/network_time/network_time_tracker_unittest.cc
+++ b/components/network_time/network_time_tracker_unittest.cc
@@ -57,7 +57,7 @@
 
     field_trial_test_->SetNetworkQueriesWithVariationsService(
         true, 0.0 /* query probability */,
-        FieldTrialTest::FETCHES_IN_BACKGROUND_AND_ON_DEMAND);
+        NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND);
 
     tracker_.reset(new NetworkTimeTracker(
         std::unique_ptr<base::Clock>(clock_),
@@ -555,7 +555,7 @@
 // is not configured to allow on-demand time fetches.
 TEST_F(NetworkTimeTrackerTest, StartTimeFetchWithoutVariationsParam) {
   field_trial_test_->SetNetworkQueriesWithVariationsService(
-      true, 0.0, FieldTrialTest::FETCHES_IN_BACKGROUND_ONLY);
+      true, 0.0, NetworkTimeTracker::FETCHES_IN_BACKGROUND_ONLY);
   test_server_->RegisterRequestHandler(base::Bind(&GoodTimeResponseHandler));
   EXPECT_TRUE(test_server_->Start());
   tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
@@ -574,7 +574,7 @@
   tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
 
   field_trial_test_->SetNetworkQueriesWithVariationsService(
-      true, 0.0, FieldTrialTest::FETCHES_IN_BACKGROUND_AND_ON_DEMAND);
+      true, 0.0, NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND);
   base::Time in_network_time = clock_->Now();
   UpdateNetworkTime(in_network_time, resolution_, latency_,
                     tick_clock_->NowTicks());
@@ -586,7 +586,7 @@
             tracker_->GetTimerDelayForTesting());
 
   field_trial_test_->SetNetworkQueriesWithVariationsService(
-      true, 1.0, FieldTrialTest::FETCHES_IN_BACKGROUND_AND_ON_DEMAND);
+      true, 1.0, NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND);
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
   tracker_->WaitForFetchForTesting(123123123);
   EXPECT_EQ(base::TimeDelta::FromMinutes(60),
@@ -596,7 +596,7 @@
 TEST_F(NetworkTimeTrackerTest, NoNetworkQueryWhileFeatureDisabled) {
   // Disable network time queries and check that a query is not sent.
   field_trial_test_->SetNetworkQueriesWithVariationsService(
-      false, 0.0, FieldTrialTest::FETCHES_IN_BACKGROUND_AND_ON_DEMAND);
+      false, 0.0, NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND);
   EXPECT_FALSE(tracker_->QueryTimeServiceForTesting());
   // The timer is not started when the feature is disabled.
   EXPECT_EQ(base::TimeDelta::FromMinutes(0),
@@ -604,7 +604,7 @@
 
   // Enable time queries and check that a query is sent.
   field_trial_test_->SetNetworkQueriesWithVariationsService(
-      true, 0.0, FieldTrialTest::FETCHES_IN_BACKGROUND_AND_ON_DEMAND);
+      true, 0.0, NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND);
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
   tracker_->WaitForFetchForTesting(123123123);
 }
diff --git a/components/ntp_snippets/remote/ntp_snippets_service.cc b/components/ntp_snippets/remote/ntp_snippets_service.cc
index 21961e9..1f884ad0 100644
--- a/components/ntp_snippets/remote/ntp_snippets_service.cc
+++ b/components/ntp_snippets/remote/ntp_snippets_service.cc
@@ -438,21 +438,8 @@
   auto content_it = category_contents_.find(suggestion_id.category());
   DCHECK(content_it != category_contents_.end());
   CategoryContent* content = &content_it->second;
-  auto it = std::find_if(
-      content->snippets.begin(), content->snippets.end(),
-      [&suggestion_id](const std::unique_ptr<NTPSnippet>& snippet) {
-        return snippet->id() == suggestion_id.id_within_category();
-      });
-  if (it == content->snippets.end())
-    return;
-
-  (*it)->set_dismissed(true);
-
-  database_->SaveSnippet(**it);
-  database_->DeleteImage(suggestion_id.id_within_category());
-
-  content->dismissed.push_back(std::move(*it));
-  content->snippets.erase(it);
+  DismissSuggestionFromCategoryContent(content,
+                                       suggestion_id.id_within_category());
 }
 
 void NTPSnippetsService::FetchSuggestionImage(
@@ -501,23 +488,8 @@
     const DismissedSuggestionsCallback& callback) {
   auto content_it = category_contents_.find(category);
   DCHECK(content_it != category_contents_.end());
-  const CategoryContent& content = content_it->second;
-  std::vector<ContentSuggestion> result;
-  for (const std::unique_ptr<NTPSnippet>& snippet : content.dismissed) {
-    if (!snippet->is_complete())
-      continue;
-    ContentSuggestion suggestion(category, snippet->id(),
-                                 snippet->best_source().url);
-    suggestion.set_amp_url(snippet->best_source().amp_url);
-    suggestion.set_title(base::UTF8ToUTF16(snippet->title()));
-    suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet()));
-    suggestion.set_publish_date(snippet->publish_date());
-    suggestion.set_publisher_name(
-        base::UTF8ToUTF16(snippet->best_source().publisher_name));
-    suggestion.set_score(snippet->score());
-    result.emplace_back(std::move(suggestion));
-  }
-  callback.Run(std::move(result));
+  callback.Run(
+      ConvertToContentSuggestions(category, content_it->second.dismissed));
 }
 
 void NTPSnippetsService::ClearDismissedSuggestionsForDebugging(
@@ -627,7 +599,7 @@
               });
   }
 
-  // TODO(tschumann): If I move ClearExpiredDismisedSnippets() to the beginning
+  // TODO(tschumann): If I move ClearExpiredDismissedSnippets() to the beginning
   // of the function, it essentially does nothing but tests are still green. Fix
   // this!
   ClearExpiredDismissedSnippets();
@@ -666,11 +638,40 @@
       UpdateCategoryInfo(category, fetched_category.info);
   SanitizeReceivedSnippets(existing_content->dismissed,
                            &fetched_category.snippets);
+  // We compute the result now before modifying |fetched_category.snippets|.
+  // However, we wait with notifying the caller until the end of the method when
+  // all state is updated.
   std::vector<ContentSuggestion> result =
       ConvertToContentSuggestions(category, fetched_category.snippets);
-  // Add the snippets to the archive so that we keep track of the image urls.
-  ArchiveSnippets(existing_content, &fetched_category.snippets);
+
+  // Fill up the newly fetched snippets with existing ones, store them, and
+  // notify observers about new data.
+  while (fetched_category.snippets.size() <
+             static_cast<size_t>(kMaxSnippetCount) &&
+         !existing_content->snippets.empty()) {
+    fetched_category.snippets.emplace(
+        fetched_category.snippets.begin(),
+        std::move(existing_content->snippets.back()));
+    existing_content->snippets.pop_back();
+  }
+  std::vector<std::string> to_dismiss =
+      *GetSnippetIDVector(existing_content->snippets);
+  for (const auto& id : to_dismiss) {
+    DismissSuggestionFromCategoryContent(existing_content, id);
+  }
+  DCHECK(existing_content->snippets.empty());
+
+  IntegrateSnippets(existing_content, std::move(fetched_category.snippets));
+
+  // TODO(tschumann): We should properly honor the existing category state,
+  // e.g. to make sure we don't serve results after the sign-out. Revisit this
+  // once the snippets fetcher supports concurrent requests. We can then see if
+  // Nuke should also cancel outstanding requests or we want to check the
+  // status.
+  UpdateCategoryStatus(category, CategoryStatus::AVAILABLE);
+  // Notify callers and observers.
   fetching_callback.Run(Status(StatusCode::SUCCESS), std::move(result));
+  NotifyNewSuggestions(category, *existing_content);
 }
 
 void NTPSnippetsService::OnFetchFinished(
@@ -782,6 +783,9 @@
   DCHECK(ready());
 
   // Do not touch the current set of snippets if the newly fetched one is empty.
+  // TODO(tschumann): This should go. If we get empty results we should update
+  // accordingly and remove the old one (only of course if this was not received
+  // through a fetch-more).
   if (new_snippets.empty())
     return;
 
@@ -793,6 +797,7 @@
   EraseByPrimaryID(&content->snippets, *GetSnippetIDVector(new_snippets));
   // Do not delete the thumbnail images as they are still handy on open NTPs.
   database_->DeleteSnippets(GetSnippetIDVector(content->snippets));
+  // Note, that ArchiveSnippets will clear |content->snippets|.
   ArchiveSnippets(content, &content->snippets);
 
   database_->SaveSnippets(new_snippets);
@@ -800,6 +805,28 @@
   content->snippets = std::move(new_snippets);
 }
 
+void NTPSnippetsService::DismissSuggestionFromCategoryContent(
+    CategoryContent* content,
+    const std::string& id_within_category) {
+  auto it = std::find_if(
+      content->snippets.begin(), content->snippets.end(),
+      [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) {
+        return snippet->id() == id_within_category;
+      });
+  if (it == content->snippets.end())
+    return;
+
+  (*it)->set_dismissed(true);
+
+  database_->SaveSnippet(**it);
+  // TODO(tschumann): We should not delete the image yet. Other NTPs might still
+  // reference them.
+  database_->DeleteImage(id_within_category);
+
+  content->dismissed.push_back(std::move(*it));
+  content->snippets.erase(it);
+}
+
 void NTPSnippetsService::ClearExpiredDismissedSnippets() {
   std::vector<Category> categories_to_erase;
 
@@ -820,6 +847,8 @@
     // Delete the removed article suggestions from the DB.
     database_->DeleteSnippets(GetSnippetIDVector(to_delete));
     // The image got already deleted when the suggestion was dismissed.
+    // TODO(tschumann): Delete the image here instead of at the time of
+    // dismissal.
 
     if (content->snippets.empty() && content->dismissed.empty() &&
         category != articles_category_ &&
diff --git a/components/ntp_snippets/remote/ntp_snippets_service.h b/components/ntp_snippets/remote/ntp_snippets_service.h
index 3a0cb47..766f32f 100644
--- a/components/ntp_snippets/remote/ntp_snippets_service.h
+++ b/components/ntp_snippets/remote/ntp_snippets_service.h
@@ -267,6 +267,13 @@
   void IntegrateSnippets(CategoryContent* content,
                          NTPSnippet::PtrVector new_snippets);
 
+  // Dismisses a snippet within a given category content.
+  // Note that this modifies the snippet datastructures of |content|
+  // invalidating iterators.
+  void DismissSuggestionFromCategoryContent(
+      CategoryContent* content,
+      const std::string& id_within_category);
+
   // Removes expired dismissed snippets from the service and the database.
   void ClearExpiredDismissedSnippets();
 
diff --git a/components/ntp_snippets/remote/ntp_snippets_service_unittest.cc b/components/ntp_snippets/remote/ntp_snippets_service_unittest.cc
index b116a798..b04f06e 100644
--- a/components/ntp_snippets/remote/ntp_snippets_service_unittest.cc
+++ b/components/ntp_snippets/remote/ntp_snippets_service_unittest.cc
@@ -909,6 +909,7 @@
                          /*known_ids=*/std::set<std::string>(),
                          expect_only_second_suggestion_received);
 
+  // Verify we can resolve the image of the new snippets.
   ServeImageCallback cb = base::Bind(&ServeOneByOneImage, service.get());
   EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _))
       .Times(2)
@@ -922,13 +923,145 @@
   EXPECT_FALSE(image.IsEmpty());
   EXPECT_EQ(1, image.Width());
 
-  // Verify that the observer did not get updated about the additional snippets.
-  // Rationale: It's not clear that snippets fetched in one context (i.e. one
-  // potentially old NTP) should be shown in other NTPs as well.
-  // We can revisit this decision, but for now it's easiest to keep them
-  // independent.
+  // Verify that the observer received the update as well. We should see the
+  // newly-fetched items filled up with existing ones.
   EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
-              ElementsAre(IdWithinCategoryEq("http://first")));
+              ElementsAre(IdWithinCategoryEq("http://first"),
+                          IdWithinCategoryEq("http://second")));
+}
+
+// The tests TestMergingFetchedMoreSnippetsFillup and
+// TestMergingFetchedMoreSnippetsReplaceAll simulate the following user story:
+// 1) fetch suggestions in NTP A
+// 2) fetch more suggestions in NTP A.
+// 3) open new NTP B: See the last 10 results visible in step 2).
+// 4) fetch more suggestions in NTP B. Make sure no results from step 1) which
+//    were superseded in step 2) get merged back in again.
+// TODO(tschumann): Test step 4) on a higher level instead of peeking into the
+// internal 'dismissed' data. The proper check is to make sure we tell the
+// backend to exclude these snippets.
+TEST_F(NTPSnippetsServiceTest, TestMergingFetchedMoreSnippetsFillup) {
+  auto service = MakeSnippetsService(/*set_empty_response=*/false);
+  LoadFromJSONString(
+      service.get(),
+      GetTestJson(
+          {GetSnippetWithUrl("http://id-1"), GetSnippetWithUrl("http://id-2"),
+           GetSnippetWithUrl("http://id-3"), GetSnippetWithUrl("http://id-4"),
+           GetSnippetWithUrl("http://id-5"), GetSnippetWithUrl("http://id-6"),
+           GetSnippetWithUrl("http://id-7"), GetSnippetWithUrl("http://id-8"),
+           GetSnippetWithUrl("http://id-9"),
+           GetSnippetWithUrl("http://id-10")}));
+  EXPECT_THAT(
+      observer().SuggestionsForCategory(articles_category()),
+      ElementsAre(
+          IdWithinCategoryEq("http://id-1"), IdWithinCategoryEq("http://id-2"),
+          IdWithinCategoryEq("http://id-3"), IdWithinCategoryEq("http://id-4"),
+          IdWithinCategoryEq("http://id-5"), IdWithinCategoryEq("http://id-6"),
+          IdWithinCategoryEq("http://id-7"), IdWithinCategoryEq("http://id-8"),
+          IdWithinCategoryEq("http://id-9"),
+          IdWithinCategoryEq("http://id-10")));
+
+  auto expect_receiving_two_new_snippets =
+      base::Bind([](Status status, std::vector<ContentSuggestion> suggestions) {
+        ASSERT_THAT(suggestions, SizeIs(2));
+        EXPECT_THAT(suggestions[0], IdWithinCategoryEq("http://more-id-1"));
+        EXPECT_THAT(suggestions[1], IdWithinCategoryEq("http://more-id-2"));
+      });
+  LoadMoreFromJSONString(
+      service.get(), articles_category(),
+      GetTestJson({GetSnippetWithUrl("http://more-id-1"),
+                   GetSnippetWithUrl("http://more-id-2")}),
+      /*known_ids=*/{"http://id-1", "http://id-2", "http://id-3", "http://id-4",
+                     "http://id-5", "http://id-6", "http://id-7", "http://id-8",
+                     "http://id-9", "http://id-10"},
+      expect_receiving_two_new_snippets);
+
+  // Verify that the observer received the update as well. We should see the
+  // newly-fetched items filled up with existing ones. The merging is done
+  // mimicking a scrolling behavior.
+  EXPECT_THAT(
+      observer().SuggestionsForCategory(articles_category()),
+      ElementsAre(
+          IdWithinCategoryEq("http://id-3"), IdWithinCategoryEq("http://id-4"),
+          IdWithinCategoryEq("http://id-5"), IdWithinCategoryEq("http://id-6"),
+          IdWithinCategoryEq("http://id-7"), IdWithinCategoryEq("http://id-8"),
+          IdWithinCategoryEq("http://id-9"), IdWithinCategoryEq("http://id-10"),
+          IdWithinCategoryEq("http://more-id-1"),
+          IdWithinCategoryEq("http://more-id-2")));
+  // Verify the superseded suggestions got marked as dismissed.
+  EXPECT_THAT(service->GetDismissedSnippetsForTesting(articles_category()),
+              ElementsAre(IdEq("http://id-1"), IdEq("http://id-2")));
+}
+
+TEST_F(NTPSnippetsServiceTest, TestMergingFetchedMoreSnippetsReplaceAll) {
+  auto service = MakeSnippetsService(/*set_empty_response=*/false);
+  LoadFromJSONString(
+      service.get(),
+      GetTestJson(
+          {GetSnippetWithUrl("http://id-1"), GetSnippetWithUrl("http://id-2"),
+           GetSnippetWithUrl("http://id-3"), GetSnippetWithUrl("http://id-4"),
+           GetSnippetWithUrl("http://id-5"), GetSnippetWithUrl("http://id-6"),
+           GetSnippetWithUrl("http://id-7"), GetSnippetWithUrl("http://id-8"),
+           GetSnippetWithUrl("http://id-9"),
+           GetSnippetWithUrl("http://id-10")}));
+  EXPECT_THAT(
+      observer().SuggestionsForCategory(articles_category()),
+      ElementsAre(
+          IdWithinCategoryEq("http://id-1"), IdWithinCategoryEq("http://id-2"),
+          IdWithinCategoryEq("http://id-3"), IdWithinCategoryEq("http://id-4"),
+          IdWithinCategoryEq("http://id-5"), IdWithinCategoryEq("http://id-6"),
+          IdWithinCategoryEq("http://id-7"), IdWithinCategoryEq("http://id-8"),
+          IdWithinCategoryEq("http://id-9"),
+          IdWithinCategoryEq("http://id-10")));
+
+  auto expect_receiving_ten_new_snippets =
+      base::Bind([](Status status, std::vector<ContentSuggestion> suggestions) {
+        EXPECT_THAT(suggestions, ElementsAre(
+            IdWithinCategoryEq("http://more-id-1"),
+            IdWithinCategoryEq("http://more-id-2"),
+            IdWithinCategoryEq("http://more-id-3"),
+            IdWithinCategoryEq("http://more-id-4"),
+            IdWithinCategoryEq("http://more-id-5"),
+            IdWithinCategoryEq("http://more-id-6"),
+            IdWithinCategoryEq("http://more-id-7"),
+            IdWithinCategoryEq("http://more-id-8"),
+            IdWithinCategoryEq("http://more-id-9"),
+            IdWithinCategoryEq("http://more-id-10")));
+      });
+  LoadMoreFromJSONString(
+      service.get(), articles_category(),
+      GetTestJson({GetSnippetWithUrl("http://more-id-1"),
+                   GetSnippetWithUrl("http://more-id-2"),
+                   GetSnippetWithUrl("http://more-id-3"),
+                   GetSnippetWithUrl("http://more-id-4"),
+                   GetSnippetWithUrl("http://more-id-5"),
+                   GetSnippetWithUrl("http://more-id-6"),
+                   GetSnippetWithUrl("http://more-id-7"),
+                   GetSnippetWithUrl("http://more-id-8"),
+                   GetSnippetWithUrl("http://more-id-9"),
+                   GetSnippetWithUrl("http://more-id-10")}),
+      /*known_ids=*/{"http://id-1", "http://id-2", "http://id-3", "http://id-4",
+                     "http://id-5", "http://id-6", "http://id-7", "http://id-8",
+                     "http://id-9", "http://id-10"},
+      expect_receiving_ten_new_snippets);
+  EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
+              ElementsAre(IdWithinCategoryEq("http://more-id-1"),
+                          IdWithinCategoryEq("http://more-id-2"),
+                          IdWithinCategoryEq("http://more-id-3"),
+                          IdWithinCategoryEq("http://more-id-4"),
+                          IdWithinCategoryEq("http://more-id-5"),
+                          IdWithinCategoryEq("http://more-id-6"),
+                          IdWithinCategoryEq("http://more-id-7"),
+                          IdWithinCategoryEq("http://more-id-8"),
+                          IdWithinCategoryEq("http://more-id-9"),
+                          IdWithinCategoryEq("http://more-id-10")));
+  // Verify the superseded suggestions got marked as dismissed.
+  EXPECT_THAT(
+      service->GetDismissedSnippetsForTesting(articles_category()),
+      ElementsAre(IdEq("http://id-1"), IdEq("http://id-2"), IdEq("http://id-3"),
+                  IdEq("http://id-4"), IdEq("http://id-5"), IdEq("http://id-6"),
+                  IdEq("http://id-7"), IdEq("http://id-8"), IdEq("http://id-9"),
+                  IdEq("http://id-10")));
 }
 
 // TODO(tschumann): We don't have test making sure the NTPSnippetsFetcher
@@ -948,22 +1081,6 @@
 
 }  // namespace
 
-TEST_F(NTPSnippetsServiceTest, InvokesOnlyCallbackOnFetchingMore) {
-  auto service = MakeSnippetsService();
-
-  MockFunction<void(Status, const std::vector<ContentSuggestion>&)> loaded;
-  EXPECT_CALL(loaded, Call(HasCode(StatusCode::SUCCESS), SizeIs(1)));
-
-  LoadMoreFromJSONString(service.get(), articles_category(),
-                         GetTestJson({GetSnippetWithUrl("http://some")}),
-                         std::set<std::string>(),
-                         base::Bind(&SuggestionsLoaded, &loaded));
-
-  // The observer shouldn't have been triggered.
-  EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
-              IsEmpty());
-}
-
 TEST_F(NTPSnippetsServiceTest, ReturnFetchRequestEmptyBeforeInit) {
   auto service = MakeSnippetsServiceWithoutInitialization();
   MockFunction<void(Status, const std::vector<ContentSuggestion>&)> loaded;
diff --git a/components/offline_pages/BUILD.gn b/components/offline_pages/BUILD.gn
index 0b12ffb..123c1e4 100644
--- a/components/offline_pages/BUILD.gn
+++ b/components/offline_pages/BUILD.gn
@@ -112,7 +112,7 @@
   java_cpp_enum("offline_page_model_enums_java") {
     sources = [
       "background/request_notifier.h",
-      "background/request_queue.h",
+      "background/request_queue_results.h",
       "background/save_page_request.h",
       "offline_page_types.h",
       "offline_store_types.h",
diff --git a/components/offline_pages/background/BUILD.gn b/components/offline_pages/background/BUILD.gn
index 2765469..3f428f1 100644
--- a/components/offline_pages/background/BUILD.gn
+++ b/components/offline_pages/background/BUILD.gn
@@ -20,6 +20,10 @@
     "offliner.h",
     "offliner_factory.h",
     "offliner_policy.h",
+    "pick_request_task.cc",
+    "pick_request_task.h",
+    "pick_request_task_factory.cc",
+    "pick_request_task_factory.h",
     "remove_requests_task.cc",
     "remove_requests_task.h",
     "request_coordinator.cc",
@@ -27,12 +31,11 @@
     "request_coordinator_event_logger.cc",
     "request_coordinator_event_logger.h",
     "request_notifier.h",
-    "request_picker.cc",
-    "request_picker.h",
     "request_queue.cc",
     "request_queue.h",
     "request_queue_in_memory_store.cc",
     "request_queue_in_memory_store.h",
+    "request_queue_results.h",
     "request_queue_store.h",
     "request_queue_store_sql.cc",
     "request_queue_store_sql.h",
@@ -62,10 +65,10 @@
     "mark_attempt_aborted_task_unittest.cc",
     "mark_attempt_completed_task_unittest.cc",
     "mark_attempt_started_task_unittest.cc",
+    "pick_request_task_unittest.cc",
     "remove_requests_task_unittest.cc",
     "request_coordinator_event_logger_unittest.cc",
     "request_coordinator_unittest.cc",
-    "request_picker_unittest.cc",
     "request_queue_store_unittest.cc",
     "request_queue_unittest.cc",
     "save_page_request_unittest.cc",
diff --git a/components/offline_pages/background/mark_attempt_completed_task.h b/components/offline_pages/background/mark_attempt_completed_task.h
index 7997ee5..7a1d3b3b2 100644
--- a/components/offline_pages/background/mark_attempt_completed_task.h
+++ b/components/offline_pages/background/mark_attempt_completed_task.h
@@ -8,12 +8,14 @@
 #include <stdint.h>
 #include <memory>
 
-#include "components/offline_pages/background/request_queue_store.h"
+#include "components/offline_pages/background/request_queue_results.h"
 #include "components/offline_pages/background/update_request_task.h"
 #include "components/offline_pages/core/task.h"
 
 namespace offline_pages {
 
+class RequestQueueStore;
+
 class MarkAttemptCompletedTask : public UpdateRequestTask {
  public:
   MarkAttemptCompletedTask(RequestQueueStore* store,
diff --git a/components/offline_pages/background/pick_request_task.cc b/components/offline_pages/background/pick_request_task.cc
new file mode 100644
index 0000000..3de14dd4d5
--- /dev/null
+++ b/components/offline_pages/background/pick_request_task.cc
@@ -0,0 +1,302 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/background/pick_request_task.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "components/offline_pages/background/device_conditions.h"
+#include "components/offline_pages/background/offliner_policy.h"
+#include "components/offline_pages/background/request_coordinator_event_logger.h"
+#include "components/offline_pages/background/request_notifier.h"
+#include "components/offline_pages/background/request_queue_store.h"
+#include "components/offline_pages/background/save_page_request.h"
+
+namespace {
+template <typename T>
+int signum(T t) {
+  return (T(0) < t) - (t < T(0));
+}
+
+#define CALL_MEMBER_FUNCTION(object, ptrToMember) ((object)->*(ptrToMember))
+}  // namespace
+
+namespace offline_pages {
+
+PickRequestTask::PickRequestTask(RequestQueueStore* store,
+                                 OfflinerPolicy* policy,
+                                 RequestNotifier* notifier,
+                                 RequestCoordinatorEventLogger* event_logger,
+                                 RequestPickedCallback picked_callback,
+                                 RequestNotPickedCallback not_picked_callback,
+                                 DeviceConditions& device_conditions,
+                                 const std::set<int64_t>& disabled_requests)
+    : store_(store),
+      policy_(policy),
+      notifier_(notifier),
+      event_logger_(event_logger),
+      picked_callback_(picked_callback),
+      not_picked_callback_(not_picked_callback),
+      disabled_requests_(disabled_requests),
+      weak_ptr_factory_(this) {
+  device_conditions_.reset(new DeviceConditions(device_conditions));
+}
+
+PickRequestTask::~PickRequestTask() {}
+
+void PickRequestTask::Run() {
+  // Get all the requests from the queue, we will classify them in the callback.
+  store_->GetRequests(base::Bind(&PickRequestTask::ChooseAndPrune,
+                                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void PickRequestTask::ChooseAndPrune(
+    bool success,
+    std::vector<std::unique_ptr<SavePageRequest>> requests) {
+  // If there is nothing to do, return right away.
+  if (requests.size() == 0) {
+    not_picked_callback_.Run(false);
+    TaskComplete();
+    return;
+  }
+
+  // Get the expired requests to be removed from the queue, and the valid ones
+  // from which to pick the next request.
+  std::vector<std::unique_ptr<SavePageRequest>> valid_requests;
+  std::vector<int64_t> expired_request_ids;
+  SplitRequests(std::move(requests), &valid_requests, &expired_request_ids);
+
+  // Continue processing by choosing a request.
+  ChooseRequestAndCallback(std::move(valid_requests));
+
+  // Continue processing by handling expired requests, if any.
+  if (expired_request_ids.size() == 0) {
+    TaskComplete();
+    return;
+  }
+
+  RemoveStaleRequests(std::move(expired_request_ids));
+}
+
+void PickRequestTask::ChooseRequestAndCallback(
+    std::vector<std::unique_ptr<SavePageRequest>> valid_requests) {
+  // Pick the most deserving request for our conditions.
+  const SavePageRequest* picked_request = nullptr;
+
+  RequestCompareFunction comparator = nullptr;
+
+  // Choose which comparison function to use based on policy.
+  if (policy_->RetryCountIsMoreImportantThanRecency())
+    comparator = &PickRequestTask::RetryCountFirstCompareFunction;
+  else
+    comparator = &PickRequestTask::RecencyFirstCompareFunction;
+
+  // TODO(petewil): Consider replacing this bool with a better named enum.
+  bool non_user_requested_tasks_remaining = false;
+
+  // Iterate once through the requests, keeping track of best candidate.
+  for (unsigned i = 0; i < valid_requests.size(); ++i) {
+    // If the  request is on the disabled list, skip it.
+    auto search = disabled_requests_.find(valid_requests[i]->request_id());
+    if (search != disabled_requests_.end())
+      continue;
+    // If there are non-user-requested tasks remaining, we need to make sure
+    // that they are scheduled after we run out of user requested tasks. Here we
+    // detect if any exist. If we don't find any user-requested tasks, we will
+    // inform the "not_picked_callback_" that it needs to schedule a task for
+    // non-user-requested items, which have different network and power needs.
+    if (!valid_requests[i]->user_requested())
+      non_user_requested_tasks_remaining = true;
+    if (!RequestConditionsSatisfied(valid_requests[i].get()))
+      continue;
+    if (IsNewRequestBetter(picked_request, valid_requests[i].get(), comparator))
+      picked_request = valid_requests[i].get();
+  }
+
+  // If we have a best request to try next, get the request coodinator to
+  // start it.  Otherwise return that we have no candidates.
+  if (picked_request != nullptr)
+    picked_callback_.Run(*picked_request);
+  else
+    not_picked_callback_.Run(non_user_requested_tasks_remaining);
+}
+
+// Continue the async part of the processing by deleting the expired requests.
+// TODO(petewil): Does that really need to be done on the task queue? Hard to
+// see how we need to wait for it before starting the next task. OTOH, we'd hate
+// to do a second slow DB operation to get entries a second time, and waiting
+// until this is done will make sure other gets don't see these old entries.
+// Consider moving this to a fresh task type to clean the queue.
+void PickRequestTask::RemoveStaleRequests(
+    std::vector<int64_t> stale_request_ids) {
+  store_->RemoveRequests(stale_request_ids,
+                         base::Bind(&PickRequestTask::OnRequestsExpired,
+                                    weak_ptr_factory_.GetWeakPtr()));
+}
+
+void PickRequestTask::OnRequestsExpired(
+    std::unique_ptr<UpdateRequestsResult> result) {
+  RequestNotifier::BackgroundSavePageResult save_page_result(
+      RequestNotifier::BackgroundSavePageResult::EXPIRED);
+  for (const auto& request : result->updated_items) {
+    event_logger_->RecordDroppedSavePageRequest(
+        request.client_id().name_space, save_page_result, request.request_id());
+    notifier_->NotifyCompleted(request, save_page_result);
+  }
+
+  // The task is now done, return control to the task queue.
+  TaskComplete();
+}
+
+void PickRequestTask::SplitRequests(
+    std::vector<std::unique_ptr<SavePageRequest>> requests,
+    std::vector<std::unique_ptr<SavePageRequest>>* valid_requests,
+    std::vector<int64_t>* expired_request_ids) {
+  for (auto& request : requests) {
+    if (base::Time::Now() - request->creation_time() >=
+        base::TimeDelta::FromSeconds(kRequestExpirationTimeInSeconds)) {
+      expired_request_ids->push_back(request->request_id());
+    } else {
+      valid_requests->push_back(std::move(request));
+    }
+  }
+}
+
+// Filter out requests that don't meet the current conditions.  For instance, if
+// this is a predictive request, and we are not on WiFi, it should be ignored
+// this round.
+bool PickRequestTask::RequestConditionsSatisfied(
+    const SavePageRequest* request) {
+  // If the user did not request the page directly, make sure we are connected
+  // to power and have WiFi and sufficient battery remaining before we take this
+  // request.
+  if (!device_conditions_->IsPowerConnected() &&
+      policy_->PowerRequired(request->user_requested())) {
+    return false;
+  }
+
+  if (device_conditions_->GetNetConnectionType() !=
+          net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI &&
+      policy_->UnmeteredNetworkRequired(request->user_requested())) {
+    return false;
+  }
+
+  if (device_conditions_->GetBatteryPercentage() <
+      policy_->BatteryPercentageRequired(request->user_requested())) {
+    return false;
+  }
+
+  // If we have already started this page the max number of times, it is not
+  // eligible to try again.
+  if (request->started_attempt_count() >= policy_->GetMaxStartedTries())
+    return false;
+
+  // If we have already completed trying this page the max number of times, it
+  // is not eligible to try again.
+  if (request->completed_attempt_count() >= policy_->GetMaxCompletedTries())
+    return false;
+
+  // If the request is paused, do not consider it.
+  if (request->request_state() == SavePageRequest::RequestState::PAUSED)
+    return false;
+
+  // If the request is expired, do not consider it.
+  base::TimeDelta requestAge = base::Time::Now() - request->creation_time();
+  if (requestAge > base::TimeDelta::FromSeconds(
+                       policy_->GetRequestExpirationTimeInSeconds()))
+    return false;
+
+  // If this request is not active yet, return false.
+  // TODO(petewil): If the only reason we return nothing to do is that we have
+  // inactive requests, we still want to try again later after their activation
+  // time elapses, we shouldn't take ourselves completely off the scheduler.
+  if (request->activation_time() > base::Time::Now())
+    return false;
+
+  return true;
+}
+
+// Look at policies to decide which requests to prefer.
+bool PickRequestTask::IsNewRequestBetter(const SavePageRequest* oldRequest,
+                                         const SavePageRequest* newRequest,
+                                         RequestCompareFunction comparator) {
+  // If there is no old request, the new one is better.
+  if (oldRequest == nullptr)
+    return true;
+
+  // User requested pages get priority.
+  if (newRequest->user_requested() && !oldRequest->user_requested())
+    return true;
+
+  // Otherwise, use the comparison function for the current policy, which
+  // returns true if the older request is better.
+  return !(CALL_MEMBER_FUNCTION(this, comparator)(oldRequest, newRequest));
+}
+
+// Compare the results, checking request count before recency.  Returns true if
+// left hand side is better, false otherwise.
+bool PickRequestTask::RetryCountFirstCompareFunction(
+    const SavePageRequest* left,
+    const SavePageRequest* right) {
+  // Check the attempt count.
+  int result = CompareRetryCount(left, right);
+
+  if (result != 0)
+    return (result > 0);
+
+  // If we get here, the attempt counts were the same, so check recency.
+  result = CompareCreationTime(left, right);
+
+  return (result > 0);
+}
+
+// Compare the results, checking recency before request count. Returns true if
+// left hand side is better, false otherwise.
+bool PickRequestTask::RecencyFirstCompareFunction(
+    const SavePageRequest* left,
+    const SavePageRequest* right) {
+  // Check the recency.
+  int result = CompareCreationTime(left, right);
+
+  if (result != 0)
+    return (result > 0);
+
+  // If we get here, the recency was the same, so check the attempt count.
+  result = CompareRetryCount(left, right);
+
+  return (result > 0);
+}
+
+// Compare left and right side, returning 1 if the left side is better
+// (preferred by policy), 0 if the same, and -1 if the right side is better.
+int PickRequestTask::CompareRetryCount(const SavePageRequest* left,
+                                       const SavePageRequest* right) {
+  // Check the attempt count.
+  int result = signum(left->completed_attempt_count() -
+                      right->completed_attempt_count());
+
+  // Flip the direction of comparison if policy prefers fewer retries.
+  if (policy_->ShouldPreferUntriedRequests())
+    result *= -1;
+
+  return result;
+}
+
+// Compare left and right side, returning 1 if the left side is better
+// (preferred by policy), 0 if the same, and -1 if the right side is better.
+int PickRequestTask::CompareCreationTime(const SavePageRequest* left,
+                                         const SavePageRequest* right) {
+  // Check the recency.
+  base::TimeDelta difference = left->creation_time() - right->creation_time();
+  int result = signum(difference.InMilliseconds());
+
+  // Flip the direction of comparison if policy prefers fewer retries.
+  if (policy_->ShouldPreferEarlierRequests())
+    result *= -1;
+
+  return result;
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/background/pick_request_task.h b/components/offline_pages/background/pick_request_task.h
new file mode 100644
index 0000000..64d85d40
--- /dev/null
+++ b/components/offline_pages/background/pick_request_task.h
@@ -0,0 +1,115 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_PICK_REQUEST_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_PICK_REQUEST_TASK_H_
+
+#include <set>
+
+#include "base/memory/weak_ptr.h"
+#include "components/offline_pages/background/request_queue_results.h"
+#include "components/offline_pages/background/save_page_request.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+
+class DeviceConditions;
+class OfflinerPolicy;
+class PickRequestTask;
+class RequestCoordinatorEventLogger;
+class RequestNotifier;
+class RequestQueueStore;
+
+typedef bool (PickRequestTask::*RequestCompareFunction)(
+    const SavePageRequest* left,
+    const SavePageRequest* right);
+
+class PickRequestTask : public Task {
+ public:
+  // Callback to report when a request was available.
+  typedef base::Callback<void(const SavePageRequest& request)>
+      RequestPickedCallback;
+
+  // Callback to report when no request was available.
+  typedef base::Callback<void(bool)> RequestNotPickedCallback;
+
+  PickRequestTask(RequestQueueStore* store,
+                  OfflinerPolicy* policy,
+                  RequestNotifier* notifier,
+                  RequestCoordinatorEventLogger* event_logger,
+                  RequestPickedCallback picked_callback,
+                  RequestNotPickedCallback not_picked_callback,
+                  DeviceConditions& device_conditions,
+                  const std::set<int64_t>& disabled_requests);
+
+  ~PickRequestTask() override;
+
+  // TaskQueue::Task implementation, starts the async chain
+  void Run() override;
+
+ private:
+  // Step 1, handle getting the results from the store.
+  void ChooseAndPrune(bool request,
+                      std::vector<std::unique_ptr<SavePageRequest>> requests);
+
+  // Step 2a. Handle choosing an entry, and calling the right callback.
+  void ChooseRequestAndCallback(
+      std::vector<std::unique_ptr<SavePageRequest>> valid_requests);
+
+  // Step 2b. Handle deleting stale entries and notifying observers.
+  void RemoveStaleRequests(std::vector<int64_t> stale_request_ids);
+
+  // Step 3. Send delete notifications for the expired requests.
+  void OnRequestsExpired(std::unique_ptr<UpdateRequestsResult> result);
+
+  // Helper functions.
+
+  // Split requests into valid and expired categories.
+  void SplitRequests(
+      std::vector<std::unique_ptr<SavePageRequest>> requests,
+      std::vector<std::unique_ptr<SavePageRequest>>* valid_requests,
+      std::vector<int64_t>* expired_request_ids);
+
+  // Determine if this request has device conditions appropriate for running it.
+  bool RequestConditionsSatisfied(const SavePageRequest* request);
+
+  // Determine if the new request is preferred under current policies.
+  bool IsNewRequestBetter(const SavePageRequest* oldRequest,
+                          const SavePageRequest* newRequest,
+                          RequestCompareFunction comparator);
+
+  // Returns true if the left hand side is better.
+  bool RetryCountFirstCompareFunction(const SavePageRequest* left,
+                                      const SavePageRequest* right);
+
+  // Returns true if the left hand side is better.
+  bool RecencyFirstCompareFunction(const SavePageRequest* left,
+                                   const SavePageRequest* right);
+
+  // Compare left and right side, returning 1 if the left side is better
+  // (preferred by policy), 0 if the same, and -1 if the right side is better.
+  int CompareRetryCount(const SavePageRequest* left,
+                        const SavePageRequest* right);
+
+  // Compare left and right side, returning 1 if the left side is better
+  // (preferred by policy), 0 if the same, and -1 if the right side is better.
+  int CompareCreationTime(const SavePageRequest* left,
+                          const SavePageRequest* right);
+
+  // Member variables, all pointers are not owned here.
+  RequestQueueStore* store_;
+  OfflinerPolicy* policy_;
+  RequestNotifier* notifier_;
+  RequestCoordinatorEventLogger* event_logger_;
+  RequestPickedCallback picked_callback_;
+  RequestNotPickedCallback not_picked_callback_;
+  std::unique_ptr<DeviceConditions> device_conditions_;
+  const std::set<int64_t>& disabled_requests_;
+  // Allows us to pass a weak pointer to callbacks.
+  base::WeakPtrFactory<PickRequestTask> weak_ptr_factory_;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_PICK_REQUEST_TASK_H_
diff --git a/components/offline_pages/background/pick_request_task_factory.cc b/components/offline_pages/background/pick_request_task_factory.cc
new file mode 100644
index 0000000..8e3efad2
--- /dev/null
+++ b/components/offline_pages/background/pick_request_task_factory.cc
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/background/pick_request_task_factory.h"
+
+#include "components/offline_pages/background/offliner_policy.h"
+#include "components/offline_pages/background/request_coordinator_event_logger.h"
+#include "components/offline_pages/background/request_notifier.h"
+
+namespace offline_pages {
+
+// Capture the common parameters that we will need to make a pick task,
+// and use them when making tasks.  Create this once each session, and
+// use it to build a picker task when needed.
+PickRequestTaskFactory::PickRequestTaskFactory(
+    OfflinerPolicy* policy,
+    RequestNotifier* notifier,
+    RequestCoordinatorEventLogger* event_logger)
+    : policy_(policy), notifier_(notifier), event_logger_(event_logger) {}
+
+PickRequestTaskFactory::~PickRequestTaskFactory() {}
+
+std::unique_ptr<PickRequestTask> PickRequestTaskFactory::CreatePickerTask(
+    RequestQueueStore* store,
+    const PickRequestTask::RequestPickedCallback& picked_callback,
+    const PickRequestTask::RequestNotPickedCallback& not_picked_callback,
+    DeviceConditions& conditions,
+    std::set<int64_t>& disabled_requests) {
+  std::unique_ptr<PickRequestTask> task(new PickRequestTask(
+      store, policy_, notifier_, event_logger_, picked_callback,
+      not_picked_callback, conditions, disabled_requests));
+  return task;
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/background/pick_request_task_factory.h b/components/offline_pages/background/pick_request_task_factory.h
new file mode 100644
index 0000000..1ee9ae3
--- /dev/null
+++ b/components/offline_pages/background/pick_request_task_factory.h
@@ -0,0 +1,48 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_PICK_REQUEST_TASK_FACTORY_H_
+#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_PICK_REQUEST_TASK_FACTORY_H_
+
+#include <stdint.h>
+
+#include <set>
+
+#include "components/offline_pages/background/pick_request_task.h"
+
+namespace offline_pages {
+
+class DeviceContitions;
+class OfflinerPolicy;
+class RequestCoordinatorEventLogger;
+class RequestNotifier;
+class RequestQueueStore;
+
+class PickRequestTaskFactory {
+ public:
+  PickRequestTaskFactory(OfflinerPolicy* policy,
+                         RequestNotifier* notifier,
+                         RequestCoordinatorEventLogger* event_logger);
+
+  ~PickRequestTaskFactory();
+
+  std::unique_ptr<PickRequestTask> CreatePickerTask(
+      RequestQueueStore* store,
+      const PickRequestTask::RequestPickedCallback& picked_callback,
+      const PickRequestTask::RequestNotPickedCallback& not_picked_callback,
+      DeviceConditions& conditions,
+      std::set<int64_t>& disabled_requests);
+
+ private:
+  // Unowned pointer to the Policy
+  OfflinerPolicy* policy_;
+  // Unowned pointer to the notifier
+  RequestNotifier* notifier_;
+  // Unowned pointer to the EventLogger
+  RequestCoordinatorEventLogger* event_logger_;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_PICK_REQUEST_TASK_FACTORY_H_
diff --git a/components/offline_pages/background/request_picker_unittest.cc b/components/offline_pages/background/pick_request_task_unittest.cc
similarity index 66%
rename from components/offline_pages/background/request_picker_unittest.cc
rename to components/offline_pages/background/pick_request_task_unittest.cc
index 963f9aa..02cb21c4 100644
--- a/components/offline_pages/background/request_picker_unittest.cc
+++ b/components/offline_pages/background/pick_request_task_unittest.cc
@@ -2,20 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/offline_pages/background/request_picker.h"
+#include "components/offline_pages/background/pick_request_task.h"
+
+#include <memory>
+#include <set>
 
 #include "base/bind.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
 #include "components/offline_pages/background/device_conditions.h"
-#include "components/offline_pages/background/offliner_factory.h"
 #include "components/offline_pages/background/offliner_policy.h"
+#include "components/offline_pages/background/request_coordinator.h"
+#include "components/offline_pages/background/request_coordinator_event_logger.h"
 #include "components/offline_pages/background/request_notifier.h"
-#include "components/offline_pages/background/request_queue.h"
 #include "components/offline_pages/background/request_queue_in_memory_store.h"
+#include "components/offline_pages/background/request_queue_store.h"
 #include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/offline_page_item.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace offline_pages {
@@ -48,6 +50,7 @@
                                     true);
 }  // namespace
 
+// Helper class needed by the PickRequestTask
 class RequestNotifierStub : public RequestNotifier {
  public:
   RequestNotifierStub()
@@ -80,111 +83,121 @@
   int32_t total_expired_requests_;
 };
 
-class RequestPickerTest : public testing::Test {
+class PickRequestTaskTest : public testing::Test {
  public:
-  RequestPickerTest();
+  PickRequestTaskTest();
 
-  ~RequestPickerTest() override;
+  ~PickRequestTaskTest() override;
 
   void SetUp() override;
 
   void PumpLoop();
 
-  void AddRequestDone(RequestQueue::AddRequestResult result,
-                      const SavePageRequest& request);
+  void AddRequestDone(ItemActionStatus status);
 
   void RequestPicked(const SavePageRequest& request);
 
   void RequestNotPicked(const bool non_user_requested_tasks_remaining);
 
-  void QueueRequestsAndChooseOne(const SavePageRequest& request1,
-                                 const SavePageRequest& request2);
+  void QueueRequests(const SavePageRequest& request1,
+                     const SavePageRequest& request2);
+
+  // Reset the factory and the task using the current policy.
+  void MakeFactoryAndTask();
 
   RequestNotifierStub* GetNotifier() { return notifier_.get(); }
 
+  PickRequestTask* task() { return task_.get(); }
+
+  void TaskCompletionCallback(Task* completed_task);
+
  protected:
-  // The request queue is simple enough we will use a real queue with a memory
-  // store instead of a stub.
-  std::unique_ptr<RequestQueue> queue_;
-  std::unique_ptr<RequestPicker> picker_;
+  std::unique_ptr<RequestQueueStore> store_;
   std::unique_ptr<RequestNotifierStub> notifier_;
   std::unique_ptr<SavePageRequest> last_picked_;
   std::unique_ptr<OfflinerPolicy> policy_;
   RequestCoordinatorEventLogger event_logger_;
+  std::set<int64_t> disabled_requests_;
+  std::unique_ptr<PickRequestTaskFactory> factory_;
+  std::unique_ptr<PickRequestTask> task_;
   bool request_queue_not_picked_called_;
+  bool task_complete_called_;
 
  private:
   scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
   base::ThreadTaskRunnerHandle task_runner_handle_;
 };
 
-RequestPickerTest::RequestPickerTest()
+PickRequestTaskTest::PickRequestTaskTest()
     : task_runner_(new base::TestSimpleTaskRunner),
       task_runner_handle_(task_runner_) {}
 
-RequestPickerTest::~RequestPickerTest() {}
+PickRequestTaskTest::~PickRequestTaskTest() {}
 
-void RequestPickerTest::SetUp() {
-  std::unique_ptr<RequestQueueInMemoryStore> store(
-      new RequestQueueInMemoryStore());
-  queue_.reset(new RequestQueue(std::move(store)));
+void PickRequestTaskTest::SetUp() {
+  DeviceConditions conditions;
+  store_.reset(new RequestQueueInMemoryStore());
   policy_.reset(new OfflinerPolicy());
   notifier_.reset(new RequestNotifierStub());
-  picker_.reset(new RequestPicker(queue_.get(), policy_.get(), notifier_.get(),
-                                  &event_logger_));
+  MakeFactoryAndTask();
   request_queue_not_picked_called_ = false;
+  task_complete_called_ = false;
+  last_picked_.reset();
 }
 
-void RequestPickerTest::PumpLoop() {
+void PickRequestTaskTest::PumpLoop() {
   task_runner_->RunUntilIdle();
 }
 
-void RequestPickerTest::AddRequestDone(RequestQueue::AddRequestResult result,
-                                       const SavePageRequest& request) {}
+void PickRequestTaskTest::TaskCompletionCallback(Task* completed_task) {
+  task_complete_called_ = true;
+}
 
-void RequestPickerTest::RequestPicked(const SavePageRequest& request) {
+void PickRequestTaskTest::AddRequestDone(ItemActionStatus status) {}
+
+void PickRequestTaskTest::RequestPicked(const SavePageRequest& request) {
   last_picked_.reset(new SavePageRequest(request));
 }
 
-void RequestPickerTest::RequestNotPicked(
+void PickRequestTaskTest::RequestNotPicked(
     const bool non_user_requested_tasks_remaining) {
   request_queue_not_picked_called_ = true;
 }
 
-// Test helper to queue the two given requests and then pick one of them per
-// configured policy.
-void RequestPickerTest::QueueRequestsAndChooseOne(
-    const SavePageRequest& request1, const SavePageRequest& request2) {
+// Test helper to queue the two given requests.
+void PickRequestTaskTest::QueueRequests(const SavePageRequest& request1,
+                                        const SavePageRequest& request2) {
   DeviceConditions conditions;
   std::set<int64_t> disabled_requests;
   // Add test requests on the Queue.
-  queue_->AddRequest(request1, base::Bind(&RequestPickerTest::AddRequestDone,
+  store_->AddRequest(request1, base::Bind(&PickRequestTaskTest::AddRequestDone,
                                           base::Unretained(this)));
-  queue_->AddRequest(request2, base::Bind(&RequestPickerTest::AddRequestDone,
+  store_->AddRequest(request2, base::Bind(&PickRequestTaskTest::AddRequestDone,
                                           base::Unretained(this)));
 
   // Pump the loop to give the async queue the opportunity to do the adds.
   PumpLoop();
-
-  // Call the method under test.
-  picker_->ChooseNextRequest(
-      base::Bind(&RequestPickerTest::RequestPicked, base::Unretained(this)),
-      base::Bind(&RequestPickerTest::RequestNotPicked, base::Unretained(this)),
-      &conditions, disabled_requests);
-
-  // Pump the loop again to give the async queue the opportunity to return
-  // results from the Get operation, and for the picker to call the "picked"
-  // callback.
-  PumpLoop();
 }
 
-TEST_F(RequestPickerTest, PickFromEmptyQueue) {
+void PickRequestTaskTest::MakeFactoryAndTask() {
+  factory_.reset(new PickRequestTaskFactory(policy_.get(), notifier_.get(),
+                                            &event_logger_));
   DeviceConditions conditions;
-  std::set<int64_t> disabled_requests;
-  picker_->ChooseNextRequest(
-      base::Bind(&RequestPickerTest::RequestPicked, base::Unretained(this)),
-      base::Bind(&RequestPickerTest::RequestNotPicked, base::Unretained(this)),
-      &conditions, disabled_requests);
+  task_ = factory_->CreatePickerTask(
+      store_.get(),
+      base::Bind(&PickRequestTaskTest::RequestPicked, base::Unretained(this)),
+      base::Bind(&PickRequestTaskTest::RequestNotPicked,
+                 base::Unretained(this)),
+      conditions, disabled_requests_);
+  task_->SetTaskCompletionCallbackForTesting(
+      task_runner_.get(),
+      base::Bind(&PickRequestTaskTest::TaskCompletionCallback,
+                 base::Unretained(this)));
+}
+
+TEST_F(PickRequestTaskTest, PickFromEmptyQueue) {
+  task()->Run();
+  PumpLoop();
 
   // Pump the loop again to give the async queue the opportunity to return
   // results from the Get operation, and for the picker to call the "QueueEmpty"
@@ -192,29 +205,34 @@
   PumpLoop();
 
   EXPECT_TRUE(request_queue_not_picked_called_);
+  EXPECT_TRUE(task_complete_called_);
 }
 
-TEST_F(RequestPickerTest, ChooseRequestWithHigherRetryCount) {
+TEST_F(PickRequestTaskTest, ChooseRequestWithHigherRetryCount) {
+  // Set up policy to prefer higher retry count.
   policy_.reset(new OfflinerPolicy(
       kPreferUntried, kPreferEarlier, kPreferRetryCount, kMaxStartedTries,
       kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds));
-  picker_.reset(new RequestPicker(queue_.get(), policy_.get(), notifier_.get(),
-                                  &event_logger_));
+  MakeFactoryAndTask();
 
   base::Time creation_time = base::Time::Now();
-  SavePageRequest request1(
-      kRequestId1, kUrl1, kClientId1, creation_time, kUserRequested);
-  SavePageRequest request2(
-      kRequestId2, kUrl2, kClientId2, creation_time, kUserRequested);
+  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
+                           kUserRequested);
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+                           kUserRequested);
   request2.set_completed_attempt_count(kAttemptCount);
 
-  QueueRequestsAndChooseOne(request1, request2);
+  QueueRequests(request1, request2);
+
+  task()->Run();
+  PumpLoop();
 
   EXPECT_EQ(kRequestId2, last_picked_->request_id());
   EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_TRUE(task_complete_called_);
 }
 
-TEST_F(RequestPickerTest, ChooseRequestWithSameRetryCountButEarlier) {
+TEST_F(PickRequestTaskTest, ChooseRequestWithSameRetryCountButEarlier) {
   base::Time creation_time1 =
       base::Time::Now() - base::TimeDelta::FromSeconds(10);
   base::Time creation_time2 = base::Time::Now();
@@ -223,19 +241,22 @@
   SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2,
                            kUserRequested);
 
-  QueueRequestsAndChooseOne(request1, request2);
+  QueueRequests(request1, request2);
+
+  task()->Run();
+  PumpLoop();
 
   EXPECT_EQ(kRequestId1, last_picked_->request_id());
   EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_TRUE(task_complete_called_);
 }
 
-TEST_F(RequestPickerTest, ChooseEarlierRequest) {
+TEST_F(PickRequestTaskTest, ChooseEarlierRequest) {
   // We need a custom policy object prefering recency to retry count.
   policy_.reset(new OfflinerPolicy(
       kPreferUntried, kPreferEarlier, !kPreferRetryCount, kMaxStartedTries,
       kMaxCompletedTries, kBackgroundProcessingTimeBudgetSeconds));
-  picker_.reset(new RequestPicker(queue_.get(), policy_.get(), notifier_.get(),
-                                  &event_logger_));
+  MakeFactoryAndTask();
 
   base::Time creation_time1 =
       base::Time::Now() - base::TimeDelta::FromSeconds(10);
@@ -246,19 +267,22 @@
                            kUserRequested);
   request2.set_completed_attempt_count(kAttemptCount);
 
-  QueueRequestsAndChooseOne(request1, request2);
+  QueueRequests(request1, request2);
+
+  task()->Run();
+  PumpLoop();
 
   EXPECT_EQ(kRequestId1, last_picked_->request_id());
   EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_TRUE(task_complete_called_);
 }
 
-TEST_F(RequestPickerTest, ChooseSameTimeRequestWithHigherRetryCount) {
+TEST_F(PickRequestTaskTest, ChooseSameTimeRequestWithHigherRetryCount) {
   // We need a custom policy object preferring recency to retry count.
   policy_.reset(new OfflinerPolicy(
       kPreferUntried, kPreferEarlier, !kPreferRetryCount, kMaxStartedTries,
       kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds));
-  picker_.reset(new RequestPicker(queue_.get(), policy_.get(), notifier_.get(),
-                                  &event_logger_));
+  MakeFactoryAndTask();
 
   base::Time creation_time = base::Time::Now();
   SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
@@ -267,19 +291,22 @@
                            kUserRequested);
   request2.set_completed_attempt_count(kAttemptCount);
 
-  QueueRequestsAndChooseOne(request1, request2);
+  QueueRequests(request1, request2);
+
+  task()->Run();
+  PumpLoop();
 
   EXPECT_EQ(kRequestId2, last_picked_->request_id());
   EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_TRUE(task_complete_called_);
 }
 
-TEST_F(RequestPickerTest, ChooseRequestWithLowerRetryCount) {
+TEST_F(PickRequestTaskTest, ChooseRequestWithLowerRetryCount) {
   // We need a custom policy object preferring lower retry count.
   policy_.reset(new OfflinerPolicy(
       !kPreferUntried, kPreferEarlier, kPreferRetryCount, kMaxStartedTries,
       kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds));
-  picker_.reset(new RequestPicker(queue_.get(), policy_.get(), notifier_.get(),
-                                  &event_logger_));
+  MakeFactoryAndTask();
 
   base::Time creation_time = base::Time::Now();
   SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
@@ -288,19 +315,22 @@
                            kUserRequested);
   request2.set_completed_attempt_count(kAttemptCount);
 
-  QueueRequestsAndChooseOne(request1, request2);
+  QueueRequests(request1, request2);
+
+  task()->Run();
+  PumpLoop();
 
   EXPECT_EQ(kRequestId1, last_picked_->request_id());
   EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_TRUE(task_complete_called_);
 }
 
-TEST_F(RequestPickerTest, ChooseLaterRequest) {
+TEST_F(PickRequestTaskTest, ChooseLaterRequest) {
   // We need a custom policy preferring recency over retry, and later requests.
   policy_.reset(new OfflinerPolicy(
       kPreferUntried, !kPreferEarlier, !kPreferRetryCount, kMaxStartedTries,
       kMaxCompletedTries, kBackgroundProcessingTimeBudgetSeconds));
-  picker_.reset(new RequestPicker(queue_.get(), policy_.get(), notifier_.get(),
-                                  &event_logger_));
+  MakeFactoryAndTask();
 
   base::Time creation_time1 =
       base::Time::Now() - base::TimeDelta::FromSeconds(10);
@@ -310,13 +340,17 @@
   SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2,
                            kUserRequested);
 
-  QueueRequestsAndChooseOne(request1, request2);
+  QueueRequests(request1, request2);
+
+  task()->Run();
+  PumpLoop();
 
   EXPECT_EQ(kRequestId2, last_picked_->request_id());
   EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_TRUE(task_complete_called_);
 }
 
-TEST_F(RequestPickerTest, ChooseNonExpiredRequest) {
+TEST_F(PickRequestTaskTest, ChooseNonExpiredRequest) {
   base::Time creation_time = base::Time::Now();
   base::Time expired_time =
       creation_time - base::TimeDelta::FromSeconds(
@@ -326,8 +360,9 @@
   SavePageRequest request2(kRequestId2, kUrl2, kClientId2, expired_time,
                            kUserRequested);
 
-  QueueRequestsAndChooseOne(request1, request2);
+  QueueRequests(request1, request2);
 
+  task()->Run();
   PumpLoop();
 
   EXPECT_EQ(kRequestId1, last_picked_->request_id());
@@ -336,9 +371,10 @@
   EXPECT_EQ(RequestNotifier::BackgroundSavePageResult::EXPIRED,
             GetNotifier()->last_request_expiration_status());
   EXPECT_EQ(1, GetNotifier()->total_expired_requests());
+  EXPECT_TRUE(task_complete_called_);
 }
 
-TEST_F(RequestPickerTest, ChooseRequestThatHasNotExceededStartLimit) {
+TEST_F(PickRequestTaskTest, ChooseRequestThatHasNotExceededStartLimit) {
   base::Time creation_time1 =
       base::Time::Now() - base::TimeDelta::FromSeconds(1);
   base::Time creation_time2 = base::Time::Now();
@@ -351,13 +387,17 @@
   // However, we will make the earlier reqeust exceed the limit.
   request1.set_started_attempt_count(policy_->GetMaxStartedTries());
 
-  QueueRequestsAndChooseOne(request1, request2);
+  QueueRequests(request1, request2);
+
+  task()->Run();
+  PumpLoop();
 
   EXPECT_EQ(kRequestId2, last_picked_->request_id());
   EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_TRUE(task_complete_called_);
 }
 
-TEST_F(RequestPickerTest, ChooseRequestThatHasNotExceededCompletionLimit) {
+TEST_F(PickRequestTaskTest, ChooseRequestThatHasNotExceededCompletionLimit) {
   base::Time creation_time1 =
       base::Time::Now() - base::TimeDelta::FromSeconds(1);
   base::Time creation_time2 = base::Time::Now();
@@ -370,47 +410,39 @@
   // However, we will make the earlier reqeust exceed the limit.
   request1.set_completed_attempt_count(policy_->GetMaxCompletedTries());
 
-  QueueRequestsAndChooseOne(request1, request2);
+  QueueRequests(request1, request2);
+
+  task()->Run();
+  PumpLoop();
 
   EXPECT_EQ(kRequestId2, last_picked_->request_id());
   EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_TRUE(task_complete_called_);
 }
 
-
-TEST_F(RequestPickerTest, ChooseRequestThatIsNotDisabled) {
+TEST_F(PickRequestTaskTest, ChooseRequestThatIsNotDisabled) {
   policy_.reset(new OfflinerPolicy(
       kPreferUntried, kPreferEarlier, kPreferRetryCount, kMaxStartedTries,
       kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds));
-  picker_.reset(new RequestPicker(queue_.get(), policy_.get(), notifier_.get(),
-                                  &event_logger_));
-
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request1(
-      kRequestId1, kUrl1, kClientId1, creation_time, kUserRequested);
-  SavePageRequest request2(
-      kRequestId2, kUrl2, kClientId2, creation_time, kUserRequested);
-  request2.set_completed_attempt_count(kAttemptCount);
 
   // put request 2 on disabled list, ensure request1 picked instead,
   // even though policy would prefer 2.
-  std::set<int64_t> disabled_requests {kRequestId2};
-  DeviceConditions conditions;
+  disabled_requests_.insert(kRequestId2);
+  MakeFactoryAndTask();
+
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
+                           kUserRequested);
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+                           kUserRequested);
+  request2.set_completed_attempt_count(kAttemptCount);
 
   // Add test requests on the Queue.
-  queue_->AddRequest(request1, base::Bind(&RequestPickerTest::AddRequestDone,
-                                          base::Unretained(this)));
-  queue_->AddRequest(request2, base::Bind(&RequestPickerTest::AddRequestDone,
-                                          base::Unretained(this)));
+  QueueRequests(request1, request2);
 
-  // Pump the loop to give the async queue the opportunity to do the adds.
+  task()->Run();
   PumpLoop();
 
-  // Call the method under test.
-  picker_->ChooseNextRequest(
-      base::Bind(&RequestPickerTest::RequestPicked, base::Unretained(this)),
-      base::Bind(&RequestPickerTest::RequestNotPicked, base::Unretained(this)),
-      &conditions, disabled_requests);
-
   // Pump the loop again to give the async queue the opportunity to return
   // results from the Get operation, and for the picker to call the "picked"
   // callback.
@@ -418,5 +450,7 @@
 
   EXPECT_EQ(kRequestId1, last_picked_->request_id());
   EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_TRUE(task_complete_called_);
 }
+
 }  // namespace offline_pages
diff --git a/components/offline_pages/background/request_coordinator.cc b/components/offline_pages/background/request_coordinator.cc
index ca016f8a..fdda36d 100644
--- a/components/offline_pages/background/request_coordinator.cc
+++ b/components/offline_pages/background/request_coordinator.cc
@@ -16,7 +16,6 @@
 #include "base/time/time.h"
 #include "components/offline_pages/background/offliner_factory.h"
 #include "components/offline_pages/background/offliner_policy.h"
-#include "components/offline_pages/background/request_picker.h"
 #include "components/offline_pages/background/save_page_request.h"
 #include "components/offline_pages/client_policy_controller.h"
 #include "components/offline_pages/offline_page_feature.h"
@@ -181,8 +180,9 @@
       immediate_schedule_callback_(base::Bind(&EmptySchedulerCallback)),
       weak_ptr_factory_(this) {
   DCHECK(policy_ != nullptr);
-  picker_.reset(
-      new RequestPicker(queue_.get(), policy_.get(), this, &event_logger_));
+  std::unique_ptr<PickRequestTaskFactory> picker_factory(
+      new PickRequestTaskFactory(policy_.get(), this, &event_logger_));
+  queue_->SetPickerFactory(std::move(picker_factory));
 }
 
 RequestCoordinator::~RequestCoordinator() {}
@@ -232,7 +232,7 @@
 
 void RequestCoordinator::GetQueuedRequestsCallback(
     const GetRequestsCallback& callback,
-    RequestQueue::GetRequestsResult result,
+    GetRequestsResult result,
     std::vector<std::unique_ptr<SavePageRequest>> requests) {
   callback.Run(std::move(requests));
 }
@@ -242,13 +242,15 @@
     DCHECK(active_request_.get());
     offliner_->Cancel();
 
-    // If we timed out, let the offliner done callback handle it.
-    if (stop_status == Offliner::RequestStatus::REQUEST_COORDINATOR_TIMED_OUT)
-      return;
-
-    // Otherwise, this attempt never really had a chance to run, mark it
-    // aborted.
-    AbortRequestAttempt(*active_request_.get());
+    if (stop_status == Offliner::RequestStatus::REQUEST_COORDINATOR_TIMED_OUT) {
+      // Consider watchdog timeout a completed attempt.
+      SavePageRequest request(*active_request_.get());
+      UpdateRequestForCompletedAttempt(request,
+                                       Offliner::REQUEST_COORDINATOR_TIMED_OUT);
+    } else {
+      // Otherwise consider this stop an aborted attempt.
+      UpdateRequestForAbortedAttempt(*active_request_.get());
+    }
   }
 
   // Stopping offliner means it will not call callback so set last status.
@@ -267,7 +269,7 @@
 }
 
 void RequestCoordinator::GetRequestsForSchedulingCallback(
-    RequestQueue::GetRequestsResult result,
+    GetRequestsResult result,
     std::vector<std::unique_ptr<SavePageRequest>> requests) {
   bool user_requested = false;
 
@@ -303,10 +305,11 @@
   return false;
 }
 
-void RequestCoordinator::AbortRequestAttempt(const SavePageRequest& request) {
+void RequestCoordinator::UpdateRequestForAbortedAttempt(
+    const SavePageRequest& request) {
   if (request.started_attempt_count() >= policy_->GetMaxStartedTries()) {
-    const BackgroundSavePageResult result(
-        BackgroundSavePageResult::START_COUNT_EXCEEDED);
+    const RequestNotifier::BackgroundSavePageResult result(
+        RequestNotifier::BackgroundSavePageResult::START_COUNT_EXCEEDED);
     event_logger_.RecordDroppedSavePageRequest(request.client_id().name_space,
                                                result, request.request_id());
     RemoveAttemptedRequest(request, result);
@@ -321,7 +324,7 @@
 
 void RequestCoordinator::RemoveAttemptedRequest(
     const SavePageRequest& request,
-    BackgroundSavePageResult result) {
+    RequestNotifier::BackgroundSavePageResult result) {
   std::vector<int64_t> remove_requests;
   remove_requests.push_back(request.request_id());
   queue_->RemoveRequests(remove_requests,
@@ -340,10 +343,10 @@
     NotifyChanged(result->updated_items.at(0));
   } else {
     DVLOG(1) << "Failed to mark request aborted: " << request_id;
-    RequestQueue::UpdateRequestResult request_result =
+    UpdateRequestResult request_result =
         result->store_state != StoreState::LOADED
-            ? RequestQueue::UpdateRequestResult::STORE_FAILURE
-            : RequestQueue::UpdateRequestResult::REQUEST_DOES_NOT_EXIST;
+            ? UpdateRequestResult::STORE_FAILURE
+            : UpdateRequestResult::REQUEST_DOES_NOT_EXIST;
     event_logger_.RecordUpdateRequestFailed(client_id.name_space,
                                             request_result);
   }
@@ -357,7 +360,7 @@
       request_ids,
       base::Bind(&RequestCoordinator::HandleRemovedRequestsAndCallback,
                  weak_ptr_factory_.GetWeakPtr(), callback,
-                 BackgroundSavePageResult::REMOVED));
+                 RequestNotifier::BackgroundSavePageResult::REMOVED));
 
   // Record the network quality when this request is made.
   if (network_quality_estimator_) {
@@ -420,7 +423,7 @@
 }
 
 void RequestCoordinator::AddRequestResultCallback(
-    RequestQueue::AddRequestResult result,
+    AddRequestResult result,
     const SavePageRequest& request) {
   NotifyAdded(request);
   // Inform the scheduler that we have an outstanding task.
@@ -438,10 +441,10 @@
     NotifyChanged(result->updated_items.at(0));
   } else {
     DVLOG(1) << "Failed to mark request completed " << request_id;
-    RequestQueue::UpdateRequestResult request_result =
+    UpdateRequestResult request_result =
         result->store_state != StoreState::LOADED
-            ? RequestQueue::UpdateRequestResult::STORE_FAILURE
-            : RequestQueue::UpdateRequestResult::REQUEST_DOES_NOT_EXIST;
+            ? UpdateRequestResult::STORE_FAILURE
+            : UpdateRequestResult::REQUEST_DOES_NOT_EXIST;
     event_logger_.RecordUpdateRequestFailed(client_id.name_space,
                                             request_result);
   }
@@ -473,7 +476,7 @@
 
 void RequestCoordinator::HandleRemovedRequestsAndCallback(
     const RemoveRequestsCallback& callback,
-    BackgroundSavePageResult status,
+    RequestNotifier::BackgroundSavePageResult status,
     std::unique_ptr<UpdateRequestsResult> result) {
   // TODO(dougarnett): Define status code for user/api cancel and use here
   // to determine whether to record cancel time UMA.
@@ -484,7 +487,7 @@
 }
 
 void RequestCoordinator::HandleRemovedRequests(
-    BackgroundSavePageResult status,
+    RequestNotifier::BackgroundSavePageResult status,
     std::unique_ptr<UpdateRequestsResult> result) {
   for (const auto& request : result->updated_items)
     NotifyCompleted(request, status);
@@ -507,7 +510,10 @@
 }
 
 void RequestCoordinator::HandleWatchdogTimeout() {
-  StopProcessing(Offliner::REQUEST_COORDINATOR_TIMED_OUT);
+  Offliner::RequestStatus watchdog_status =
+      Offliner::REQUEST_COORDINATOR_TIMED_OUT;
+  StopPrerendering(watchdog_status);
+  TryNextRequest();
 }
 
 // Returns true if the caller should expect a callback, false otherwise. For
@@ -515,6 +521,7 @@
 bool RequestCoordinator::StartProcessing(
     const DeviceConditions& device_conditions,
     const base::Callback<void(bool)>& callback) {
+  DVLOG(2) << "Scheduled " << __func__;
   return StartProcessingInternal(ProcessingWindowState::SCHEDULED_WINDOW,
                                  device_conditions, callback);
 }
@@ -547,6 +554,7 @@
 
 RequestCoordinator::OfflinerImmediateStartStatus
 RequestCoordinator::TryImmediateStart() {
+  DVLOG(2) << "Immediate " << __func__;
   // Make sure not already busy processing.
   if (is_busy_)
     return OfflinerImmediateStartStatus::BUSY;
@@ -605,14 +613,17 @@
     return;
   }
 
-  // Choose a request to process that meets the available conditions.
-  // This is an async call, and returns right away.
-  picker_->ChooseNextRequest(base::Bind(&RequestCoordinator::RequestPicked,
-                                        weak_ptr_factory_.GetWeakPtr()),
-                             base::Bind(&RequestCoordinator::RequestNotPicked,
-                                        weak_ptr_factory_.GetWeakPtr()),
-                             current_conditions_.get(),
-                             disabled_requests_);
+  // Ask request queue to make a new PickRequestTask object, then put it on the
+  // task queue.
+  queue_->PickNextRequest(base::Bind(&RequestCoordinator::RequestPicked,
+                                     weak_ptr_factory_.GetWeakPtr()),
+                          base::Bind(&RequestCoordinator::RequestNotPicked,
+                                     weak_ptr_factory_.GetWeakPtr()),
+                          *current_conditions_.get(), disabled_requests_);
+  // TODO(petewil): Verify current_conditions has a good value on all calling
+  // paths.  It is really more of a "last known conditions" than "current
+  // conditions".  Consider having a call to Java to check the current
+  // conditions.
 }
 
 // Called by the request picker when a request has been picked.
@@ -689,10 +700,10 @@
     // TODO(fgorski): what is the best result? Do we create a new status?
     StopProcessing(Offliner::PRERENDERING_NOT_STARTED);
     DVLOG(1) << "Failed to mark attempt started: " << request_id;
-    RequestQueue::UpdateRequestResult request_result =
+    UpdateRequestResult request_result =
         update_result->store_state != StoreState::LOADED
-            ? RequestQueue::UpdateRequestResult::STORE_FAILURE
-            : RequestQueue::UpdateRequestResult::REQUEST_DOES_NOT_EXIST;
+            ? UpdateRequestResult::STORE_FAILURE
+            : UpdateRequestResult::REQUEST_DOES_NOT_EXIST;
     event_logger_.RecordUpdateRequestFailed(client_namespace, request_result);
     return;
   }
@@ -743,30 +754,40 @@
   RecordOfflinerResultUMA(request.client_id(), request.creation_time(),
                           last_offlining_status_);
   watchdog_timer_.Stop();
-
   is_busy_ = false;
   active_request_.reset(nullptr);
 
+  UpdateRequestForCompletedAttempt(request, status);
+  if (ShouldTryNextRequest(status))
+    TryNextRequest();
+  else
+    scheduler_callback_.Run(true);
+}
+
+void RequestCoordinator::UpdateRequestForCompletedAttempt(
+    const SavePageRequest& request,
+    Offliner::RequestStatus status) {
   if (status == Offliner::RequestStatus::FOREGROUND_CANCELED ||
       status == Offliner::RequestStatus::PRERENDERING_CANCELED) {
     // Update the request for the canceled attempt.
     // TODO(dougarnett): See if we can conclusively identify other attempt
     // aborted cases to treat this way (eg, for Render Process Killed).
-    AbortRequestAttempt(request);
+    UpdateRequestForAbortedAttempt(request);
   } else if (status == Offliner::RequestStatus::SAVED) {
     // Remove the request from the queue if it succeeded.
-    RemoveAttemptedRequest(request, BackgroundSavePageResult::SUCCESS);
-  } else if (status == Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY) {
     RemoveAttemptedRequest(request,
-                           BackgroundSavePageResult::PRERENDER_FAILURE);
+                           RequestNotifier::BackgroundSavePageResult::SUCCESS);
+  } else if (status == Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY) {
+    RemoveAttemptedRequest(
+        request, RequestNotifier::BackgroundSavePageResult::PRERENDER_FAILURE);
   } else if (request.completed_attempt_count() + 1 >=
              policy_->GetMaxCompletedTries()) {
     // Remove from the request queue if we exceeded max retries. The +1
     // represents the request that just completed. Since we call
     // MarkAttemptCompleted within the if branches, the completed_attempt_count
     // has not yet been updated when we are checking the if condition.
-    const BackgroundSavePageResult result(
-        BackgroundSavePageResult::RETRY_COUNT_EXCEEDED);
+    const RequestNotifier::BackgroundSavePageResult result(
+        RequestNotifier::BackgroundSavePageResult::RETRY_COUNT_EXCEEDED);
     event_logger_.RecordDroppedSavePageRequest(request.client_id().name_space,
                                                result, request.request_id());
     RemoveAttemptedRequest(request, result);
@@ -779,29 +800,27 @@
                    weak_ptr_factory_.GetWeakPtr(), request.request_id(),
                    request.client_id()));
   }
+}
 
-  // Determine whether we might try another request in this
-  // processing window based on how the previous request completed.
-  switch (status) {
+bool RequestCoordinator::ShouldTryNextRequest(
+    Offliner::RequestStatus previous_request_status) {
+  switch (previous_request_status) {
     case Offliner::RequestStatus::SAVED:
     case Offliner::RequestStatus::SAVE_FAILED:
     case Offliner::RequestStatus::REQUEST_COORDINATOR_CANCELED:
     case Offliner::RequestStatus::REQUEST_COORDINATOR_TIMED_OUT:
     case Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY:
-      // Consider processing another request in this service window.
-      TryNextRequest();
-      break;
+      return true;
     case Offliner::RequestStatus::FOREGROUND_CANCELED:
     case Offliner::RequestStatus::PRERENDERING_CANCELED:
     case Offliner::RequestStatus::PRERENDERING_FAILED:
       // No further processing in this service window.
-      // Let the scheduler know we are done processing.
-      scheduler_callback_.Run(true);
-      break;
+      return false;
     default:
       // Make explicit choice about new status codes that actually reach here.
       // Their default is no further processing in this service window.
       NOTREACHED();
+      return false;
   }
 }
 
@@ -825,12 +844,12 @@
   // Remove the request, but send out SUCCEEDED instead of removed.
   std::vector<int64_t> request_ids { request_id };
   queue_->RemoveRequests(
-    request_ids,
-    base::Bind(&RequestCoordinator::HandleRemovedRequestsAndCallback,
-               weak_ptr_factory_.GetWeakPtr(),
-               base::Bind(&RequestCoordinator::CompletedRequestCallback,
-                          weak_ptr_factory_.GetWeakPtr()),
-               BackgroundSavePageResult::SUCCESS));
+      request_ids,
+      base::Bind(&RequestCoordinator::HandleRemovedRequestsAndCallback,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 base::Bind(&RequestCoordinator::CompletedRequestCallback,
+                            weak_ptr_factory_.GetWeakPtr()),
+                 RequestNotifier::BackgroundSavePageResult::SUCCESS));
 }
 
 const Scheduler::TriggerConditions RequestCoordinator::GetTriggerConditions(
@@ -855,8 +874,9 @@
     observer.OnAdded(request);
 }
 
-void RequestCoordinator::NotifyCompleted(const SavePageRequest& request,
-                                         BackgroundSavePageResult status) {
+void RequestCoordinator::NotifyCompleted(
+    const SavePageRequest& request,
+    RequestNotifier::BackgroundSavePageResult status) {
   for (Observer& observer : observers_)
     observer.OnCompleted(request, status);
 }
diff --git a/components/offline_pages/background/request_coordinator.h b/components/offline_pages/background/request_coordinator.h
index a812c6a..719c65c 100644
--- a/components/offline_pages/background/request_coordinator.h
+++ b/components/offline_pages/background/request_coordinator.h
@@ -34,6 +34,7 @@
 class OfflinerFactory;
 class Offliner;
 class RequestPicker;
+class RequestQueue;
 class SavePageRequest;
 class Scheduler;
 class ClientPolicyController;
@@ -63,13 +64,6 @@
     DISABLED_FOR_OFFLINER,
   };
 
-  // Callback to report when a request was available.
-  typedef base::Callback<void(const SavePageRequest& request)>
-      RequestPickedCallback;
-
-  // Callback to report when no request was available.
-  typedef base::Callback<void(bool)> RequestNotPickedCallback;
-
   // Callback specifying which request IDs were actually removed.
   typedef base::Callback<void(const MultipleItemStatuses&)>
       RemoveRequestsCallback;
@@ -192,9 +186,7 @@
     return processing_state_ == ProcessingWindowState::STOPPED;
   }
 
-  OfflineEventLogger* GetLogger() {
-    return &event_logger_;
-  }
+  RequestCoordinatorEventLogger* GetLogger() { return &event_logger_; }
 
  private:
   // Immediate start attempt status code for UMA.
@@ -229,17 +221,17 @@
   // SavePageRequest objects for the caller of GetQueuedRequests.
   void GetQueuedRequestsCallback(
       const GetRequestsCallback& callback,
-      RequestQueue::GetRequestsResult result,
+      GetRequestsResult result,
       std::vector<std::unique_ptr<SavePageRequest>> requests);
 
   // Receives the results of a get from the request queue, and turns that into
   // SavePageRequest objects for the caller of GetQueuedRequests.
   void GetRequestsForSchedulingCallback(
-      RequestQueue::GetRequestsResult result,
+      GetRequestsResult result,
       std::vector<std::unique_ptr<SavePageRequest>> requests);
 
   // Receives the result of add requests to the request queue.
-  void AddRequestResultCallback(RequestQueue::AddRequestResult result,
+  void AddRequestResultCallback(AddRequestResult result,
                                 const SavePageRequest& request);
 
   // Receives the result of mark attempt completed requests.
@@ -255,10 +247,10 @@
 
   void HandleRemovedRequestsAndCallback(
       const RemoveRequestsCallback& callback,
-      BackgroundSavePageResult status,
+      RequestNotifier::BackgroundSavePageResult status,
       std::unique_ptr<UpdateRequestsResult> result);
 
-  void HandleRemovedRequests(BackgroundSavePageResult status,
+  void HandleRemovedRequests(RequestNotifier::BackgroundSavePageResult status,
                              std::unique_ptr<UpdateRequestsResult> result);
 
   bool StartProcessingInternal(const ProcessingWindowState processing_state,
@@ -302,14 +294,23 @@
   void OfflinerDoneCallback(const SavePageRequest& request,
                             Offliner::RequestStatus status);
 
+  // Records a completed attempt for the request and update it in the queue
+  // (possibly removing it).
+  void UpdateRequestForCompletedAttempt(const SavePageRequest& request,
+                                        Offliner::RequestStatus status);
+
+  // Returns whether we should try another request based on the outcome
+  // of the previous one.
+  bool ShouldTryNextRequest(Offliner::RequestStatus previous_request_status);
+
   void TryNextRequest();
 
   // If there is an active request in the list, cancel that request.
   bool CancelActiveRequestIfItMatches(const std::vector<int64_t>& request_ids);
 
   // Records an aborted attempt for the request and update it in the queue
-  // (possibly removing it). Returns the updated copy.
-  void AbortRequestAttempt(const SavePageRequest& request);
+  // (possibly removing it).
+  void UpdateRequestForAbortedAttempt(const SavePageRequest& request);
 
   // Remove the attempted request from the queue with status to pass through to
   // any observers and UMA histogram.
@@ -384,8 +385,6 @@
   std::unique_ptr<SavePageRequest> active_request_;
   // Status of the most recent offlining.
   Offliner::RequestStatus last_offlining_status_;
-  // Class to choose which request to schedule next
-  std::unique_ptr<RequestPicker> picker_;
   // A set of request_ids that we are holding off until the download manager is
   // done with them.
   std::set<int64_t> disabled_requests_;
diff --git a/components/offline_pages/background/request_coordinator_event_logger.cc b/components/offline_pages/background/request_coordinator_event_logger.cc
index acdf8f76..1585265 100644
--- a/components/offline_pages/background/request_coordinator_event_logger.cc
+++ b/components/offline_pages/background/request_coordinator_event_logger.cc
@@ -66,14 +66,13 @@
   }
 }
 
-static std::string UpdateRequestResultToString(
-    RequestQueue::UpdateRequestResult result) {
+static std::string UpdateRequestResultToString(UpdateRequestResult result) {
   switch (result) {
-    case RequestQueue::UpdateRequestResult::SUCCESS:
+    case UpdateRequestResult::SUCCESS:
       return "SUCCESS";
-    case RequestQueue::UpdateRequestResult::STORE_FAILURE:
+    case UpdateRequestResult::STORE_FAILURE:
       return "STORE_FAILURE";
-    case RequestQueue::UpdateRequestResult::REQUEST_DOES_NOT_EXIST:
+    case UpdateRequestResult::REQUEST_DOES_NOT_EXIST:
       return "REQUEST_DOES_NOT_EXIST";
     default:
       NOTREACHED();
@@ -103,7 +102,7 @@
 
 void RequestCoordinatorEventLogger::RecordUpdateRequestFailed(
     const std::string& name_space,
-    RequestQueue::UpdateRequestResult result) {
+    UpdateRequestResult result) {
   RecordActivity("Updating queued request for " + name_space + " failed - " +
                  UpdateRequestResultToString(result));
 }
diff --git a/components/offline_pages/background/request_coordinator_event_logger.h b/components/offline_pages/background/request_coordinator_event_logger.h
index 61a7beb..ec07079 100644
--- a/components/offline_pages/background/request_coordinator_event_logger.h
+++ b/components/offline_pages/background/request_coordinator_event_logger.h
@@ -10,7 +10,7 @@
 
 #include "components/offline_pages/background/offliner.h"
 #include "components/offline_pages/background/request_notifier.h"
-#include "components/offline_pages/background/request_queue.h"
+#include "components/offline_pages/background/request_queue_results.h"
 #include "components/offline_pages/offline_event_logger.h"
 
 namespace offline_pages {
@@ -30,7 +30,7 @@
       int64_t request_id);
 
   void RecordUpdateRequestFailed(const std::string& name_space,
-                                 RequestQueue::UpdateRequestResult result);
+                                 UpdateRequestResult result);
 };
 
 }  // namespace offline_pages
diff --git a/components/offline_pages/background/request_coordinator_event_logger_unittest.cc b/components/offline_pages/background/request_coordinator_event_logger_unittest.cc
index e41702b..eed1f45 100644
--- a/components/offline_pages/background/request_coordinator_event_logger_unittest.cc
+++ b/components/offline_pages/background/request_coordinator_event_logger_unittest.cc
@@ -15,8 +15,8 @@
 const RequestNotifier::BackgroundSavePageResult kDroppedResult =
     RequestNotifier::BackgroundSavePageResult::START_COUNT_EXCEEDED;
 const int64_t kId = 1234;
-const RequestQueue::UpdateRequestResult kQueueUpdateResult =
-    RequestQueue::UpdateRequestResult::STORE_FAILURE;
+const UpdateRequestResult kQueueUpdateResult =
+    UpdateRequestResult::STORE_FAILURE;
 
 const char kOfflinerStatusLogString[] =
     "Background save attempt for last_n:1234 - SAVED";
diff --git a/components/offline_pages/background/request_coordinator_unittest.cc b/components/offline_pages/background/request_coordinator_unittest.cc
index d12dda4..531c4d17 100644
--- a/components/offline_pages/background/request_coordinator_unittest.cc
+++ b/components/offline_pages/background/request_coordinator_unittest.cc
@@ -22,6 +22,7 @@
 #include "components/offline_pages/background/offliner.h"
 #include "components/offline_pages/background/offliner_factory.h"
 #include "components/offline_pages/background/offliner_policy.h"
+#include "components/offline_pages/background/pick_request_task_factory.h"
 #include "components/offline_pages/background/request_queue.h"
 #include "components/offline_pages/background/request_queue_in_memory_store.h"
 #include "components/offline_pages/background/save_page_request.h"
@@ -263,11 +264,10 @@
   }
 
   // Callback for Add requests.
-  void AddRequestDone(RequestQueue::AddRequestResult result,
-                      const SavePageRequest& request);
+  void AddRequestDone(AddRequestResult result, const SavePageRequest& request);
 
   // Callback for getting requests.
-  void GetRequestsDone(RequestQueue::GetRequestsResult result,
+  void GetRequestsDone(GetRequestsResult result,
                        std::vector<std::unique_ptr<SavePageRequest>> requests);
 
   // Callback for removing requests.
@@ -283,7 +283,7 @@
   void SendOfflinerDoneCallback(const SavePageRequest& request,
                                 Offliner::RequestStatus status);
 
-  RequestQueue::GetRequestsResult last_get_requests_result() const {
+  GetRequestsResult last_get_requests_result() const {
     return last_get_requests_result_;
   }
 
@@ -376,7 +376,7 @@
   }
 
  private:
-  RequestQueue::GetRequestsResult last_get_requests_result_;
+  GetRequestsResult last_get_requests_result_;
   MultipleItemStatuses last_remove_results_;
   std::vector<std::unique_ptr<SavePageRequest>> last_requests_;
   scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
@@ -390,7 +390,7 @@
 };
 
 RequestCoordinatorTest::RequestCoordinatorTest()
-    : last_get_requests_result_(RequestQueue::GetRequestsResult::STORE_FAILURE),
+    : last_get_requests_result_(GetRequestsResult::STORE_FAILURE),
       task_runner_(new base::TestMockTimeTaskRunner),
       task_runner_handle_(task_runner_),
       offliner_(nullptr),
@@ -402,20 +402,26 @@
 
 void RequestCoordinatorTest::SetUp() {
   std::unique_ptr<OfflinerPolicy> policy(new OfflinerPolicy());
-  std::unique_ptr<OfflinerFactory> factory(new OfflinerFactoryStub());
+  std::unique_ptr<OfflinerFactory> offliner_factory(new OfflinerFactoryStub());
   // Save the offliner for use by the tests.
-  offliner_ =
-      reinterpret_cast<OfflinerStub*>(factory->GetOffliner(policy.get()));
+  offliner_ = reinterpret_cast<OfflinerStub*>(
+      offliner_factory->GetOffliner(policy.get()));
   std::unique_ptr<RequestQueueInMemoryStore>
       store(new RequestQueueInMemoryStore());
   std::unique_ptr<RequestQueue> queue(new RequestQueue(std::move(store)));
   std::unique_ptr<Scheduler> scheduler_stub(new SchedulerStub());
   network_quality_estimator_.reset(new NetworkQualityEstimatorStub());
   coordinator_.reset(new RequestCoordinator(
-      std::move(policy), std::move(factory), std::move(queue),
+      std::move(policy), std::move(offliner_factory), std::move(queue),
       std::move(scheduler_stub), network_quality_estimator_.get()));
   coordinator_->AddObserver(&observer_);
   SetNetworkConnected(true);
+  std::unique_ptr<PickRequestTaskFactory> picker_factory(
+      new PickRequestTaskFactory(
+          coordinator_->policy(),
+          static_cast<RequestNotifier*>(coordinator_.get()),
+          coordinator_->GetLogger()));
+  coordinator_->queue()->SetPickerFactory(std::move(picker_factory));
 }
 
 void RequestCoordinatorTest::PumpLoop() {
@@ -423,7 +429,7 @@
 }
 
 void RequestCoordinatorTest::GetRequestsDone(
-    RequestQueue::GetRequestsResult result,
+    GetRequestsResult result,
     std::vector<std::unique_ptr<SavePageRequest>> requests) {
   last_get_requests_result_ = result;
   last_requests_ = std::move(requests);
@@ -441,9 +447,8 @@
   waiter_.Signal();
 }
 
-void RequestCoordinatorTest::AddRequestDone(
-    RequestQueue::AddRequestResult result,
-    const SavePageRequest& request) {}
+void RequestCoordinatorTest::AddRequestDone(AddRequestResult result,
+                                            const SavePageRequest& request) {}
 
 void RequestCoordinatorTest::SetupForOfflinerDoneCallbackTest(
     offline_pages::SavePageRequest* request) {
@@ -1073,6 +1078,9 @@
   // Build a request to use with the pre-renderer, and put it on the queue.
   offline_pages::SavePageRequest request(
       kRequestId1, kUrl1, kClientId1, base::Time::Now(), kUserRequested);
+  // Set request to allow one more completed attempt.
+  int max_tries = coordinator()->policy()->GetMaxCompletedTries();
+  request.set_completed_attempt_count(max_tries - 1);
   coordinator()->queue()->AddRequest(
       request,
       base::Bind(&RequestCoordinatorTest::AddRequestDone,
@@ -1109,6 +1117,7 @@
   PumpLoop();
 
   EXPECT_FALSE(is_starting());
+  EXPECT_FALSE(coordinator()->is_busy());
   EXPECT_TRUE(OfflinerWasCanceled());
 }
 
diff --git a/components/offline_pages/background/request_picker.cc b/components/offline_pages/background/request_picker.cc
deleted file mode 100644
index c1d2783..0000000
--- a/components/offline_pages/background/request_picker.cc
+++ /dev/null
@@ -1,276 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/request_picker.h"
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "components/offline_pages/background/save_page_request.h"
-
-namespace {
-template <typename T>
-int signum(T t) {
-  return (T(0) < t) - (t < T(0));
-}
-
-#define CALL_MEMBER_FUNCTION(object, ptrToMember) ((object)->*(ptrToMember))
-}  // namespace
-
-namespace offline_pages {
-
-RequestPicker::RequestPicker(RequestQueue* requestQueue,
-                             OfflinerPolicy* policy,
-                             RequestNotifier* notifier,
-                             RequestCoordinatorEventLogger* event_logger)
-    : queue_(requestQueue),
-      policy_(policy),
-      notifier_(notifier),
-      event_logger_(event_logger),
-      fewer_retries_better_(false),
-      earlier_requests_better_(false),
-      weak_ptr_factory_(this) {}
-
-RequestPicker::~RequestPicker() {}
-
-// Entry point for the async operation to choose the next request.
-void RequestPicker::ChooseNextRequest(
-    RequestCoordinator::RequestPickedCallback picked_callback,
-    RequestCoordinator::RequestNotPickedCallback not_picked_callback,
-    DeviceConditions* device_conditions,
-    const std::set<int64_t>& disabled_requests) {
-  picked_callback_ = picked_callback;
-  not_picked_callback_ = not_picked_callback;
-  fewer_retries_better_ = policy_->ShouldPreferUntriedRequests();
-  earlier_requests_better_ = policy_->ShouldPreferEarlierRequests();
-  current_conditions_.reset(new DeviceConditions(*device_conditions));
-  // Get all requests from queue (there is no filtering mechanism).
-  queue_->GetRequests(base::Bind(&RequestPicker::GetRequestResultCallback,
-                                 weak_ptr_factory_.GetWeakPtr(),
-                                 disabled_requests));
-}
-
-// When we get contents from the queue, use them to pick the next
-// request to operate on (if any).
-void RequestPicker::GetRequestResultCallback(
-    const std::set<int64_t>& disabled_requests,
-    RequestQueue::GetRequestsResult,
-    std::vector<std::unique_ptr<SavePageRequest>> requests) {
-  // If there is nothing to do, return right away.
-  if (requests.size() == 0) {
-    not_picked_callback_.Run(false);
-    return;
-  }
-
-  // Get the expired requests to be removed from the queue, and the valid ones
-  // from which to pick the next request.
-  std::vector<std::unique_ptr<SavePageRequest>> valid_requests;
-  std::vector<std::unique_ptr<SavePageRequest>> expired_requests;
-  SplitRequests(std::move(requests), &valid_requests, &expired_requests);
-  std::vector<int64_t> expired_request_ids;
-  for (const auto& request : expired_requests)
-    expired_request_ids.push_back(request->request_id());
-
-  queue_->RemoveRequests(expired_request_ids,
-                         base::Bind(&RequestPicker::OnRequestExpired,
-                                    weak_ptr_factory_.GetWeakPtr()));
-
-  // Pick the most deserving request for our conditions.
-  const SavePageRequest* picked_request = nullptr;
-
-  RequestCompareFunction comparator = nullptr;
-
-  // Choose which comparison function to use based on policy.
-  if (policy_->RetryCountIsMoreImportantThanRecency())
-    comparator = &RequestPicker::RetryCountFirstCompareFunction;
-  else
-    comparator = &RequestPicker::RecencyFirstCompareFunction;
-
-  // Iterate once through the requests, keeping track of best candidate.
-  bool non_user_requested_tasks_remaining = false;
-  for (unsigned i = 0; i < valid_requests.size(); ++i) {
-    // If the  request is on the disabled list, skip it.
-    auto search = disabled_requests.find(valid_requests[i]->request_id());
-    if (search != disabled_requests.end()) {
-      continue;
-    }
-    if (!valid_requests[i]->user_requested())
-      non_user_requested_tasks_remaining = true;
-    if (!RequestConditionsSatisfied(valid_requests[i].get()))
-      continue;
-    if (IsNewRequestBetter(picked_request, valid_requests[i].get(), comparator))
-      picked_request = valid_requests[i].get();
-  }
-
-  // If we have a best request to try next, get the request coodinator to
-  // start it.  Otherwise return that we have no candidates.
-  if (picked_request != nullptr) {
-    picked_callback_.Run(*picked_request);
-  } else {
-    not_picked_callback_.Run(non_user_requested_tasks_remaining);
-  }
-}
-
-// Filter out requests that don't meet the current conditions.  For instance, if
-// this is a predictive request, and we are not on WiFi, it should be ignored
-// this round.
-bool RequestPicker::RequestConditionsSatisfied(const SavePageRequest* request) {
-  // If the user did not request the page directly, make sure we are connected
-  // to power and have WiFi and sufficient battery remaining before we take this
-  // request.
-
-  if (!current_conditions_->IsPowerConnected() &&
-      policy_->PowerRequired(request->user_requested())) {
-    return false;
-  }
-
-  if (current_conditions_->GetNetConnectionType() !=
-          net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI &&
-      policy_->UnmeteredNetworkRequired(request->user_requested())) {
-    return false;
-  }
-
-  if (current_conditions_->GetBatteryPercentage() <
-      policy_->BatteryPercentageRequired(request->user_requested())) {
-    return false;
-  }
-
-  // If we have already started this page the max number of times, it is not
-  // eligible to try again.
-  if (request->started_attempt_count() >= policy_->GetMaxStartedTries())
-    return false;
-
-  // If we have already completed trying this page the max number of times, it
-  // is not eligible to try again.
-  if (request->completed_attempt_count() >= policy_->GetMaxCompletedTries())
-    return false;
-
-  // If the request is paused, do not consider it.
-  if (request->request_state() == SavePageRequest::RequestState::PAUSED)
-    return false;
-
-  // If the request is expired, do not consider it.
-  base::TimeDelta requestAge = base::Time::Now() - request->creation_time();
-  if (requestAge >
-      base::TimeDelta::FromSeconds(
-          policy_->GetRequestExpirationTimeInSeconds()))
-    return false;
-
-  // If this request is not active yet, return false.
-  // TODO(petewil): If the only reason we return nothing to do is that we have
-  // inactive requests, we still want to try again later after their activation
-  // time elapses, we shouldn't take ourselves completely off the scheduler.
-  if (request->activation_time() > base::Time::Now())
-    return false;
-
-  return true;
-}
-
-// Look at policies to decide which requests to prefer.
-bool RequestPicker::IsNewRequestBetter(const SavePageRequest* oldRequest,
-                                       const SavePageRequest* newRequest,
-                                       RequestCompareFunction comparator) {
-
-  // If there is no old request, the new one is better.
-  if (oldRequest == nullptr)
-    return true;
-
-  // User requested pages get priority.
-  if (newRequest->user_requested() && !oldRequest->user_requested())
-    return true;
-
-  // Otherwise, use the comparison function for the current policy, which
-  // returns true if the older request is better.
-  return !(CALL_MEMBER_FUNCTION(this, comparator)(oldRequest, newRequest));
-}
-
-// Compare the results, checking request count before recency.  Returns true if
-// left hand side is better, false otherwise.
-bool RequestPicker::RetryCountFirstCompareFunction(
-    const SavePageRequest* left, const SavePageRequest* right) {
-  // Check the attempt count.
-  int result = CompareRetryCount(left, right);
-
-  if (result != 0)
-    return (result > 0);
-
-  // If we get here, the attempt counts were the same, so check recency.
-  result = CompareCreationTime(left, right);
-
-  return (result > 0);
-}
-
-// Compare the results, checking recency before request count. Returns true if
-// left hand side is better, false otherwise.
-bool RequestPicker::RecencyFirstCompareFunction(
-    const SavePageRequest* left, const SavePageRequest* right) {
-  // Check the recency.
-  int result = CompareCreationTime(left, right);
-
-  if (result != 0)
-    return (result > 0);
-
-  // If we get here, the recency was the same, so check the attempt count.
-  result = CompareRetryCount(left, right);
-
-  return (result > 0);
-}
-
-// Compare left and right side, returning 1 if the left side is better
-// (preferred by policy), 0 if the same, and -1 if the right side is better.
-int RequestPicker::CompareRetryCount(
-    const SavePageRequest* left, const SavePageRequest* right) {
-  // Check the attempt count.
-  int result = signum(left->completed_attempt_count() -
-                      right->completed_attempt_count());
-
-  // Flip the direction of comparison if policy prefers fewer retries.
-  if (fewer_retries_better_)
-    result *= -1;
-
-  return result;
-}
-
-// Compare left and right side, returning 1 if the left side is better
-// (preferred by policy), 0 if the same, and -1 if the right side is better.
-int RequestPicker::CompareCreationTime(
-    const SavePageRequest* left, const SavePageRequest* right) {
-  // Check the recency.
-  base::TimeDelta difference = left->creation_time() - right->creation_time();
-  int result = signum(difference.InMilliseconds());
-
-  // Flip the direction of comparison if policy prefers fewer retries.
-  if (earlier_requests_better_)
-    result *= -1;
-
-  return result;
-}
-
-void RequestPicker::SplitRequests(
-    std::vector<std::unique_ptr<SavePageRequest>> requests,
-    std::vector<std::unique_ptr<SavePageRequest>>* valid_requests,
-    std::vector<std::unique_ptr<SavePageRequest>>* expired_requests) {
-  for (auto& request : requests) {
-    if (base::Time::Now() - request->creation_time() >=
-        base::TimeDelta::FromSeconds(kRequestExpirationTimeInSeconds)) {
-      expired_requests->push_back(std::move(request));
-    } else {
-      valid_requests->push_back(std::move(request));
-    }
-  }
-}
-
-// Callback used after expired requests are deleted from the queue and notifies
-// the coordinator.
-void RequestPicker::OnRequestExpired(
-    std::unique_ptr<UpdateRequestsResult> result) {
-  const RequestCoordinator::BackgroundSavePageResult save_page_result(
-      RequestCoordinator::BackgroundSavePageResult::EXPIRED);
-  for (const auto& request : result->updated_items) {
-    event_logger_->RecordDroppedSavePageRequest(
-        request.client_id().name_space, save_page_result, request.request_id());
-    notifier_->NotifyCompleted(request, save_page_result);
-  }
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/request_picker.h b/components/offline_pages/background/request_picker.h
deleted file mode 100644
index d07dd80b..0000000
--- a/components/offline_pages/background/request_picker.h
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_PICKER_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_PICKER_H_
-
-#include <memory>
-#include <set>
-
-#include "base/memory/weak_ptr.h"
-#include "components/offline_pages/background/device_conditions.h"
-#include "components/offline_pages/background/offliner_policy.h"
-#include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/background/request_coordinator_event_logger.h"
-#include "components/offline_pages/background/request_queue.h"
-
-namespace offline_pages {
-
-class RequestNotifier;
-
-typedef bool (RequestPicker::*RequestCompareFunction)(
-    const SavePageRequest* left, const SavePageRequest* right);
-
-class RequestPicker {
- public:
-  RequestPicker(RequestQueue* requestQueue,
-                OfflinerPolicy* policy,
-                RequestNotifier* notifier,
-                RequestCoordinatorEventLogger* event_logger);
-
-  ~RequestPicker();
-
-  // Choose which request we should process next based on the current
-  // conditions, and call back to the RequestCoordinator when we have one.
-  void ChooseNextRequest(
-      RequestCoordinator::RequestPickedCallback picked_callback,
-      RequestCoordinator::RequestNotPickedCallback not_picked_callback,
-      DeviceConditions* device_conditions,
-      const std::set<int64_t>& disabled_requests);
-
- private:
-  // Callback for the GetRequest results to be delivered.
-  void GetRequestResultCallback(
-      const std::set<int64_t>& disabled_requests,
-      RequestQueue::GetRequestsResult result,
-      std::vector<std::unique_ptr<SavePageRequest>> results);
-
-  // Filter out requests that don't meet the current conditions.  For instance,
-  // if this is a predictive request, and we are not on WiFi, it should be
-  // ignored this round.
-  bool RequestConditionsSatisfied(const SavePageRequest* request);
-
-  // Using policies, decide if the new request is preferable to the best we have
-  // so far.
-  bool IsNewRequestBetter(const SavePageRequest* oldRequest,
-                          const SavePageRequest* newRequest,
-                          RequestCompareFunction comparator);
-
-  // Is the new request preferable from the retry count first standpoint?
-  bool RetryCountFirstCompareFunction(const SavePageRequest* left,
-                                      const SavePageRequest* right);
-
-  // Is the new request better from the recency first standpoint?
-  bool RecencyFirstCompareFunction(const SavePageRequest* left,
-                                   const SavePageRequest* right);
-
-  // Does the new request have better retry count?
-  int CompareRetryCount(const SavePageRequest* left,
-                        const SavePageRequest* right);
-
-  // Does the new request have better creation time?
-  int CompareCreationTime(const SavePageRequest* left,
-                          const SavePageRequest* right);
-
-  // Split all requests into expired ones and still valid ones.  Takes ownership
-  // of the requests, and moves them into either valid or expired requests.
-  void SplitRequests(
-      std::vector<std::unique_ptr<SavePageRequest>> requests,
-      std::vector<std::unique_ptr<SavePageRequest>>* valid_requests,
-      std::vector<std::unique_ptr<SavePageRequest>>* expired_requests);
-
-  // Callback used after requests get expired.
-  void OnRequestExpired(std::unique_ptr<UpdateRequestsResult> result);
-
-  // Unowned pointer to the request queue.
-  RequestQueue* queue_;
-  // Unowned pointer to the policy object.
-  OfflinerPolicy* policy_;
-  // Unowned pointer to the request coordinator.
-  RequestNotifier* notifier_;
-  // Unowned pointer to the event logger.
-  RequestCoordinatorEventLogger* event_logger_;
-  // Current conditions on the device
-  std::unique_ptr<DeviceConditions> current_conditions_;
-  // True if we prefer less-tried requests
-  bool fewer_retries_better_;
-  // True if we prefer requests submitted more recently
-  bool earlier_requests_better_;
-  // Callback for when we are done picking a request to do next.
-  RequestCoordinator::RequestPickedCallback picked_callback_;
-  // Callback for when there are no more reqeusts to pick.
-  RequestCoordinator::RequestNotPickedCallback not_picked_callback_;
-  // Allows us to pass a weak pointer to callbacks.
-  base::WeakPtrFactory<RequestPicker> weak_ptr_factory_;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_PICKER_H_
diff --git a/components/offline_pages/background/request_queue.cc b/components/offline_pages/background/request_queue.cc
index 8e2103e8..b66c289 100644
--- a/components/offline_pages/background/request_queue.cc
+++ b/components/offline_pages/background/request_queue.cc
@@ -13,6 +13,8 @@
 #include "components/offline_pages/background/mark_attempt_aborted_task.h"
 #include "components/offline_pages/background/mark_attempt_completed_task.h"
 #include "components/offline_pages/background/mark_attempt_started_task.h"
+#include "components/offline_pages/background/pick_request_task.h"
+#include "components/offline_pages/background/pick_request_task_factory.h"
 #include "components/offline_pages/background/remove_requests_task.h"
 #include "components/offline_pages/background/request_queue_store.h"
 #include "components/offline_pages/background/save_page_request.h"
@@ -24,9 +26,8 @@
 void GetRequestsDone(const RequestQueue::GetRequestsCallback& callback,
                      bool success,
                      std::vector<std::unique_ptr<SavePageRequest>> requests) {
-  RequestQueue::GetRequestsResult result =
-      success ? RequestQueue::GetRequestsResult::SUCCESS
-              : RequestQueue::GetRequestsResult::STORE_FAILURE;
+  GetRequestsResult result =
+      success ? GetRequestsResult::SUCCESS : GetRequestsResult::STORE_FAILURE;
   // TODO(fgorski): Filter out expired requests based on policy.
   // This may trigger the purging if necessary.
   // Also this may be turned into a method on the request queue or add a policy
@@ -38,16 +39,16 @@
 void AddRequestDone(const RequestQueue::AddRequestCallback& callback,
                     const SavePageRequest& request,
                     ItemActionStatus status) {
-  RequestQueue::AddRequestResult result;
+  AddRequestResult result;
   switch (status) {
     case ItemActionStatus::SUCCESS:
-      result = RequestQueue::AddRequestResult::SUCCESS;
+      result = AddRequestResult::SUCCESS;
       break;
     case ItemActionStatus::ALREADY_EXISTS:
-      result = RequestQueue::AddRequestResult::ALREADY_EXISTS;
+      result = AddRequestResult::ALREADY_EXISTS;
       break;
     case ItemActionStatus::STORE_ERROR:
-      result = RequestQueue::AddRequestResult::STORE_FAILURE;
+      result = AddRequestResult::STORE_FAILURE;
       break;
     case ItemActionStatus::NOT_FOUND:
     default:
@@ -114,4 +115,19 @@
 
 void RequestQueue::PurgeRequests(const PurgeRequestsCallback& callback) {}
 
+void RequestQueue::PickNextRequest(
+    PickRequestTask::RequestPickedCallback picked_callback,
+    PickRequestTask::RequestNotPickedCallback not_picked_callback,
+    DeviceConditions& conditions,
+    std::set<int64_t>& disabled_requests) {
+  // Using the PickerContext, create a picker task.
+  std::unique_ptr<Task> task(picker_factory_->CreatePickerTask(
+      store_.get(), picked_callback, not_picked_callback, conditions,
+      disabled_requests));
+
+  // Queue up the picking task, it will call one of the callbacks when it
+  // completes.
+  task_queue_.AddTask(std::move(task));
+}
+
 }  // namespace offline_pages
diff --git a/components/offline_pages/background/request_queue.h b/components/offline_pages/background/request_queue.h
index 3168ea1..bed7ca26 100644
--- a/components/offline_pages/background/request_queue.h
+++ b/components/offline_pages/background/request_queue.h
@@ -8,12 +8,17 @@
 #include <stdint.h>
 
 #include <memory>
+#include <set>
 #include <string>
 #include <vector>
 
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "components/offline_pages/background/device_conditions.h"
+#include "components/offline_pages/background/pick_request_task.h"
+#include "components/offline_pages/background/pick_request_task_factory.h"
+#include "components/offline_pages/background/request_queue_results.h"
 #include "components/offline_pages/background/save_page_request.h"
 #include "components/offline_pages/core/task_queue.h"
 #include "components/offline_pages/offline_page_item.h"
@@ -22,31 +27,11 @@
 namespace offline_pages {
 
 class RequestQueueStore;
-typedef StoreUpdateResult<SavePageRequest> UpdateRequestsResult;
+class PickRequestTaskFactory;
 
 // Class responsible for managing save page requests.
 class RequestQueue {
  public:
-  enum class GetRequestsResult {
-    SUCCESS,
-    STORE_FAILURE,
-  };
-
-  enum class AddRequestResult {
-    SUCCESS,
-    STORE_FAILURE,
-    ALREADY_EXISTS,
-    REQUEST_QUOTA_HIT,  // Cannot add a request with this namespace, as it has
-                        // reached a quota of active requests.
-  };
-
-  // GENERATED_JAVA_ENUM_PACKAGE:org.chromium.components.offlinepages.background
-  enum class UpdateRequestResult {
-    SUCCESS,
-    STORE_FAILURE,
-    REQUEST_DOES_NOT_EXIST,  // Failed to delete the request because it does not
-                             // exist.
-  };
 
   // Callback used for |GetRequests|.
   typedef base::Callback<void(GetRequestsResult,
@@ -103,6 +88,22 @@
   // are returned through |callback|.
   void MarkAttemptCompleted(int64_t request_id, const UpdateCallback& callback);
 
+  // Make a task to pick the next request, and report our choice to the
+  // callbacks.
+  void PickNextRequest(
+      PickRequestTask::RequestPickedCallback picked_callback,
+      PickRequestTask::RequestNotPickedCallback not_picked_callback,
+      DeviceConditions& conditions,
+      std::set<int64_t>& disabled_requests);
+
+  // Takes ownership of the factory.  We use a setter to allow users of the
+  // request queue to not need a PickerFactory to create it, since we have lots
+  // of code using the request queue.  The request coordinator will set a
+  // factory before calling PickNextRequest.
+  void SetPickerFactory(std::unique_ptr<PickRequestTaskFactory> factory) {
+    picker_factory_ = std::move(factory);
+  }
+
  private:
   // Callback used by |PurgeRequests|.
   typedef base::Callback<void(UpdateRequestResult,
@@ -119,6 +120,9 @@
   // Task queue to serialize store access.
   TaskQueue task_queue_;
 
+  // Builds PickRequestTask objects.
+  std::unique_ptr<PickRequestTaskFactory> picker_factory_;
+
   // Allows us to pass a weak pointer to callbacks.
   base::WeakPtrFactory<RequestQueue> weak_ptr_factory_;
 
diff --git a/components/offline_pages/background/request_queue_results.h b/components/offline_pages/background/request_queue_results.h
new file mode 100644
index 0000000..ae5326c
--- /dev/null
+++ b/components/offline_pages/background/request_queue_results.h
@@ -0,0 +1,40 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_RESULTS_H_
+#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_RESULTS_H_
+
+#include "components/offline_pages/background/save_page_request.h"
+#include "components/offline_pages/offline_store_types.h"
+
+namespace offline_pages {
+
+// Extracted from RequestQueue so that we can build types that use these results
+// that RequestQueue depends on (for example, the PickRequestTask).
+typedef StoreUpdateResult<SavePageRequest> UpdateRequestsResult;
+
+enum class GetRequestsResult {
+  SUCCESS,
+  STORE_FAILURE,
+};
+
+enum class AddRequestResult {
+  SUCCESS,
+  STORE_FAILURE,
+  ALREADY_EXISTS,
+  REQUEST_QUOTA_HIT,  // Cannot add a request with this namespace, as it has
+                      // reached a quota of active requests.
+};
+
+// GENERATED_JAVA_ENUM_PACKAGE:org.chromium.components.offlinepages.background
+enum class UpdateRequestResult {
+  SUCCESS,
+  STORE_FAILURE,
+  REQUEST_DOES_NOT_EXIST,  // Failed to delete the request because it does not
+                           // exist.
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_RESULTS_H_
diff --git a/components/offline_pages/background/request_queue_unittest.cc b/components/offline_pages/background/request_queue_unittest.cc
index 1e6a0ddc..d36496a1 100644
--- a/components/offline_pages/background/request_queue_unittest.cc
+++ b/components/offline_pages/background/request_queue_unittest.cc
@@ -10,15 +10,20 @@
 #include "base/bind.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/background/device_conditions.h"
+#include "components/offline_pages/background/offliner_policy.h"
+#include "components/offline_pages/background/request_coordinator.h"
+#include "components/offline_pages/background/request_coordinator_event_logger.h"
+#include "components/offline_pages/background/request_notifier.h"
 #include "components/offline_pages/background/request_queue_in_memory_store.h"
 #include "components/offline_pages/background/save_page_request.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace offline_pages {
 
-using AddRequestResult = RequestQueue::AddRequestResult;
-using GetRequestsResult = RequestQueue::GetRequestsResult;
-using UpdateRequestResult = RequestQueue::UpdateRequestResult;
+using AddRequestResult = AddRequestResult;
+using GetRequestsResult = GetRequestsResult;
+using UpdateRequestResult = UpdateRequestResult;
 
 namespace {
 // Data for request 1.
@@ -31,8 +36,50 @@
 const ClientId kClientId2("bookmark", "567");
 const bool kUserRequested = true;
 const int64_t kRequestId3 = 99;
+const int kOneWeekInSeconds = 7 * 24 * 60 * 60;
+
+// Default request
+const SavePageRequest kEmptyRequest(0UL,
+                                    GURL(""),
+                                    ClientId("", ""),
+                                    base::Time(),
+                                    true);
+
 }  // namespace
 
+// Helper class needed by the PickRequestTask
+class RequestNotifierStub : public RequestNotifier {
+ public:
+  RequestNotifierStub()
+      : last_expired_request_(kEmptyRequest), total_expired_requests_(0) {}
+
+  void NotifyAdded(const SavePageRequest& request) override {}
+  void NotifyChanged(const SavePageRequest& request) override {}
+
+  void NotifyCompleted(const SavePageRequest& request,
+                       BackgroundSavePageResult status) override {
+    last_expired_request_ = request;
+    last_request_expiration_status_ = status;
+    total_expired_requests_++;
+  }
+
+  const SavePageRequest& last_expired_request() {
+    return last_expired_request_;
+  }
+
+  RequestCoordinator::BackgroundSavePageResult
+  last_request_expiration_status() {
+    return last_request_expiration_status_;
+  }
+
+  int32_t total_expired_requests() { return total_expired_requests_; }
+
+ private:
+  BackgroundSavePageResult last_request_expiration_status_;
+  SavePageRequest last_expired_request_;
+  int32_t total_expired_requests_;
+};
+
 // TODO(fgorski): Add tests for store failures in add/remove/get.
 class RequestQueueTest : public testing::Test {
  public:
@@ -76,6 +123,9 @@
     return update_requests_result_.get();
   }
 
+  void RequestPickedCallback(const SavePageRequest& request) {}
+  void RequestNotPickedCallback(bool non_user_requested_tasks_remain) {}
+
  private:
   AddRequestResult last_add_result_;
   std::unique_ptr<SavePageRequest> last_added_request_;
@@ -484,4 +534,53 @@
             update_requests_result()->updated_items.at(0).request_state());
 }
 
+// Request expiration is detected during the call to pick a request, which
+// is why this test calls PickNextRequest().
+TEST_F(RequestQueueTest, CleanStaleRequests) {
+  // Create a request that is already expired.
+  base::Time creation_time =
+      base::Time::Now() - base::TimeDelta::FromSeconds(2 * kOneWeekInSeconds);
+
+  SavePageRequest original_request(kRequestId, kUrl, kClientId, creation_time,
+                                   kUserRequested);
+  queue()->AddRequest(
+      original_request,
+      base::Bind(&RequestQueueTest::AddRequestDone, base::Unretained(this)));
+  this->PumpLoop();
+  this->ClearResults();
+
+  // Set up a picker factory pointing to our fake notifier.
+  OfflinerPolicy policy;
+  RequestNotifierStub notifier;
+  RequestCoordinatorEventLogger event_logger;
+  std::unique_ptr<PickRequestTaskFactory> picker_factory(
+      new PickRequestTaskFactory(&policy, &notifier, &event_logger));
+  queue()->SetPickerFactory(std::move(picker_factory));
+
+  // Do a pick and clean operation, which will remove stale entries.
+  DeviceConditions conditions;
+  std::set<int64_t> disabled_list;
+  queue()->PickNextRequest(
+      base::Bind(&RequestQueueTest::RequestPickedCallback,
+                 base::Unretained(this)),
+      base::Bind(&RequestQueueTest::RequestNotPickedCallback,
+                 base::Unretained(this)),
+      conditions, disabled_list);
+
+  this->PumpLoop();
+
+  // Notifier should have been notified that the request was removed.
+  ASSERT_EQ(notifier.last_expired_request().request_id(), kRequestId);
+  ASSERT_EQ(notifier.last_request_expiration_status(),
+            RequestNotifier::BackgroundSavePageResult::EXPIRED);
+
+  // Doing a get should show no entries left in the queue since the expired
+  // request has been removed.
+  queue()->GetRequests(
+      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
+  this->PumpLoop();
+  ASSERT_EQ(GetRequestsResult::SUCCESS, this->last_get_requests_result());
+  ASSERT_TRUE(this->last_requests().empty());
+}
+
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/task_queue.cc b/components/offline_pages/core/task_queue.cc
index 115c509..6f97413 100644
--- a/components/offline_pages/core/task_queue.cc
+++ b/components/offline_pages/core/task_queue.cc
@@ -30,6 +30,8 @@
 }
 
 void TaskQueue::MaybeStartTask() {
+  DVLOG(2) << "running? " << HasRunningTask() << ", pending? "
+           << HasPendingTasks() << " " << __func__;
   if (HasRunningTask() || !HasPendingTasks())
     return;
 
diff --git a/components/policy/core/common/BUILD.gn b/components/policy/core/common/BUILD.gn
index ced53032..cbf2b87c 100644
--- a/components/policy/core/common/BUILD.gn
+++ b/components/policy/core/common/BUILD.gn
@@ -147,6 +147,7 @@
       "//components/data_use_measurement/core",
       "//components/json_schema",
       "//components/prefs",
+      "//extensions/features",
       "//google_apis",
       "//net",
       "//third_party/re2",
@@ -290,6 +291,7 @@
   source_set("unit_tests") {
     testonly = true
     sources = [
+      "//extensions/features",
       "cloud/cloud_policy_client_unittest.cc",
       "cloud/cloud_policy_core_unittest.cc",
       "cloud/cloud_policy_manager_unittest.cc",
@@ -356,6 +358,7 @@
       "//base/test:test_support",
       "//components/policy:generated",
       "//components/prefs:test_support",
+      "//extensions/features",
       "//google_apis",
       "//net:test_support",
       "//testing/gmock",
diff --git a/components/policy/core/common/DEPS b/components/policy/core/common/DEPS
index 7af9e160c..dd9c59b 100644
--- a/components/policy/core/common/DEPS
+++ b/components/policy/core/common/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "-components/policy/core/browser",
   "+components/data_use_measurement/core",
+  "+extensions/features",
 ]
diff --git a/components/policy/core/common/schema_registry.cc b/components/policy/core/common/schema_registry.cc
index 03580859..eb447b84 100644
--- a/components/policy/core/common/schema_registry.cc
+++ b/components/policy/core/common/schema_registry.cc
@@ -5,6 +5,7 @@
 #include "components/policy/core/common/schema_registry.h"
 
 #include "base/logging.h"
+#include "extensions/features/features.h"
 
 namespace policy {
 
@@ -15,7 +16,7 @@
 SchemaRegistry::SchemaRegistry() : schema_map_(new SchemaMap) {
   for (int i = 0; i < POLICY_DOMAIN_SIZE; ++i)
     domains_ready_[i] = false;
-#if !defined(ENABLE_EXTENSIONS)
+#if !BUILDFLAG(ENABLE_EXTENSIONS)
   SetExtensionsDomainsReady();
 #endif
 }
diff --git a/components/policy/core/common/schema_registry_unittest.cc b/components/policy/core/common/schema_registry_unittest.cc
index 08134d2..b9fdbf1 100644
--- a/components/policy/core/common/schema_registry_unittest.cc
+++ b/components/policy/core/common/schema_registry_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "components/policy/core/common/policy_namespace.h"
 #include "components/policy/core/common/schema.h"
+#include "extensions/features/features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -114,7 +115,7 @@
   registry.AddObserver(&observer);
 
   EXPECT_FALSE(registry.IsReady());
-#if defined(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   EXPECT_CALL(observer, OnSchemaRegistryReady()).Times(0);
   registry.SetExtensionsDomainsReady();
   Mock::VerifyAndClearExpectations(&observer);
diff --git a/components/safe_browsing_db/v4_local_database_manager.cc b/components/safe_browsing_db/v4_local_database_manager.cc
index b9676df..07aa34b 100644
--- a/components/safe_browsing_db/v4_local_database_manager.cc
+++ b/components/safe_browsing_db/v4_local_database_manager.cc
@@ -33,6 +33,8 @@
   return ListInfos(
       {ListInfo(true, "UrlMalware.store", GetUrlMalwareId(),
                 SB_THREAT_TYPE_URL_MALWARE),
+       ListInfo(true, "UrlMalBin.store", GetUrlMalBinId(),
+                SB_THREAT_TYPE_BINARY_MALWARE_URL),
        ListInfo(true, "UrlSoceng.store", GetUrlSocEngId(),
                 SB_THREAT_TYPE_URL_PHISHING),
        ListInfo(false, "", GetChromeUrlApiId(), SB_THREAT_TYPE_API_ABUSE),
diff --git a/components/security_state/security_state_model.cc b/components/security_state/security_state_model.cc
index 6d4094e..464efa9 100644
--- a/components/security_state/security_state_model.cc
+++ b/components/security_state/security_state_model.cc
@@ -125,12 +125,15 @@
     SecurityStateModel::ContentStatus mixed_content_status,
     SecurityStateModel::ContentStatus content_with_cert_errors_status) {
   DCHECK(visible_security_state.connection_info_initialized ||
-         visible_security_state.fails_malware_check);
+         visible_security_state.malicious_content_status !=
+             SecurityStateModel::MALICIOUS_CONTENT_STATUS_NONE);
 
   // Override the connection security information if the website failed the
   // browser's malware checks.
-  if (visible_security_state.fails_malware_check)
+  if (visible_security_state.malicious_content_status !=
+      SecurityStateModel::MALICIOUS_CONTENT_STATUS_NONE) {
     return SecurityStateModel::DANGEROUS;
+  }
 
   GURL url = visible_security_state.url;
 
@@ -208,9 +211,10 @@
     SecurityStateModel::SecurityInfo* security_info) {
   if (!visible_security_state.connection_info_initialized) {
     *security_info = SecurityStateModel::SecurityInfo();
-    security_info->fails_malware_check =
-        visible_security_state.fails_malware_check;
-    if (security_info->fails_malware_check) {
+    security_info->malicious_content_status =
+        visible_security_state.malicious_content_status;
+    if (security_info->malicious_content_status !=
+        SecurityStateModel::MALICIOUS_CONTENT_STATUS_NONE) {
       security_info->security_level = GetSecurityLevelForRequest(
           visible_security_state, client, SecurityStateModel::UNKNOWN_SHA1,
           SecurityStateModel::CONTENT_STATUS_UNKNOWN,
@@ -239,8 +243,8 @@
   security_info->sct_verify_statuses =
       visible_security_state.sct_verify_statuses;
 
-  security_info->fails_malware_check =
-      visible_security_state.fails_malware_check;
+  security_info->malicious_content_status =
+      visible_security_state.malicious_content_status;
 
   security_info->displayed_password_field_on_http =
       visible_security_state.displayed_password_field_on_http;
@@ -264,7 +268,8 @@
 
 SecurityStateModel::SecurityInfo::SecurityInfo()
     : security_level(SecurityStateModel::NONE),
-      fails_malware_check(false),
+      malicious_content_status(
+          SecurityStateModel::MALICIOUS_CONTENT_STATUS_NONE),
       sha1_deprecation_status(SecurityStateModel::NO_DEPRECATED_SHA1),
       mixed_content_status(SecurityStateModel::CONTENT_STATUS_NONE),
       content_with_cert_errors_status(SecurityStateModel::CONTENT_STATUS_NONE),
@@ -296,7 +301,8 @@
 }
 
 SecurityStateModel::VisibleSecurityState::VisibleSecurityState()
-    : fails_malware_check(false),
+    : malicious_content_status(
+          SecurityStateModel::MALICIOUS_CONTENT_STATUS_NONE),
       connection_info_initialized(false),
       cert_status(0),
       connection_status(0),
@@ -315,7 +321,7 @@
 bool SecurityStateModel::VisibleSecurityState::operator==(
     const SecurityStateModel::VisibleSecurityState& other) const {
   return (url == other.url &&
-          fails_malware_check == other.fails_malware_check &&
+          malicious_content_status == other.malicious_content_status &&
           !!certificate == !!other.certificate &&
           (certificate ? certificate->Equals(other.certificate.get()) : true) &&
           connection_status == other.connection_status &&
diff --git a/components/security_state/security_state_model.h b/components/security_state/security_state_model.h
index b740bb1..741c1d4b 100644
--- a/components/security_state/security_state_model.h
+++ b/components/security_state/security_state_model.h
@@ -94,14 +94,23 @@
     CONTENT_STATUS_DISPLAYED_AND_RAN,
   };
 
+  // Describes whether the page contains malicious resources such as
+  // malware or phishing attacks.
+  enum MaliciousContentStatus {
+    MALICIOUS_CONTENT_STATUS_NONE,
+    MALICIOUS_CONTENT_STATUS_MALWARE,
+    MALICIOUS_CONTENT_STATUS_UNWANTED_SOFTWARE,
+    MALICIOUS_CONTENT_STATUS_SOCIAL_ENGINEERING,
+  };
+
   // Describes the security status of a page or request. This is the
   // main data structure provided by this class.
   struct SecurityInfo {
     SecurityInfo();
     ~SecurityInfo();
     SecurityLevel security_level;
-    // True if the page fails the browser's malware or phishing checks.
-    bool fails_malware_check;
+    // Describes the nature of the page's malicious content, if any.
+    MaliciousContentStatus malicious_content_status;
     SHA1DeprecationStatus sha1_deprecation_status;
     // |mixed_content_status| describes the presence of content that was
     // loaded over a nonsecure (HTTP) connection.
@@ -153,8 +162,7 @@
     bool operator==(const VisibleSecurityState& other) const;
     GURL url;
 
-    // True if the page fails the browser's malware or phishing checks.
-    bool fails_malware_check;
+    MaliciousContentStatus malicious_content_status;
 
     // CONNECTION SECURITY FIELDS
     // Whether the connection security fields are initialized.
diff --git a/components/security_state/security_state_model_unittest.cc b/components/security_state/security_state_model_unittest.cc
index 082b6a08..9ec91c6 100644
--- a/components/security_state/security_state_model_unittest.cc
+++ b/components/security_state/security_state_model_unittest.cc
@@ -34,7 +34,8 @@
         cert_status_(net::CERT_STATUS_SHA1_SIGNATURE_PRESENT),
         displayed_mixed_content_(false),
         ran_mixed_content_(false),
-        fails_malware_check_(false),
+        malicious_content_status_(
+            SecurityStateModel::MALICIOUS_CONTENT_STATUS_NONE),
         displayed_password_field_on_http_(false),
         displayed_credit_card_field_on_http_(false) {
     cert_ =
@@ -57,8 +58,9 @@
   void SetRanMixedContent(bool ran_mixed_content) {
     ran_mixed_content_ = ran_mixed_content;
   }
-  void set_fails_malware_check(bool fails_malware_check) {
-    fails_malware_check_ = fails_malware_check;
+  void set_malicious_content_status(
+      SecurityStateModel::MaliciousContentStatus malicious_content_status) {
+    malicious_content_status_ = malicious_content_status;
   }
   void set_displayed_password_field_on_http(
       bool displayed_password_field_on_http) {
@@ -82,7 +84,7 @@
     state->security_bits = 256;
     state->displayed_mixed_content = displayed_mixed_content_;
     state->ran_mixed_content = ran_mixed_content_;
-    state->fails_malware_check = fails_malware_check_;
+    state->malicious_content_status = malicious_content_status_;
     state->displayed_password_field_on_http = displayed_password_field_on_http_;
     state->displayed_credit_card_field_on_http =
         displayed_credit_card_field_on_http_;
@@ -101,7 +103,7 @@
   net::CertStatus cert_status_;
   bool displayed_mixed_content_;
   bool ran_mixed_content_;
-  bool fails_malware_check_;
+  SecurityStateModel::MaliciousContentStatus malicious_content_status_;
   bool displayed_password_field_on_http_;
   bool displayed_credit_card_field_on_http_;
 };
@@ -218,10 +220,18 @@
   client.set_connection_status(net::SSL_CONNECTION_VERSION_TLS1_2
                                << net::SSL_CONNECTION_VERSION_SHIFT);
   client.SetCipherSuite(ciphersuite);
-  client.set_fails_malware_check(true);
+
   SecurityStateModel::SecurityInfo security_info;
   model.GetSecurityInfo(&security_info);
-  EXPECT_TRUE(security_info.fails_malware_check);
+  EXPECT_EQ(SecurityStateModel::MALICIOUS_CONTENT_STATUS_NONE,
+            security_info.malicious_content_status);
+
+  client.set_malicious_content_status(
+      SecurityStateModel::MALICIOUS_CONTENT_STATUS_MALWARE);
+  model.GetSecurityInfo(&security_info);
+
+  EXPECT_EQ(SecurityStateModel::MALICIOUS_CONTENT_STATUS_MALWARE,
+            security_info.malicious_content_status);
   EXPECT_EQ(SecurityStateModel::DANGEROUS, security_info.security_level);
 }
 
@@ -231,10 +241,12 @@
   TestSecurityStateModelClient client;
   SecurityStateModel model;
   model.SetClient(&client);
-  client.set_fails_malware_check(true);
+  client.set_malicious_content_status(
+      SecurityStateModel::MALICIOUS_CONTENT_STATUS_SOCIAL_ENGINEERING);
   SecurityStateModel::SecurityInfo security_info;
   model.GetSecurityInfo(&security_info);
-  EXPECT_TRUE(security_info.fails_malware_check);
+  EXPECT_EQ(SecurityStateModel::MALICIOUS_CONTENT_STATUS_SOCIAL_ENGINEERING,
+            security_info.malicious_content_status);
   EXPECT_EQ(SecurityStateModel::DANGEROUS, security_info.security_level);
 }
 
diff --git a/components/ssl_errors/error_classification_unittest.cc b/components/ssl_errors/error_classification_unittest.cc
index 1e0990fa..800ccd7 100644
--- a/components/ssl_errors/error_classification_unittest.cc
+++ b/components/ssl_errors/error_classification_unittest.cc
@@ -358,7 +358,7 @@
   network_time_tracker.SetTimeServerURLForTesting(test_server.GetURL("/"));
   field_trial_test()->SetNetworkQueriesWithVariationsService(
       true, 0.0,
-      network_time::FieldTrialTest::FETCHES_IN_BACKGROUND_AND_ON_DEMAND);
+      network_time::NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND);
 
   // No sync attempt.
   EXPECT_EQ(
diff --git a/components/test_runner/OWNERS b/components/test_runner/OWNERS
index 8bbffa23..a8a2075 100644
--- a/components/test_runner/OWNERS
+++ b/components/test_runner/OWNERS
@@ -1,4 +1,3 @@
-abarth@chromium.org
 dmazzoni@chromium.org
 dpranke@chromium.org
 mkwst@chromium.org
diff --git a/components/test_runner/event_sender.cc b/components/test_runner/event_sender.cc
index f56555d..a45154a 100644
--- a/components/test_runner/event_sender.cc
+++ b/components/test_runner/event_sender.cc
@@ -31,6 +31,7 @@
 #include "third_party/WebKit/public/platform/WebString.h"
 #include "third_party/WebKit/public/platform/WebVector.h"
 #include "third_party/WebKit/public/web/WebContextMenuData.h"
+#include "third_party/WebKit/public/web/WebFrameWidget.h"
 #include "third_party/WebKit/public/web/WebKit.h"
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebPagePopup.h"
@@ -1363,7 +1364,7 @@
   WebPoint screen_point(event->globalX, event->globalY);
   current_drag_data_ = drag_data;
   current_drag_effects_allowed_ = mask;
-  current_drag_effect_ = view()->dragTargetDragEnter(
+  current_drag_effect_ = mainFrameWidget()->dragTargetDragEnter(
       drag_data, client_point, screen_point, current_drag_effects_allowed_,
       modifiersWithButtons(
           current_pointer_state_[kRawMousePointerId].modifiers_,
@@ -2037,7 +2038,7 @@
   WebPoint scaled_last_pos(last_pos.x * scale, last_pos.y * scale);
 
   // Provide a drag source.
-  view()->dragTargetDragEnter(
+  mainFrameWidget()->dragTargetDragEnter(
       current_drag_data_, scaled_last_pos, scaled_last_pos,
       current_drag_effects_allowed_, 0);
   // |is_drag_mode_| saves events and then replays them later. We don't
@@ -2691,10 +2692,10 @@
   if (current_drag_effect_) {
     // Specifically pass any keyboard modifiers to the drop method. This allows
     // tests to control the drop type (i.e. copy or move).
-    view()->dragTargetDrop(current_drag_data_, client_point, screen_point,
-                           event->modifiers);
+    mainFrameWidget()->dragTargetDrop(current_drag_data_, client_point,
+                                      screen_point, event->modifiers);
   } else {
-    view()->dragTargetDragLeave();
+    mainFrameWidget()->dragTargetDragLeave();
   }
   current_drag_data_.reset();
   view()->dragSourceEndedAt(client_point, screen_point, current_drag_effect_);
@@ -2717,7 +2718,7 @@
 
   WebPoint client_point(event->x, event->y);
   WebPoint screen_point(event->globalX, event->globalY);
-  blink::WebDragOperation drag_effect = view()->dragTargetDragOver(
+  blink::WebDragOperation drag_effect = mainFrameWidget()->dragTargetDragOver(
       client_point, screen_point, current_drag_effects_allowed_,
       event->modifiers);
 
@@ -2743,7 +2744,7 @@
 
   WebPoint client_point(event->x, event->y);
   WebPoint screen_point(event->globalX, event->globalY);
-  current_drag_effect_ = view()->dragTargetDragOver(
+  current_drag_effect_ = mainFrameWidget()->dragTargetDragOver(
       client_point, screen_point, current_drag_effects_allowed_,
       event->modifiers);
 }
@@ -2896,6 +2897,10 @@
   return web_widget_test_proxy_base_->web_widget();
 }
 
+blink::WebFrameWidget* EventSender::mainFrameWidget() {
+  return view()->mainFrame()->toWebLocalFrame()->frameWidget();
+}
+
 std::unique_ptr<WebInputEvent> EventSender::TransformScreenToWidgetCoordinates(
     const WebInputEvent& event) {
   return delegate()->TransformScreenToWidgetCoordinates(
diff --git a/components/test_runner/event_sender.h b/components/test_runner/event_sender.h
index 156e745..c108011b 100644
--- a/components/test_runner/event_sender.h
+++ b/components/test_runner/event_sender.h
@@ -24,6 +24,7 @@
 #include "third_party/WebKit/public/platform/WebTouchPoint.h"
 
 namespace blink {
+class WebFrameWidget;
 class WebLocalFrame;
 class WebView;
 class WebWidget;
@@ -265,6 +266,7 @@
   const blink::WebView* view() const;
   blink::WebView* view();
   blink::WebWidget* widget();
+  blink::WebFrameWidget* mainFrameWidget();
 
   bool force_layout_on_events_;
 
diff --git a/components/user_manager/fake_user_manager.cc b/components/user_manager/fake_user_manager.cc
index 164e319..f05eb58 100644
--- a/components/user_manager/fake_user_manager.cc
+++ b/components/user_manager/fake_user_manager.cc
@@ -179,14 +179,6 @@
   return nullptr;
 }
 
-const user_manager::User* FakeUserManager::GetLoggedInUser() const {
-  return nullptr;
-}
-
-user_manager::User* FakeUserManager::GetLoggedInUser() {
-  return nullptr;
-}
-
 const user_manager::User* FakeUserManager::GetPrimaryUser() const {
   return primary_user_;
 }
diff --git a/components/user_manager/fake_user_manager.h b/components/user_manager/fake_user_manager.h
index 596152b..8bc6b204 100644
--- a/components/user_manager/fake_user_manager.h
+++ b/components/user_manager/fake_user_manager.h
@@ -71,8 +71,6 @@
   const user_manager::User* FindUser(
       const AccountId& account_id) const override;
   user_manager::User* FindUserAndModify(const AccountId& account_id) override;
-  const user_manager::User* GetLoggedInUser() const override;
-  user_manager::User* GetLoggedInUser() override;
   const user_manager::User* GetPrimaryUser() const override;
   void SaveUserOAuthStatus(
       const AccountId& account_id,
diff --git a/components/user_manager/user_manager.h b/components/user_manager/user_manager.h
index f2a31699..ed4a524 100644
--- a/components/user_manager/user_manager.h
+++ b/components/user_manager/user_manager.h
@@ -195,12 +195,6 @@
   // Same as FindUser but returns non-const pointer to User object.
   virtual User* FindUserAndModify(const AccountId& account_id) = 0;
 
-  // Returns the logged-in user.
-  // TODO(nkostylev): Deprecate this call, move clients to GetActiveUser().
-  // http://crbug.com/230852
-  virtual const User* GetLoggedInUser() const = 0;
-  virtual User* GetLoggedInUser() = 0;
-
   // Returns the logged-in user that is currently active within this session.
   // There could be multiple users logged in at the the same but for now
   // we support only one of them being active.
diff --git a/components/user_manager/user_manager_base.cc b/components/user_manager/user_manager_base.cc
index 3d13494..f58ad5e6 100644
--- a/components/user_manager/user_manager_base.cc
+++ b/components/user_manager/user_manager_base.cc
@@ -323,16 +323,6 @@
   return FindUserInListAndModify(account_id);
 }
 
-const User* UserManagerBase::GetLoggedInUser() const {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
-  return active_user_;
-}
-
-User* UserManagerBase::GetLoggedInUser() {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
-  return active_user_;
-}
-
 const User* UserManagerBase::GetActiveUser() const {
   DCHECK(task_runner_->RunsTasksOnCurrentThread());
   return active_user_;
@@ -532,7 +522,7 @@
 bool UserManagerBase::IsCurrentUserNonCryptohomeDataEphemeral() const {
   DCHECK(task_runner_->RunsTasksOnCurrentThread());
   return IsUserLoggedIn() &&
-         IsUserNonCryptohomeDataEphemeral(GetLoggedInUser()->GetAccountId());
+         IsUserNonCryptohomeDataEphemeral(GetActiveUser()->GetAccountId());
 }
 
 bool UserManagerBase::IsCurrentUserCryptohomeDataEphemeral() const {
@@ -610,7 +600,7 @@
   //    policy was enabled.
   //    - or -
   // b) The user logged into any other account type.
-  if (IsUserLoggedIn() && (account_id == GetLoggedInUser()->GetAccountId()) &&
+  if (IsUserLoggedIn() && (account_id == GetActiveUser()->GetAccountId()) &&
       (is_current_user_ephemeral_regular_user_ ||
        !IsLoggedInAsUserWithGaiaAccount())) {
     return true;
diff --git a/components/user_manager/user_manager_base.h b/components/user_manager/user_manager_base.h
index 2986d2f..df20612 100644
--- a/components/user_manager/user_manager_base.h
+++ b/components/user_manager/user_manager_base.h
@@ -64,8 +64,6 @@
   bool IsKnownUser(const AccountId& account_id) const override;
   const User* FindUser(const AccountId& account_id) const override;
   User* FindUserAndModify(const AccountId& account_id) override;
-  const User* GetLoggedInUser() const override;
-  User* GetLoggedInUser() override;
   const User* GetActiveUser() const override;
   User* GetActiveUser() override;
   const User* GetPrimaryUser() const override;
diff --git a/components/variations/experiment_labels.cc b/components/variations/experiment_labels.cc
index ef978805..c7cce95 100644
--- a/components/variations/experiment_labels.cc
+++ b/components/variations/experiment_labels.cc
@@ -20,55 +20,8 @@
 
 const char kVariationPrefix[] = "CrVar";
 
-// This method builds a single experiment label for a Chrome Variation,
-// including a timestamp that is a year in the future from |current_time|. Since
-// multiple headers can be transmitted, |count| is a number that is appended
-// after the label key to differentiate the labels.
-base::string16 CreateSingleExperimentLabel(int count,
-                                           variations::VariationID id,
-                                           const base::Time& current_time) {
-  // Build the parts separately so they can be validated.
-  const base::string16 key =
-      base::ASCIIToUTF16(kVariationPrefix) + base::IntToString16(count);
-  DCHECK_LE(key.size(), 8U);
-  const base::string16 value = base::IntToString16(id);
-  DCHECK_LE(value.size(), 8U);
-  base::string16 label(key);
-  label += base::ASCIIToUTF16("=");
-  label += value;
-  label += base::ASCIIToUTF16("|");
-  label += variations::BuildExperimentDateString(current_time);
-  return label;
-}
-
 }  // namespace
 
-base::string16 BuildGoogleUpdateExperimentLabel(
-    const base::FieldTrial::ActiveGroups& active_groups) {
-  base::string16 experiment_labels;
-  int counter = 0;
-
-  const base::Time current_time(base::Time::Now());
-
-  // Find all currently active VariationIDs associated with Google Update.
-  for (base::FieldTrial::ActiveGroups::const_iterator it =
-       active_groups.begin(); it != active_groups.end(); ++it) {
-    const variations::VariationID id =
-        variations::GetGoogleVariationID(variations::GOOGLE_UPDATE_SERVICE,
-                                         it->trial_name, it->group_name);
-
-    if (id == variations::EMPTY_ID)
-      continue;
-
-    if (!experiment_labels.empty())
-      experiment_labels += variations::kExperimentLabelSeparator;
-    experiment_labels += CreateSingleExperimentLabel(++counter, id,
-                                                     current_time);
-  }
-
-  return experiment_labels;
-}
-
 base::string16 ExtractNonVariationLabels(const base::string16& labels) {
   // First, split everything by the label separator.
   std::vector<base::StringPiece16> entries = base::SplitStringPiece(
@@ -94,23 +47,4 @@
   return non_variation_labels;
 }
 
-base::string16 CombineExperimentLabels(const base::string16& variation_labels,
-                                       const base::string16& other_labels) {
-  base::StringPiece16 separator(&variations::kExperimentLabelSeparator, 1);
-  DCHECK(!base::StartsWith(variation_labels, separator,
-                           base::CompareCase::SENSITIVE));
-  DCHECK(!base::EndsWith(variation_labels, separator,
-                         base::CompareCase::SENSITIVE));
-  DCHECK(!base::StartsWith(other_labels, separator,
-                           base::CompareCase::SENSITIVE));
-  DCHECK(!base::EndsWith(other_labels, separator,
-                         base::CompareCase::SENSITIVE));
-  // Note that if either label is empty, a separator is not necessary.
-  base::string16 combined_labels = other_labels;
-  if (!other_labels.empty() && !variation_labels.empty())
-    combined_labels += variations::kExperimentLabelSeparator;
-  combined_labels += variation_labels;
-  return combined_labels;
-}
-
 }  // namespace variations
diff --git a/components/variations/experiment_labels.h b/components/variations/experiment_labels.h
index 9168f70..cd10825 100644
--- a/components/variations/experiment_labels.h
+++ b/components/variations/experiment_labels.h
@@ -10,19 +10,6 @@
 
 namespace variations {
 
-// Takes the list of active groups and builds the label for the ones that have
-// Google Update VariationID associated with them. This will return an empty
-// string if there are no such groups.
-base::string16 BuildGoogleUpdateExperimentLabel(
-    const base::FieldTrial::ActiveGroups& active_groups);
-
-// Creates a final combined experiment labels string with |variation_labels|
-// and |other_labels|, appropriately appending a separator based on their
-// contents. It is assumed that |variation_labels| and |other_labels| do not
-// have leading or trailing separators.
-base::string16 CombineExperimentLabels(const base::string16& variation_labels,
-                                       const base::string16& other_labels);
-
 // Takes the value of experiment_labels from the registry and returns a valid
 // experiment_labels string value containing only the labels that are not
 // associated with Chrome Variations.
diff --git a/components/variations/experiment_labels_unittest.cc b/components/variations/experiment_labels_unittest.cc
index 8df2826..76e177d 100644
--- a/components/variations/experiment_labels_unittest.cc
+++ b/components/variations/experiment_labels_unittest.cc
@@ -19,170 +19,60 @@
 
 namespace variations {
 
-TEST(ExperimentLabelsTest, BuildGoogleUpdateExperimentLabel) {
-  const variations::VariationID TEST_VALUE_A = 3300200;
-  const variations::VariationID TEST_VALUE_B = 3300201;
-  const variations::VariationID TEST_VALUE_C = 3300202;
-  const variations::VariationID TEST_VALUE_D = 3300203;
-
-  struct {
-    const char* active_group_pairs;
-    const char* expected_ids;
-  } test_cases[] = {
-    // Empty group.
-    {"", ""},
-    // Group of 1.
-    {"FieldTrialA#Default", "3300200"},
-    // Group of 1, doesn't have an associated ID.
-    {"FieldTrialA#DoesNotExist", ""},
-    // Group of 3.
-    {"FieldTrialA#Default#FieldTrialB#Group1#FieldTrialC#Default",
-     "3300200#3300201#3300202"},
-    // Group of 3, one doesn't have an associated ID.
-    {"FieldTrialA#Default#FieldTrialB#DoesNotExist#FieldTrialC#Default",
-     "3300200#3300202"},
-    // Group of 3, all three don't have an associated ID.
-    {"FieldTrialX#Default#FieldTrialB#DoesNotExist#FieldTrialC#Default",
-     "3300202"},
-  };
-
-  // Register a few VariationIDs.
-  AssociateGoogleVariationID(variations::GOOGLE_UPDATE_SERVICE, "FieldTrialA",
-                             "Default", TEST_VALUE_A);
-  AssociateGoogleVariationID(variations::GOOGLE_UPDATE_SERVICE, "FieldTrialB",
-                             "Group1", TEST_VALUE_B);
-  AssociateGoogleVariationID(variations::GOOGLE_UPDATE_SERVICE, "FieldTrialC",
-                             "Default", TEST_VALUE_C);
-  AssociateGoogleVariationID(variations::GOOGLE_UPDATE_SERVICE, "FieldTrialD",
-                             "Default", TEST_VALUE_D);  // Not actually used.
-
-  for (size_t i = 0; i < arraysize(test_cases); ++i) {
-    // Parse the input groups.
-    base::FieldTrial::ActiveGroups groups;
-    std::vector<std::string> group_data = base::SplitString(
-        test_cases[i].active_group_pairs, "#",
-        base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-    ASSERT_EQ(0U, group_data.size() % 2);
-    for (size_t j = 0; j < group_data.size(); j += 2) {
-      base::FieldTrial::ActiveGroup group;
-      group.trial_name = group_data[j];
-      group.group_name = group_data[j + 1];
-      groups.push_back(group);
-    }
-
-    // Parse the expected output.
-    std::vector<std::string> expected_ids_list = base::SplitString(
-        test_cases[i].expected_ids, "#",
-        base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-
-    std::string experiment_labels_string = base::UTF16ToUTF8(
-        BuildGoogleUpdateExperimentLabel(groups));
-
-    // Split the VariationIDs from the labels for verification below.
-    std::set<std::string> parsed_ids;
-    for (const std::string& label : base::SplitString(
-             experiment_labels_string, ";",
-             base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
-      // The ID is precisely between the '=' and '|' characters in each label.
-      size_t index_of_equals = label.find('=');
-      size_t index_of_pipe = label.find('|');
-      ASSERT_NE(std::string::npos, index_of_equals);
-      ASSERT_NE(std::string::npos, index_of_pipe);
-      ASSERT_GT(index_of_pipe, index_of_equals);
-      parsed_ids.insert(label.substr(index_of_equals + 1,
-                                     index_of_pipe - index_of_equals - 1));
-    }
-
-    // Verify that the resulting string contains each of the expected labels,
-    // and nothing more. Note that the date is stripped out and ignored.
-    for (std::vector<std::string>::const_iterator it =
-             expected_ids_list.begin(); it != expected_ids_list.end(); ++it) {
-      std::set<std::string>::iterator it2 = parsed_ids.find(*it);
-      EXPECT_TRUE(parsed_ids.end() != it2);
-      parsed_ids.erase(it2);
-    }
-    EXPECT_TRUE(parsed_ids.empty());
-  }  // for
-}
-
-TEST(ExperimentLabelsTest, CombineExperimentLabels) {
-  struct {
-    const char* variations_labels;
-    const char* other_labels;
-    const char* expected_label;
-  } test_cases[] = {
-    {"A=B|Tue, 21 Jan 2014 15:30:21 GMT",
-     "C=D|Tue, 21 Jan 2014 15:30:21 GMT",
-     "C=D|Tue, 21 Jan 2014 15:30:21 GMT;A=B|Tue, 21 Jan 2014 15:30:21 GMT"},
-    {"A=B|Tue, 21 Jan 2014 15:30:21 GMT",
-     "",
-     "A=B|Tue, 21 Jan 2014 15:30:21 GMT"},
-    {"",
-     "A=B|Tue, 21 Jan 2014 15:30:21 GMT",
-     "A=B|Tue, 21 Jan 2014 15:30:21 GMT"},
-    {"A=B|Tue, 21 Jan 2014 15:30:21 GMT;C=D|Tue, 21 Jan 2014 15:30:21 GMT",
-     "P=Q|Tue, 21 Jan 2014 15:30:21 GMT;X=Y|Tue, 21 Jan 2014 15:30:21 GMT",
-     "P=Q|Tue, 21 Jan 2014 15:30:21 GMT;X=Y|Tue, 21 Jan 2014 15:30:21 GMT;"
-     "A=B|Tue, 21 Jan 2014 15:30:21 GMT;C=D|Tue, 21 Jan 2014 15:30:21 GMT"},
-    {"",
-     "",
-     ""},
-  };
-
-  for (size_t i = 0; i < arraysize(test_cases); ++i) {
-    std::string result = base::UTF16ToUTF8(CombineExperimentLabels(
-        base::ASCIIToUTF16(test_cases[i].variations_labels),
-        base::ASCIIToUTF16(test_cases[i].other_labels)));
-    EXPECT_EQ(test_cases[i].expected_label, result);
-  }
-}
-
 TEST(ExperimentLabelsTest, ExtractNonVariationLabels) {
   struct {
     const char* input_label;
     const char* expected_output;
   } test_cases[] = {
-    // Empty
-    {"", ""},
-    // One
-    {"gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT",
-     "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
-    // Three
-    {"CrVar1=123|Tue, 21 Jan 2014 15:30:21 GMT;"
-     "experiment1=456|Tue, 21 Jan 2014 15:30:21 GMT;"
-     "experiment2=789|Tue, 21 Jan 2014 15:30:21 GMT;"
-     "CrVar1=123|Tue, 21 Jan 2014 15:30:21 GMT",
-     "experiment1=456|Tue, 21 Jan 2014 15:30:21 GMT;"
-     "experiment2=789|Tue, 21 Jan 2014 15:30:21 GMT"},
-    // One and one Variation
-    {"gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT;"
-     "CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT",
-     "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
-    // One and one Variation, flipped
-    {"CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT;"
-     "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT",
-     "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
-    // Sandwiched
-    {"CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT;"
-     "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT;"
-     "CrVar2=3310003|Tue, 21 Jan 2014 15:30:21 GMT;"
-     "CrVar3=3310004|Tue, 21 Jan 2014 15:30:21 GMT",
-     "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
-    // Only Variations
-    {"CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT;"
-     "CrVar2=3310003|Tue, 21 Jan 2014 15:30:21 GMT;"
-     "CrVar3=3310004|Tue, 21 Jan 2014 15:30:21 GMT",
-     ""},
-    // Empty values
-    {"gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT;"
-     "CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT",
-     "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
-    // Trailing semicolon
-    {"gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT;"
-     "CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT;",  // Note the semi here.
-     "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
-    // Semis
-    {";;;;", ""},
+      // Empty
+      {"", ""},
+      // One
+      {"gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT",
+       "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
+      // Three
+      {"CrVar1=123|Tue, 21 Jan 2014 15:30:21 GMT;"
+       "experiment1=456|Tue, 21 Jan 2014 15:30:21 GMT;"
+       "experiment2=789|Tue, 21 Jan 2014 15:30:21 GMT;"
+       "CrVar1=123|Tue, 21 Jan 2014 15:30:21 GMT",
+       "experiment1=456|Tue, 21 Jan 2014 15:30:21 GMT;"
+       "experiment2=789|Tue, 21 Jan 2014 15:30:21 GMT"},
+      // One and one Variation
+      {"gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT;"
+       "CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT",
+       "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
+      // One and one Variation, flipped
+      {"CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT;"
+       "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT",
+       "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
+      // Sandwiched
+      {"CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT;"
+       "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT;"
+       "CrVar2=3310003|Tue, 21 Jan 2014 15:30:21 GMT;"
+       "CrVar3=3310004|Tue, 21 Jan 2014 15:30:21 GMT",
+       "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
+      // Only Variations
+      {"CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT;"
+       "CrVar2=3310003|Tue, 21 Jan 2014 15:30:21 GMT;"
+       "CrVar3=3310004|Tue, 21 Jan 2014 15:30:21 GMT",
+       ""},
+      // Empty values
+      {"gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT;"
+       "CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT",
+       "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
+      // Trailing semicolon
+      {"gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT;"
+       "CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT;",  // Note the semi here.
+       "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
+      // Semis
+      {";;;;", ""},
+      // Three non-Variation labels
+      // Testing that the order is preserved.
+      {"experiment1=456|Tue, 21 Jan 2014 15:30:21 GMT;"
+       "experiment2=789|Tue, 21 Jan 2014 15:30:21 GMT;"
+       "experiment3=123|Tue, 21 Jan 2014 15:30:21 GMT",
+       "experiment1=456|Tue, 21 Jan 2014 15:30:21 GMT;"
+       "experiment2=789|Tue, 21 Jan 2014 15:30:21 GMT;"
+       "experiment3=123|Tue, 21 Jan 2014 15:30:21 GMT"},
   };
 
   for (size_t i = 0; i < arraysize(test_cases); ++i) {
diff --git a/components/variations/proto/study.proto b/components/variations/proto/study.proto
index f2bae5b..d880a007 100644
--- a/components/variations/proto/study.proto
+++ b/components/variations/proto/study.proto
@@ -73,9 +73,6 @@
     // behavior on Google web properties.
     optional uint64 google_web_trigger_experiment_id = 8;
 
-    // Optional id used to uniquely identify this experiment for Google Update.
-    optional uint64 google_update_experiment_id = 4;
-
     // Optional id used to uniquely identify this experiment for Chrome Sync.
     optional uint64 chrome_sync_experiment_id = 10;
 
@@ -201,7 +198,7 @@
   // Filtering criteria specifying whether this study is applicable to a given
   // Chrome instance.
   //
-  // Next tag: 12
+  // Next tag: 13
   message Filter {
     // The start date of the study in Unix time format. (Seconds since midnight
     // January 1, 1970 UTC). See: http://en.wikipedia.org/wiki/Unix_time
@@ -233,10 +230,17 @@
     repeated Platform platform = 5;
 
     // List of locales that will receive this study. If omitted, the study
-    // applies to all locales.
+    // applies to all locales, unless |exclude_locale| is specified. Mutually
+    // exclusive with |exclude_locale|.
     // Ex: ["en-US", "en-CA"]
     repeated string locale = 6;
 
+    // List of locales that will be excluded from this study. If omitted, the
+    // study applies to all locales unless |locale| is specified. Mutually
+    // exclusive with |locale|.
+    // Ex: ["en-US", "en-CA"]
+    repeated string exclude_locale = 12;
+
     // List of form factors that will receive this study. If omitted, the study
     // applies to all form factors.
     // Ex: [PHONE, TABLET]
@@ -268,7 +272,7 @@
     repeated string country = 10;
 
     // List of lowercase ISO 3166-1 alpha-2 country codes that will be excluded
-    // in this study. If omitted, the study applies to all countries unless
+    // from this study. If omitted, the study applies to all countries unless
     // |country| is specified. Mutually exclusive with |country|.
     // Ex: ["in", "us"]
     repeated string exclude_country = 11;
diff --git a/components/variations/study_filtering.cc b/components/variations/study_filtering.cc
index 479b9e9..159b544 100644
--- a/components/variations/study_filtering.cc
+++ b/components/variations/study_filtering.cc
@@ -106,15 +106,18 @@
 }
 
 bool CheckStudyLocale(const Study_Filter& filter, const std::string& locale) {
-  // An empty locale list matches all locales.
-  if (filter.locale_size() == 0)
+  // Empty locale and exclude_locale lists matches all locales.
+  if (filter.locale_size() == 0 && filter.exclude_locale_size() == 0)
     return true;
 
-  for (int i = 0; i < filter.locale_size(); ++i) {
-    if (filter.locale(i) == locale)
-      return true;
-  }
-  return false;
+  // Check if we are supposed to filter for a specified set of countries. Note
+  // that this means this overrides the exclude_locale in case that ever occurs
+  // (which it shouldn't).
+  if (filter.locale_size() > 0)
+    return base::ContainsValue(filter.locale(), locale);
+
+  // Omit if matches any of the exclude entries.
+  return !base::ContainsValue(filter.exclude_locale(), locale);
 }
 
 bool CheckStudyPlatform(const Study_Filter& filter, Study_Platform platform) {
diff --git a/components/variations/study_filtering_unittest.cc b/components/variations/study_filtering_unittest.cc
index 0c9dfa06..d8008cf2 100644
--- a/components/variations/study_filtering_unittest.cc
+++ b/components/variations/study_filtering_unittest.cc
@@ -130,17 +130,27 @@
 TEST(VariationsStudyFilteringTest, CheckStudyLocale) {
   struct {
     const char* filter_locales;
+    const char* exclude_locales;
     bool en_us_result;
     bool en_ca_result;
     bool fr_result;
   } test_cases[] = {
-    {"en-US", true, false, false},
-    {"en-US,en-CA,fr", true, true, true},
-    {"en-US,en-CA,en-GB", true, true, false},
-    {"en-GB,en-CA,en-US", true, true, false},
-    {"ja,kr,vi", false, false, false},
-    {"fr-CA", false, false, false},
-    {"", true, true, true},
+      {"en-US", "", true, false, false},
+      // Tests that locale overrides exclude_locale, when both are given. This
+      // should not occur in practice though.
+      {"en-US", "en-US", true, false, false},
+      {"en-US,en-CA,fr", "", true, true, true},
+      {"en-US,en-CA,en-GB", "", true, true, false},
+      {"en-GB,en-CA,en-US", "", true, true, false},
+      {"ja,kr,vi", "", false, false, false},
+      {"fr-CA", "", false, false, false},
+      {"", "", true, true, true},
+      {"", "en-US", false, true, true},
+      {"", "en-US,en-CA,fr", false, false, false},
+      {"", "en-US,en-CA,en-GB", false, false, true},
+      {"", "en-GB,en-CA,en-US", false, false, true},
+      {"", "ja,kr,vi", true, true, true},
+      {"", "fr-CA", true, true, true},
   };
 
   for (size_t i = 0; i < arraysize(test_cases); ++i) {
@@ -149,6 +159,10 @@
              test_cases[i].filter_locales, ",",
              base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL))
       filter.add_locale(locale);
+    for (const std::string& exclude_locale :
+         base::SplitString(test_cases[i].exclude_locales, ",",
+                           base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL))
+      filter.add_exclude_locale(exclude_locale);
     EXPECT_EQ(test_cases[i].en_us_result,
               internal::CheckStudyLocale(filter, "en-US"));
     EXPECT_EQ(test_cases[i].en_ca_result,
diff --git a/components/variations/variations_associated_data.cc b/components/variations/variations_associated_data.cc
index f9e77849..ec12245 100644
--- a/components/variations/variations_associated_data.cc
+++ b/components/variations/variations_associated_data.cc
@@ -39,7 +39,7 @@
                    const VariationID id,
                    const bool force) {
 #if !defined(NDEBUG)
-    DCHECK_EQ(4, ID_COLLECTION_COUNT);
+    DCHECK_EQ(3, ID_COLLECTION_COUNT);
     // Ensure that at most one of the trigger/non-trigger web property IDs are
     // set.
     if (key == GOOGLE_WEB_PROPERTIES || key == GOOGLE_WEB_PROPERTIES_TRIGGER) {
diff --git a/components/variations/variations_associated_data.h b/components/variations/variations_associated_data.h
index a4156c2..0579781 100644
--- a/components/variations/variations_associated_data.h
+++ b/components/variations/variations_associated_data.h
@@ -64,9 +64,6 @@
   // server side experimental behavior, transmitted through the
   // X-Client-Data header.
   GOOGLE_WEB_PROPERTIES_TRIGGER,
-  // This collection is used by Google update services, transmitted through the
-  // Google Update experiment labels.
-  GOOGLE_UPDATE_SERVICE,
   // This collection is used by Chrome Sync services, transmitted through the
   // Chrome Sync experiment labels.
   CHROME_SYNC_SERVICE,
diff --git a/components/variations/variations_associated_data_unittest.cc b/components/variations/variations_associated_data_unittest.cc
index 5bdd1d84..40ff746 100644
--- a/components/variations/variations_associated_data_unittest.cc
+++ b/components/variations/variations_associated_data_unittest.cc
@@ -180,8 +180,6 @@
   EXPECT_EQ(EMPTY_ID,
             GetIDForTrial(GOOGLE_WEB_PROPERTIES_TRIGGER, trial_true.get()));
   EXPECT_EQ(EMPTY_ID,
-            GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get()));
-  EXPECT_EQ(EMPTY_ID,
             GetIDForTrial(CHROME_SYNC_SERVICE, trial_true.get()));
 
   AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_true->trial_name(),
@@ -189,17 +187,6 @@
   EXPECT_EQ(TEST_VALUE_A,
             GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get()));
   EXPECT_EQ(EMPTY_ID,
-            GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get()));
-  EXPECT_EQ(EMPTY_ID,
-            GetIDForTrial(CHROME_SYNC_SERVICE, trial_true.get()));
-
-  AssociateGoogleVariationID(GOOGLE_UPDATE_SERVICE, trial_true->trial_name(),
-      default_name, TEST_VALUE_A);
-  EXPECT_EQ(TEST_VALUE_A,
-            GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get()));
-  EXPECT_EQ(TEST_VALUE_A,
-            GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get()));
-  EXPECT_EQ(EMPTY_ID,
             GetIDForTrial(CHROME_SYNC_SERVICE, trial_true.get()));
 
   AssociateGoogleVariationID(CHROME_SYNC_SERVICE, trial_true->trial_name(),
@@ -207,8 +194,6 @@
   EXPECT_EQ(TEST_VALUE_A,
             GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get()));
   EXPECT_EQ(TEST_VALUE_A,
-            GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get()));
-  EXPECT_EQ(TEST_VALUE_A,
             GetIDForTrial(CHROME_SYNC_SERVICE, trial_true.get()));
 
   trial_true = CreateFieldTrial("d2", 10, default_name, &default_group_number);
@@ -221,17 +206,6 @@
   EXPECT_EQ(TEST_VALUE_A,
             GetIDForTrial(GOOGLE_WEB_PROPERTIES_TRIGGER, trial_true.get()));
   EXPECT_EQ(EMPTY_ID,
-            GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get()));
-  EXPECT_EQ(EMPTY_ID,
-            GetIDForTrial(CHROME_SYNC_SERVICE, trial_true.get()));
-
-  AssociateGoogleVariationID(GOOGLE_UPDATE_SERVICE, trial_true->trial_name(),
-                             default_name, TEST_VALUE_A);
-  EXPECT_EQ(TEST_VALUE_A,
-            GetIDForTrial(GOOGLE_WEB_PROPERTIES_TRIGGER, trial_true.get()));
-  EXPECT_EQ(TEST_VALUE_A,
-            GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get()));
-  EXPECT_EQ(EMPTY_ID,
             GetIDForTrial(CHROME_SYNC_SERVICE, trial_true.get()));
 
   AssociateGoogleVariationID(CHROME_SYNC_SERVICE, trial_true->trial_name(),
@@ -239,8 +213,6 @@
   EXPECT_EQ(TEST_VALUE_A,
             GetIDForTrial(GOOGLE_WEB_PROPERTIES_TRIGGER, trial_true.get()));
   EXPECT_EQ(TEST_VALUE_A,
-            GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get()));
-  EXPECT_EQ(TEST_VALUE_A,
             GetIDForTrial(CHROME_SYNC_SERVICE, trial_true.get()));
 }
 
diff --git a/components/variations/variations_seed_processor.cc b/components/variations/variations_seed_processor.cc
index b152ddb..50c5cdc 100644
--- a/components/variations/variations_seed_processor.cc
+++ b/components/variations/variations_seed_processor.cc
@@ -54,14 +54,6 @@
                                     experiment.name(),
                                     variation_id);
   }
-  if (experiment.has_google_update_experiment_id()) {
-    const VariationID variation_id =
-        static_cast<VariationID>(experiment.google_update_experiment_id());
-    AssociateGoogleVariationIDForce(GOOGLE_UPDATE_SERVICE,
-                                    trial_name,
-                                    experiment.name(),
-                                    variation_id);
-  }
   if (experiment.has_chrome_sync_experiment_id()) {
     const VariationID variation_id =
         static_cast<VariationID>(experiment.chrome_sync_experiment_id());
@@ -206,7 +198,6 @@
     const Study_Experiment& experiment = study.experiment(i);
     if (experiment.has_google_web_experiment_id() ||
         experiment.has_google_web_trigger_experiment_id() ||
-        experiment.has_google_update_experiment_id() ||
         experiment.has_chrome_sync_experiment_id()) {
       return true;
     }
diff --git a/components/wallpaper/wallpaper_manager_base.cc b/components/wallpaper/wallpaper_manager_base.cc
index 9e2d2331..38549ab 100644
--- a/components/wallpaper/wallpaper_manager_base.cc
+++ b/components/wallpaper/wallpaper_manager_base.cc
@@ -327,7 +327,7 @@
       return;
   }
   SetUserWallpaperNow(
-      user_manager::UserManager::Get()->GetLoggedInUser()->GetAccountId());
+      user_manager::UserManager::Get()->GetActiveUser()->GetAccountId());
 }
 
 void WallpaperManagerBase::ClearDisposableWallpaperCache() {
@@ -368,8 +368,7 @@
   }
 
   return GetUserWallpaperInfo(
-      user_manager::UserManager::Get()->GetLoggedInUser()->GetAccountId(),
-      info);
+      user_manager::UserManager::Get()->GetActiveUser()->GetAccountId(), info);
 }
 
 // static
@@ -872,7 +871,7 @@
 void WallpaperManagerBase::MoveLoggedInUserCustomWallpaper() {
   DCHECK(thread_checker_.CalledOnValidThread());
   const user_manager::User* logged_in_user =
-      user_manager::UserManager::Get()->GetLoggedInUser();
+      user_manager::UserManager::Get()->GetActiveUser();
   if (logged_in_user) {
     task_runner_->PostTask(
         FROM_HERE,
diff --git a/components/web_contents_delegate_android/web_contents_delegate_android.cc b/components/web_contents_delegate_android/web_contents_delegate_android.cc
index 470d829..7cad0da 100644
--- a/components/web_contents_delegate_android/web_contents_delegate_android.cc
+++ b/components/web_contents_delegate_android/web_contents_delegate_android.cc
@@ -251,7 +251,7 @@
   // Do nothing.
 }
 
-bool WebContentsDelegateAndroid::AddMessageToConsole(
+bool WebContentsDelegateAndroid::DidAddMessageToConsole(
     WebContents* source,
     int32_t level,
     const base::string16& message,
@@ -260,8 +260,8 @@
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
   if (obj.is_null())
-    return WebContentsDelegate::AddMessageToConsole(source, level, message,
-                                                    line_no, source_id);
+    return WebContentsDelegate::DidAddMessageToConsole(source, level, message,
+                                                       line_no, source_id);
   ScopedJavaLocalRef<jstring> jmessage(ConvertUTF16ToJavaString(env, message));
   ScopedJavaLocalRef<jstring> jsource_id(
       ConvertUTF16ToJavaString(env, source_id));
diff --git a/components/web_contents_delegate_android/web_contents_delegate_android.h b/components/web_contents_delegate_android/web_contents_delegate_android.h
index afedee53..f25c48f 100644
--- a/components/web_contents_delegate_android/web_contents_delegate_android.h
+++ b/components/web_contents_delegate_android/web_contents_delegate_android.h
@@ -93,11 +93,11 @@
   void CloseContents(content::WebContents* source) override;
   void MoveContents(content::WebContents* source,
                     const gfx::Rect& pos) override;
-  bool AddMessageToConsole(content::WebContents* source,
-                           int32_t level,
-                           const base::string16& message,
-                           int32_t line_no,
-                           const base::string16& source_id) override;
+  bool DidAddMessageToConsole(content::WebContents* source,
+                              int32_t level,
+                              const base::string16& message,
+                              int32_t line_no,
+                              const base::string16& source_id) override;
   void UpdateTargetURL(content::WebContents* source, const GURL& url) override;
   void HandleKeyboardEvent(
       content::WebContents* source,
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index af4cca1..0248825 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -6,6 +6,7 @@
 import("//build/config/ui.gni")
 import("//media/media_options.gni")
 import("//printing/features/features.gni")
+import("//third_party/WebKit/public/public_features.gni")
 import("//tools/ipc_fuzzer/ipc_fuzzer.gni")
 
 source_set("browser") {
@@ -113,6 +114,7 @@
     "//storage/browser",
     "//storage/common",
     "//third_party/WebKit/public:blink_headers",
+    "//third_party/WebKit/public:features",
     "//third_party/WebKit/public:image_resources",
     "//third_party/WebKit/public:mojo_bindings",
     "//third_party/WebKit/public:offscreen_canvas_mojo_bindings",
@@ -1942,7 +1944,6 @@
       "hyphenation/hyphenation_impl.cc",
       "hyphenation/hyphenation_impl.h",
     ]
-    defines += [ "USE_MINIKIN_HYPHENATION=1" ]
   }
 }
 
diff --git a/content/browser/DEPS b/content/browser/DEPS
index 9e6ee17..dd5d7b9e 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -97,6 +97,7 @@
   "+third_party/WebKit/public/platform/modules/serviceworker/service_worker_event_status.mojom.h",
   "+third_party/WebKit/public/platform/modules/vr/WebVR.h",
   "+third_party/WebKit/public/platform/modules/websockets/websocket.mojom.h",
+  "+third_party/WebKit/public/public_features.h",
   "+third_party/WebKit/public/web/WebAXEnums.h",
   "+third_party/WebKit/public/web/WebCompositionUnderline.h",
   "+third_party/WebKit/public/web/WebConsoleMessage.h",
diff --git a/content/browser/browser_plugin/browser_plugin_guest.cc b/content/browser/browser_plugin/browser_plugin_guest.cc
index 461081dc..1d6931d0 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.cc
+++ b/content/browser/browser_plugin/browser_plugin_guest.cc
@@ -821,28 +821,31 @@
   RenderViewHost* host = GetWebContents()->GetRenderViewHost();
   auto* embedder = owner_web_contents_->GetBrowserPluginEmbedder();
   DropData filtered_data(drop_data);
-  host->FilterDropData(&filtered_data);
+  // TODO(paulmeyer): This will need to target the correct specific
+  // RenderWidgetHost to work with OOPIFs. See crbug.com/647249.
+  RenderWidgetHost* widget = host->GetWidget();
+  widget->FilterDropData(&filtered_data);
   switch (drag_status) {
     case blink::WebDragStatusEnter:
-      host->DragTargetDragEnter(filtered_data, location, location, mask,
-                                drop_data.key_modifiers);
+      widget->DragTargetDragEnter(filtered_data, location, location, mask,
+                                  drop_data.key_modifiers);
       // Only track the URL being dragged over the guest if the link isn't
       // coming from the guest.
       if (!embedder->DragEnteredGuest(this))
         ignore_dragged_url_ = false;
       break;
     case blink::WebDragStatusOver:
-      host->DragTargetDragOver(location, location, mask,
-                               drop_data.key_modifiers);
+      widget->DragTargetDragOver(location, location, mask,
+                                 drop_data.key_modifiers);
       break;
     case blink::WebDragStatusLeave:
       embedder->DragLeftGuest(this);
-      host->DragTargetDragLeave();
+      widget->DragTargetDragLeave();
       ignore_dragged_url_ = true;
       break;
     case blink::WebDragStatusDrop:
-      host->DragTargetDrop(filtered_data, location, location,
-                           drop_data.key_modifiers);
+      widget->DragTargetDrop(filtered_data, location, location,
+                             drop_data.key_modifiers);
 
       if (!ignore_dragged_url_ && filtered_data.url.is_valid())
         delegate_->DidDropLink(filtered_data.url);
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index 301990e..2078d888 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -151,11 +151,11 @@
 
  protected:
   // WebContentsDelegate method:
-  bool AddMessageToConsole(WebContents* source,
-                           int32_t level,
-                           const base::string16& message,
-                           int32_t line_no,
-                           const base::string16& source_id) override {
+  bool DidAddMessageToConsole(WebContents* source,
+                              int32_t level,
+                              const base::string16& message,
+                              int32_t line_no,
+                              const base::string16& source_id) override {
     console_messages_.push_back(base::UTF16ToUTF8(message));
     return true;
   }
diff --git a/content/browser/frame_host/render_frame_host_delegate.cc b/content/browser/frame_host/render_frame_host_delegate.cc
index c42d24a..4dcd5811 100644
--- a/content/browser/frame_host/render_frame_host_delegate.cc
+++ b/content/browser/frame_host/render_frame_host_delegate.cc
@@ -24,7 +24,7 @@
   return GURL::EmptyGURL();
 }
 
-bool RenderFrameHostDelegate::AddMessageToConsole(
+bool RenderFrameHostDelegate::DidAddMessageToConsole(
     int32_t level,
     const base::string16& message,
     int32_t line_no,
diff --git a/content/browser/frame_host/render_frame_host_delegate.h b/content/browser/frame_host/render_frame_host_delegate.h
index 1476824..5298d583 100644
--- a/content/browser/frame_host/render_frame_host_delegate.h
+++ b/content/browser/frame_host/render_frame_host_delegate.h
@@ -66,10 +66,10 @@
   virtual const GURL& GetMainFrameLastCommittedURL() const;
 
   // A message was added to to the console.
-  virtual bool AddMessageToConsole(int32_t level,
-                                   const base::string16& message,
-                                   int32_t line_no,
-                                   const base::string16& source_id);
+  virtual bool DidAddMessageToConsole(int32_t level,
+                                      const base::string16& message,
+                                      int32_t line_no,
+                                      const base::string16& source_id);
 
   // Informs the delegate whenever a RenderFrameHost is created.
   virtual void RenderFrameCreated(RenderFrameHost* render_frame_host) {}
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 2e4df9bf..4bbb482 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -643,7 +643,8 @@
 
   handled = true;
   IPC_BEGIN_MESSAGE_MAP(RenderFrameHostImpl, msg)
-    IPC_MESSAGE_HANDLER(FrameHostMsg_AddMessageToConsole, OnAddMessageToConsole)
+    IPC_MESSAGE_HANDLER(FrameHostMsg_DidAddMessageToConsole,
+                        OnDidAddMessageToConsole)
     IPC_MESSAGE_HANDLER(FrameHostMsg_Detach, OnDetach)
     IPC_MESSAGE_HANDLER(FrameHostMsg_FrameFocused, OnFrameFocused)
     IPC_MESSAGE_HANDLER(FrameHostMsg_DidStartProvisionalLoad,
@@ -954,12 +955,12 @@
   }
 }
 
-void RenderFrameHostImpl::OnAddMessageToConsole(
+void RenderFrameHostImpl::OnDidAddMessageToConsole(
     int32_t level,
     const base::string16& message,
     int32_t line_no,
     const base::string16& source_id) {
-  if (delegate_->AddMessageToConsole(level, message, line_no, source_id))
+  if (delegate_->DidAddMessageToConsole(level, message, line_no, source_id))
     return;
 
   // Pass through log level only on WebUI pages to limit console spew.
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 110a19e..e5e6ce82 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -617,10 +617,10 @@
                            LoadEventForwardingWhilePendingDeletion);
 
   // IPC Message handlers.
-  void OnAddMessageToConsole(int32_t level,
-                             const base::string16& message,
-                             int32_t line_no,
-                             const base::string16& source_id);
+  void OnDidAddMessageToConsole(int32_t level,
+                                const base::string16& message,
+                                int32_t line_no,
+                                const base::string16& source_id);
   void OnDetach();
   void OnFrameFocused();
   void OnOpenURL(const FrameHostMsg_OpenURL_Params& params);
diff --git a/content/browser/manifest/manifest_browsertest.cc b/content/browser/manifest/manifest_browsertest.cc
index 7a9d1079..55d45d66 100644
--- a/content/browser/manifest/manifest_browsertest.cc
+++ b/content/browser/manifest/manifest_browsertest.cc
@@ -35,11 +35,11 @@
         test_(test) {
   }
 
-  bool AddMessageToConsole(WebContents* source,
-                           int32_t level,
-                           const base::string16& message,
-                           int32_t line_no,
-                           const base::string16& source_id) override;
+  bool DidAddMessageToConsole(WebContents* source,
+                              int32_t level,
+                              const base::string16& message,
+                              int32_t line_no,
+                              const base::string16& source_id) override;
 
  private:
   WebContents* web_contents_;
@@ -113,9 +113,9 @@
   DISALLOW_COPY_AND_ASSIGN(ManifestBrowserTest);
 };
 
-// The implementation of AddMessageToConsole isn't inlined because it needs
+// The implementation of DidAddMessageToConsole isn't inlined because it needs
 // to know about |test_|.
-bool MockWebContentsDelegate::AddMessageToConsole(
+bool MockWebContentsDelegate::DidAddMessageToConsole(
     WebContents* source,
     int32_t level,
     const base::string16& message,
diff --git a/content/browser/media/capture/aura_window_capture_machine.cc b/content/browser/media/capture/aura_window_capture_machine.cc
index b0c90da7..561e68a 100644
--- a/content/browser/media/capture/aura_window_capture_machine.cc
+++ b/content/browser/media/capture/aura_window_capture_machine.cc
@@ -211,10 +211,10 @@
   if (oracle_proxy_->ObserveEventAndDecideCapture(
           event, gfx::Rect(), event_time, &frame, &capture_frame_cb)) {
     std::unique_ptr<cc::CopyOutputRequest> request =
-        cc::CopyOutputRequest::CreateRequest(base::Bind(
-            &AuraWindowCaptureMachine::DidCopyOutput,
-            weak_factory_.GetWeakPtr(), frame, event_time, start_time,
-            capture_frame_cb));
+        cc::CopyOutputRequest::CreateRequest(
+            base::Bind(&AuraWindowCaptureMachine::DidCopyOutput,
+                       weak_factory_.GetWeakPtr(), std::move(frame), event_time,
+                       start_time, capture_frame_cb));
     gfx::Rect window_rect = gfx::Rect(desktop_window_->bounds().width(),
                                       desktop_window_->bounds().height());
     request->set_area(window_rect);
@@ -260,7 +260,7 @@
   // If ProcessCopyOutputResponse() failed, it will not run |capture_frame_cb|,
   // so do that now.
   if (!succeeded)
-    capture_frame_cb.Run(video_frame, event_time, false);
+    capture_frame_cb.Run(std::move(video_frame), event_time, false);
 }
 
 bool AuraWindowCaptureMachine::ProcessCopyOutputResponse(
@@ -351,7 +351,7 @@
     base::WeakPtr<AuraWindowCaptureMachine> machine,
     base::TimeTicks event_time,
     const CaptureFrameCallback& capture_frame_cb,
-    const scoped_refptr<media::VideoFrame>& target,
+    scoped_refptr<media::VideoFrame> target,
     std::unique_ptr<cc::SingleReleaseCallback> release_callback,
     bool result) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -369,7 +369,7 @@
     result = false;
   }
 
-  capture_frame_cb.Run(target, event_time, result);
+  capture_frame_cb.Run(std::move(target), event_time, result);
 }
 
 void AuraWindowCaptureMachine::OnWindowBoundsChanged(
diff --git a/content/browser/media/capture/aura_window_capture_machine.h b/content/browser/media/capture/aura_window_capture_machine.h
index ff233439..40344e4 100644
--- a/content/browser/media/capture/aura_window_capture_machine.h
+++ b/content/browser/media/capture/aura_window_capture_machine.h
@@ -103,7 +103,7 @@
       base::WeakPtr<AuraWindowCaptureMachine> machine,
       base::TimeTicks event_time,
       const CaptureFrameCallback& capture_frame_cb,
-      const scoped_refptr<media::VideoFrame>& target,
+      scoped_refptr<media::VideoFrame> target,
       std::unique_ptr<cc::SingleReleaseCallback> release_callback,
       bool result);
 
diff --git a/content/browser/media/capture/desktop_capture_device_aura_unittest.cc b/content/browser/media/capture/desktop_capture_device_aura_unittest.cc
index 6310b06..a5bba004 100644
--- a/content/browser/media/capture/desktop_capture_device_aura_unittest.cc
+++ b/content/browser/media/capture/desktop_capture_device_aura_unittest.cc
@@ -74,7 +74,7 @@
   }
   void OnIncomingCapturedVideoFrame(
       std::unique_ptr<Buffer> buffer,
-      const scoped_refptr<media::VideoFrame>& frame) override {
+      scoped_refptr<media::VideoFrame> frame) override {
     DoOnIncomingCapturedVideoFrame();
   }
   std::unique_ptr<Buffer> ResurrectLastOutputBuffer(
diff --git a/content/browser/media/capture/desktop_capture_device_unittest.cc b/content/browser/media/capture/desktop_capture_device_unittest.cc
index fe7cc67..da791c2e 100644
--- a/content/browser/media/capture/desktop_capture_device_unittest.cc
+++ b/content/browser/media/capture/desktop_capture_device_unittest.cc
@@ -93,7 +93,7 @@
   }
   void OnIncomingCapturedVideoFrame(
       std::unique_ptr<Buffer> buffer,
-      const scoped_refptr<media::VideoFrame>& frame) override {
+      scoped_refptr<media::VideoFrame> frame) override {
     DoOnIncomingCapturedVideoFrame();
   }
   std::unique_ptr<Buffer> ResurrectLastOutputBuffer(
diff --git a/content/browser/media/capture/screen_capture_device_android_unittest.cc b/content/browser/media/capture/screen_capture_device_android_unittest.cc
index 3d7be7e3..f903f19f 100644
--- a/content/browser/media/capture/screen_capture_device_android_unittest.cc
+++ b/content/browser/media/capture/screen_capture_device_android_unittest.cc
@@ -50,7 +50,7 @@
   }
   void OnIncomingCapturedVideoFrame(
       std::unique_ptr<Buffer> buffer,
-      const scoped_refptr<media::VideoFrame>& frame) override {
+      scoped_refptr<media::VideoFrame> frame) override {
     DoOnIncomingCapturedVideoFrame();
   }
   std::unique_ptr<Buffer> ResurrectLastOutputBuffer(
diff --git a/content/browser/media/capture/web_contents_video_capture_device.cc b/content/browser/media/capture/web_contents_video_capture_device.cc
index 1458199..5212e7c 100644
--- a/content/browser/media/capture/web_contents_video_capture_device.cc
+++ b/content/browser/media/capture/web_contents_video_capture_device.cc
@@ -136,7 +136,7 @@
       base::WeakPtr<FrameSubscriber> frame_subscriber_,
       const media::ThreadSafeCaptureOracle::CaptureFrameCallback&
           capture_frame_cb,
-      const scoped_refptr<media::VideoFrame>& frame,
+      scoped_refptr<media::VideoFrame> frame,
       base::TimeTicks timestamp,
       const gfx::Rect& region_in_frame,
       bool success);
@@ -358,7 +358,7 @@
     base::WeakPtr<FrameSubscriber> frame_subscriber_,
     const media::ThreadSafeCaptureOracle::CaptureFrameCallback&
         capture_frame_cb,
-    const scoped_refptr<media::VideoFrame>& frame,
+    scoped_refptr<media::VideoFrame> frame,
     base::TimeTicks timestamp,
     const gfx::Rect& region_in_frame,
     bool success) {
@@ -380,7 +380,7 @@
           frame_subscriber_->IsUserInteractingWithContent());
     }
   }
-  capture_frame_cb.Run(frame, timestamp, success);
+  capture_frame_cb.Run(std::move(frame), timestamp, success);
 }
 
 bool FrameSubscriber::IsUserInteractingWithContent() {
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 a905d9ac..ffdfc17 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
@@ -375,7 +375,7 @@
 
   void OnIncomingCapturedVideoFrame(
       std::unique_ptr<Buffer> buffer,
-      const scoped_refptr<media::VideoFrame>& frame) override {
+      scoped_refptr<media::VideoFrame> frame) override {
     EXPECT_FALSE(frame->visible_rect().IsEmpty());
     EXPECT_EQ(media::PIXEL_FORMAT_I420, frame->format());
     double frame_rate = 0;
diff --git a/content/browser/media/encrypted_media_browsertest.cc b/content/browser/media/encrypted_media_browsertest.cc
index 565bf29..066b8111 100644
--- a/content/browser/media/encrypted_media_browsertest.cc
+++ b/content/browser/media/encrypted_media_browsertest.cc
@@ -17,12 +17,6 @@
 #include "base/android/build_info.h"
 #endif
 
-// MojoCdm supports Clear Key, but currently MojoRenderer cannot use it.
-// See http://crbug.com/441957 for details.
-#if !(defined(ENABLE_MOJO_CDM) && defined(ENABLE_MOJO_RENDERER))
-#define SUPPORTS_CLEAR_KEY_IN_CONTENT_SHELL
-#endif
-
 #if defined(ENABLE_MOJO_CDM)
 // When mojo CDM is enabled, External Clear Key is supported in //content/shell/
 // by using mojo CDM with AesDecryptor running in the remote (e.g. GPU or
@@ -33,9 +27,7 @@
 #endif
 
 // Available key systems.
-#if defined(SUPPORTS_CLEAR_KEY_IN_CONTENT_SHELL)
 const char kClearKeyKeySystem[] = "org.w3.clearkey";
-#endif
 
 #if defined(SUPPORTS_EXTERNAL_CLEAR_KEY_IN_CONTENT_SHELL)
 const char kExternalClearKeyKeySystem[] = "org.chromium.externalclearkey";
@@ -149,13 +141,11 @@
 using ::testing::Combine;
 using ::testing::Values;
 
-#if defined(SUPPORTS_CLEAR_KEY_IN_CONTENT_SHELL)
 INSTANTIATE_TEST_CASE_P(SRC_ClearKey, EncryptedMediaTest,
                         Combine(Values(kClearKeyKeySystem), Values(SRC)));
 
 INSTANTIATE_TEST_CASE_P(MSE_ClearKey, EncryptedMediaTest,
                         Combine(Values(kClearKeyKeySystem), Values(MSE)));
-#endif
 
 #if defined(SUPPORTS_EXTERNAL_CLEAR_KEY_IN_CONTENT_SHELL)
 INSTANTIATE_TEST_CASE_P(SRC_ExternalClearKey,
diff --git a/content/browser/memory/memory_coordinator_impl.cc b/content/browser/memory/memory_coordinator_impl.cc
index 2340b23..c5bf7f1 100644
--- a/content/browser/memory/memory_coordinator_impl.cc
+++ b/content/browser/memory/memory_coordinator_impl.cc
@@ -5,6 +5,7 @@
 #include "content/browser/memory/memory_coordinator_impl.h"
 
 #include "base/metrics/histogram_macros.h"
+#include "base/process/process_metrics.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "content/browser/memory/memory_monitor.h"
@@ -52,6 +53,62 @@
   }
 }
 
+void RecordMetricsOnStateChange(base::MemoryState prev_state,
+                                base::MemoryState next_state,
+                                base::TimeDelta duration,
+                                size_t total_private_mb) {
+#define RECORD_METRICS(transition)                                             \
+  UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Coordinator.TotalPrivate." transition, \
+                                total_private_mb);                             \
+  UMA_HISTOGRAM_CUSTOM_TIMES("Memory.Coordinator.StateDuration." transition,   \
+                             duration, base::TimeDelta::FromSeconds(30),       \
+                             base::TimeDelta::FromHours(24), 50);
+
+  if (prev_state == base::MemoryState::NORMAL) {
+    switch (next_state) {
+      case base::MemoryState::THROTTLED:
+        RECORD_METRICS("NormalToThrottled");
+        break;
+      case base::MemoryState::SUSPENDED:
+        RECORD_METRICS("NormalToSuspended");
+        break;
+      case base::MemoryState::UNKNOWN:
+      case base::MemoryState::NORMAL:
+        NOTREACHED();
+        break;
+    }
+  } else if (prev_state == base::MemoryState::THROTTLED) {
+    switch (next_state) {
+      case base::MemoryState::NORMAL:
+        RECORD_METRICS("ThrottledToNormal");
+        break;
+      case base::MemoryState::SUSPENDED:
+        RECORD_METRICS("ThrottledToSuspended");
+        break;
+      case base::MemoryState::UNKNOWN:
+      case base::MemoryState::THROTTLED:
+        NOTREACHED();
+        break;
+    }
+  } else if (prev_state == base::MemoryState::SUSPENDED) {
+    switch (next_state) {
+      case base::MemoryState::NORMAL:
+        RECORD_METRICS("SuspendedToNormal");
+        break;
+      case base::MemoryState::THROTTLED:
+        RECORD_METRICS("SuspendedToThrottled");
+        break;
+      case base::MemoryState::UNKNOWN:
+      case base::MemoryState::SUSPENDED:
+        NOTREACHED();
+        break;
+    }
+  } else {
+    NOTREACHED();
+  }
+#undef RECORD_METRICS
+}
+
 }  // namespace
 
 // SingletonTraits for MemoryCoordinator. Returns MemoryCoordinatorImpl
@@ -194,6 +251,7 @@
 }
 
 void MemoryCoordinatorImpl::UpdateState() {
+  base::TimeTicks prev_last_state_change = last_state_change_;
   base::TimeTicks now = base::TimeTicks::Now();
   MemoryState prev_state = current_state_;
   MemoryState next_state = CalculateNextState();
@@ -208,6 +266,7 @@
                  "prev", MemoryStateToString(prev_state),
                  "next", MemoryStateToString(next_state));
 
+    RecordStateChange(prev_state, next_state, now - prev_last_state_change);
     NotifyStateToClients();
     NotifyStateToChildren();
     ScheduleUpdateState(minimum_transition_period_);
@@ -229,6 +288,34 @@
     SetChildMemoryState(iter.first, mojo_state);
 }
 
+void MemoryCoordinatorImpl::RecordStateChange(MemoryState prev_state,
+                                              MemoryState next_state,
+                                              base::TimeDelta duration) {
+  size_t total_private_kb = 0;
+
+  // TODO(bashi): On MacOS we can't get process metrics for child processes and
+  // therefore can't calculate the total private memory.
+#if !defined(OS_MACOSX)
+  auto browser_metrics = base::ProcessMetrics::CreateCurrentProcessMetrics();
+  base::WorkingSetKBytes working_set;
+  browser_metrics->GetWorkingSetKBytes(&working_set);
+  total_private_kb += working_set.priv;
+
+  for (auto& iter : children()) {
+    auto* render_process_host = RenderProcessHost::FromID(iter.first);
+    DCHECK(render_process_host);
+    DCHECK(render_process_host->GetHandle() != base::kNullProcessHandle);
+    auto metrics = base::ProcessMetrics::CreateProcessMetrics(
+        render_process_host->GetHandle());
+    metrics->GetWorkingSetKBytes(&working_set);
+    total_private_kb += working_set.priv;
+  }
+#endif
+
+  RecordMetricsOnStateChange(prev_state, next_state, duration,
+                             total_private_kb / 1024);
+}
+
 void MemoryCoordinatorImpl::ScheduleUpdateState(base::TimeDelta delta) {
   task_runner_->PostDelayedTask(FROM_HERE, update_state_callback_, delta);
 }
diff --git a/content/browser/memory/memory_coordinator_impl.h b/content/browser/memory/memory_coordinator_impl.h
index f9792a8..b86cc61 100644
--- a/content/browser/memory/memory_coordinator_impl.h
+++ b/content/browser/memory/memory_coordinator_impl.h
@@ -81,6 +81,11 @@
   // Notifies a state change to child processes.
   void NotifyStateToChildren();
 
+  // Records metrics. This is called when the global state is changed.
+  void RecordStateChange(MemoryState prev_state,
+                         MemoryState next_state,
+                         base::TimeDelta duration);
+
   // Schedules a task to update the global state. The task will be executed
   // after |delay| has passed.
   void ScheduleUpdateState(base::TimeDelta delay);
diff --git a/content/browser/renderer_host/media/video_capture_controller.cc b/content/browser/renderer_host/media/video_capture_controller.cc
index f9c9156..bf0d9d9 100644
--- a/content/browser/renderer_host/media/video_capture_controller.cc
+++ b/content/browser/renderer_host/media/video_capture_controller.cc
@@ -95,11 +95,11 @@
 
   void OnIncomingCapturedVideoFrame(
       std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer,
-      const scoped_refptr<media::VideoFrame>& frame) override {
+      scoped_refptr<media::VideoFrame> frame) override {
     BrowserThread::PostTask(
         BrowserThread::IO, FROM_HERE,
         base::Bind(&VideoFrameReceiver::OnIncomingCapturedVideoFrame, receiver_,
-                   base::Passed(&buffer), frame));
+                   base::Passed(&buffer), std::move(frame)));
   }
 
   void OnError() override {
@@ -402,7 +402,7 @@
 
 void VideoCaptureController::OnIncomingCapturedVideoFrame(
     std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer,
-    const scoped_refptr<VideoFrame>& frame) {
+    scoped_refptr<VideoFrame> frame) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   const int buffer_id = buffer->id();
   DCHECK_NE(buffer_id, media::VideoCaptureBufferPool::kInvalidId);
diff --git a/content/browser/renderer_host/media/video_capture_controller.h b/content/browser/renderer_host/media/video_capture_controller.h
index 2a6153f..8aaf97c 100644
--- a/content/browser/renderer_host/media/video_capture_controller.h
+++ b/content/browser/renderer_host/media/video_capture_controller.h
@@ -130,7 +130,7 @@
   // Implementation of media::VideoFrameReceiver interface:
   void OnIncomingCapturedVideoFrame(
       std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer,
-      const scoped_refptr<media::VideoFrame>& frame) override;
+      scoped_refptr<media::VideoFrame> frame) override;
   void OnError() override;
   void OnLog(const std::string& message) override;
   void OnBufferDestroyed(int buffer_id_to_drop) override;
diff --git a/content/browser/renderer_host/media/video_capture_device_client_unittest.cc b/content/browser/renderer_host/media/video_capture_device_client_unittest.cc
index 51db15c..020854b 100644
--- a/content/browser/renderer_host/media/video_capture_device_client_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_device_client_unittest.cc
@@ -44,7 +44,7 @@
 
   void OnIncomingCapturedVideoFrame(
       std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer,
-      const scoped_refptr<media::VideoFrame>& frame) override {
+      scoped_refptr<media::VideoFrame> frame) override {
     MockOnIncomingCapturedVideoFrame(frame->coded_size());
   }
 };
diff --git a/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.cc b/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.cc
index de8e65d..fc111e4 100644
--- a/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.cc
+++ b/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.cc
@@ -152,7 +152,7 @@
     decode_done_closure_ =
         base::Bind(decode_done_cb_, base::Passed(&out_buffer), out_frame);
   }
-  decoder_->Decode(in_buffer, out_frame);
+  decoder_->Decode(in_buffer, std::move(out_frame));
 #else
   NOTREACHED();
 #endif
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index a9479e0..c8cbea23 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -182,6 +182,7 @@
 #include "services/service_manager/public/cpp/interface_registry.h"
 #include "services/service_manager/runner/common/switches.h"
 #include "storage/browser/fileapi/sandbox_file_system_backend.h"
+#include "third_party/WebKit/public/public_features.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/display/display_switches.h"
@@ -236,7 +237,7 @@
 #include "content/common/media/media_stream_messages.h"
 #endif
 
-#if defined(USE_MINIKIN_HYPHENATION)
+#if BUILDFLAG(USE_MINIKIN_HYPHENATION)
 #include "content/browser/hyphenation/hyphenation_impl.h"
 #endif
 
@@ -1258,7 +1259,7 @@
       BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE);
   registry->AddInterface(base::Bind(&MimeRegistryImpl::Create),
                          file_task_runner);
-#if defined(USE_MINIKIN_HYPHENATION)
+#if BUILDFLAG(USE_MINIKIN_HYPHENATION)
   registry->AddInterface(base::Bind(&hyphenation::HyphenationImpl::Create),
                          file_task_runner);
 #endif
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index a0f34b66..68cc3d7 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -81,10 +81,8 @@
 #include "content/public/common/result_codes.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/common/url_utils.h"
-#include "net/base/filename_util.h"
 #include "net/base/url_util.h"
 #include "net/url_request/url_request_context_getter.h"
-#include "storage/browser/fileapi/isolated_context.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/clipboard/clipboard.h"
 #include "ui/base/touch/touch_device.h"
@@ -106,8 +104,6 @@
 using base::TimeDelta;
 using blink::WebConsoleMessage;
 using blink::WebDragOperation;
-using blink::WebDragOperationNone;
-using blink::WebDragOperationsMask;
 using blink::WebInputEvent;
 using blink::WebMediaPlayerAction;
 using blink::WebPluginAction;
@@ -153,56 +149,6 @@
 #endif
 }
 
-std::vector<DropData::Metadata> DropDataToMetaData(const DropData& drop_data) {
-  std::vector<DropData::Metadata> metadata;
-  if (!drop_data.text.is_null()) {
-    metadata.push_back(DropData::Metadata::CreateForMimeType(
-        DropData::Kind::STRING,
-        base::ASCIIToUTF16(ui::Clipboard::kMimeTypeText)));
-  }
-
-  if (drop_data.url.is_valid()) {
-    metadata.push_back(DropData::Metadata::CreateForMimeType(
-        DropData::Kind::STRING,
-        base::ASCIIToUTF16(ui::Clipboard::kMimeTypeURIList)));
-  }
-
-  if (!drop_data.html.is_null()) {
-    metadata.push_back(DropData::Metadata::CreateForMimeType(
-        DropData::Kind::STRING,
-        base::ASCIIToUTF16(ui::Clipboard::kMimeTypeHTML)));
-  }
-
-  // On Aura, filenames are available before drop.
-  for (const auto& file_info : drop_data.filenames) {
-    if (!file_info.path.empty()) {
-      metadata.push_back(DropData::Metadata::CreateForFilePath(file_info.path));
-    }
-  }
-
-  // On Android, only files' mime types are available before drop.
-  for (const auto& mime_type : drop_data.file_mime_types) {
-    if (!mime_type.empty()) {
-      metadata.push_back(DropData::Metadata::CreateForMimeType(
-          DropData::Kind::FILENAME, mime_type));
-    }
-  }
-
-  for (const auto& file_system_file : drop_data.file_system_files) {
-    if (!file_system_file.url.is_empty()) {
-      metadata.push_back(
-          DropData::Metadata::CreateForFileSystemUrl(file_system_file.url));
-    }
-  }
-
-  for (const auto& custom_data_item : drop_data.custom_data) {
-    metadata.push_back(DropData::Metadata::CreateForMimeType(
-        DropData::Kind::STRING, custom_data_item.first));
-  }
-
-  return metadata;
-}
-
 }  // namespace
 
 // static
@@ -660,61 +606,6 @@
   delegate_->RenderViewTerminated(this, status, exit_code);
 }
 
-void RenderViewHostImpl::DragTargetDragEnter(
-    const DropData& drop_data,
-    const gfx::Point& client_pt,
-    const gfx::Point& screen_pt,
-    WebDragOperationsMask operations_allowed,
-    int key_modifiers) {
-  DragTargetDragEnterWithMetaData(DropDataToMetaData(drop_data), client_pt,
-                                  screen_pt, operations_allowed, key_modifiers);
-}
-
-void RenderViewHostImpl::DragTargetDragEnterWithMetaData(
-    const std::vector<DropData::Metadata>& metadata,
-    const gfx::Point& client_pt,
-    const gfx::Point& screen_pt,
-    WebDragOperationsMask operations_allowed,
-    int key_modifiers) {
-  Send(new DragMsg_TargetDragEnter(GetRoutingID(), metadata, client_pt,
-                                   screen_pt, operations_allowed,
-                                   key_modifiers));
-}
-
-void RenderViewHostImpl::DragTargetDragOver(
-    const gfx::Point& client_pt,
-    const gfx::Point& screen_pt,
-    WebDragOperationsMask operations_allowed,
-    int key_modifiers) {
-  Send(new DragMsg_TargetDragOver(GetRoutingID(), client_pt, screen_pt,
-                                  operations_allowed, key_modifiers));
-}
-
-void RenderViewHostImpl::DragTargetDragLeave() {
-  Send(new DragMsg_TargetDragLeave(GetRoutingID()));
-}
-
-void RenderViewHostImpl::DragTargetDrop(const DropData& drop_data,
-                                        const gfx::Point& client_pt,
-                                        const gfx::Point& screen_pt,
-                                        int key_modifiers) {
-  DropData drop_data_with_permissions(drop_data);
-  GrantFileAccessFromDropData(&drop_data_with_permissions);
-  Send(new DragMsg_TargetDrop(GetRoutingID(), drop_data_with_permissions,
-                              client_pt, screen_pt, key_modifiers));
-}
-
-void RenderViewHostImpl::FilterDropData(DropData* drop_data) {
-#if DCHECK_IS_ON()
-  drop_data->view_id = GetRoutingID();
-#endif  // DCHECK_IS_ON()
-
-  GetProcess()->FilterURL(true, &drop_data->url);
-  if (drop_data->did_originate_from_renderer) {
-    drop_data->filenames.clear();
-  }
-}
-
 void RenderViewHostImpl::DragSourceEndedAt(
     int client_x, int client_y, int screen_x, int screen_y,
     WebDragOperation operation) {
@@ -1212,88 +1103,4 @@
   delegate_->RenderViewReady(this);
 }
 
-void RenderViewHostImpl::GrantFileAccessFromDropData(DropData* drop_data) {
-  DCHECK_EQ(GetRoutingID(), drop_data->view_id);
-  const int renderer_id = GetProcess()->GetID();
-  ChildProcessSecurityPolicyImpl* policy =
-      ChildProcessSecurityPolicyImpl::GetInstance();
-
-#if defined(OS_CHROMEOS)
-  // The externalfile:// scheme is used in Chrome OS to open external files in a
-  // browser tab.
-  if (drop_data->url.SchemeIs(content::kExternalFileScheme))
-    policy->GrantRequestURL(renderer_id, drop_data->url);
-#endif
-
-  // The filenames vector represents a capability to access the given files.
-  storage::IsolatedContext::FileInfoSet files;
-  for (auto& filename : drop_data->filenames) {
-    // Make sure we have the same display_name as the one we register.
-    if (filename.display_name.empty()) {
-      std::string name;
-      files.AddPath(filename.path, &name);
-      filename.display_name = base::FilePath::FromUTF8Unsafe(name);
-    } else {
-      files.AddPathWithName(filename.path,
-                            filename.display_name.AsUTF8Unsafe());
-    }
-    // A dragged file may wind up as the value of an input element, or it
-    // may be used as the target of a navigation instead.  We don't know
-    // which will happen at this point, so generously grant both access
-    // and request permissions to the specific file to cover both cases.
-    // We do not give it the permission to request all file:// URLs.
-    policy->GrantRequestSpecificFileURL(renderer_id,
-                                        net::FilePathToFileURL(filename.path));
-
-    // If the renderer already has permission to read these paths, we don't need
-    // to re-grant them. This prevents problems with DnD for files in the CrOS
-    // file manager--the file manager already had read/write access to those
-    // directories, but dragging a file would cause the read/write access to be
-    // overwritten with read-only access, making them impossible to delete or
-    // rename until the renderer was killed.
-    if (!policy->CanReadFile(renderer_id, filename.path))
-      policy->GrantReadFile(renderer_id, filename.path);
-  }
-
-  storage::IsolatedContext* isolated_context =
-      storage::IsolatedContext::GetInstance();
-  DCHECK(isolated_context);
-
-  if (!files.fileset().empty()) {
-    std::string filesystem_id =
-        isolated_context->RegisterDraggedFileSystem(files);
-    if (!filesystem_id.empty()) {
-      // Grant the permission iff the ID is valid.
-      policy->GrantReadFileSystem(renderer_id, filesystem_id);
-    }
-    drop_data->filesystem_id = base::UTF8ToUTF16(filesystem_id);
-  }
-
-  storage::FileSystemContext* file_system_context =
-      BrowserContext::GetStoragePartition(GetProcess()->GetBrowserContext(),
-                                          GetSiteInstance())
-          ->GetFileSystemContext();
-  for (auto& file_system_file : drop_data->file_system_files) {
-    storage::FileSystemURL file_system_url =
-        file_system_context->CrackURL(file_system_file.url);
-
-    std::string register_name;
-    std::string filesystem_id = isolated_context->RegisterFileSystemForPath(
-        file_system_url.type(), file_system_url.filesystem_id(),
-        file_system_url.path(), &register_name);
-
-    if (!filesystem_id.empty()) {
-      // Grant the permission iff the ID is valid.
-      policy->GrantReadFileSystem(renderer_id, filesystem_id);
-    }
-
-    // Note: We are using the origin URL provided by the sender here. It may be
-    // different from the receiver's.
-    file_system_file.url =
-        GURL(storage::GetIsolatedFileSystemRootURIString(
-                 file_system_url.origin(), filesystem_id, std::string())
-                 .append(register_name));
-  }
-}
-
 }  // namespace content
diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h
index 6a6a4c39..8990af9 100644
--- a/content/browser/renderer_host/render_view_host_impl.h
+++ b/content/browser/renderer_host/render_view_host_impl.h
@@ -107,31 +107,6 @@
                          int screen_y,
                          blink::WebDragOperation operation) override;
   void DragSourceSystemDragEnded() override;
-  // |drop_data| must have been filtered. The embedder should call
-  // FilterDropData before passing the drop data to RVHI.
-  void DragTargetDragEnter(const DropData& drop_data,
-                           const gfx::Point& client_pt,
-                           const gfx::Point& screen_pt,
-                           blink::WebDragOperationsMask operations_allowed,
-                           int key_modifiers) override;
-  void DragTargetDragEnterWithMetaData(
-      const std::vector<DropData::Metadata>& metadata,
-      const gfx::Point& client_pt,
-      const gfx::Point& screen_pt,
-      blink::WebDragOperationsMask operations_allowed,
-      int key_modifiers) override;
-  void DragTargetDragOver(const gfx::Point& client_pt,
-                          const gfx::Point& screen_pt,
-                          blink::WebDragOperationsMask operations_allowed,
-                          int key_modifiers) override;
-  void DragTargetDragLeave() override;
-  // |drop_data| must have been filtered. The embedder should call
-  // FilterDropData before passing the drop data to RVHI.
-  void DragTargetDrop(const DropData& drop_data,
-                      const gfx::Point& client_pt,
-                      const gfx::Point& screen_pt,
-                      int key_modifiers) override;
-  void FilterDropData(DropData* drop_data) override;
   void EnableAutoResize(const gfx::Size& min_size,
                         const gfx::Size& max_size) override;
   void DisableAutoResize(const gfx::Size& new_size) override;
@@ -325,12 +300,6 @@
   // See https://crbug.com/304341.
   WebPreferences ComputeWebkitPrefs();
 
-  // 1. Grants permissions to URL (if any)
-  // 2. Grants permissions to filenames
-  // 3. Grants permissions to file system files.
-  // 4. Register the files with the IsolatedContext.
-  void GrantFileAccessFromDropData(DropData* drop_data);
-
   // The RenderWidgetHost.
   std::unique_ptr<RenderWidgetHostImpl> render_widget_host_;
 
diff --git a/content/browser/renderer_host/render_view_host_unittest.cc b/content/browser/renderer_host/render_view_host_unittest.cc
index 3504cbe..26a8e77 100644
--- a/content/browser/renderer_host/render_view_host_unittest.cc
+++ b/content/browser/renderer_host/render_view_host_unittest.cc
@@ -173,9 +173,11 @@
   dropped_data.filenames.push_back(
       ui::FileInfo(dragged_file_path, base::FilePath()));
 
-  rvh()->FilterDropData(&dropped_data);
-  rvh()->DragTargetDragEnter(dropped_data, client_point, screen_point,
-                              blink::WebDragOperationNone, 0);
+  // TODO(paulmeyer): These will need to target the correct specific
+  // RenderWidgetHost to work with OOPIFs. See crbug.com/647249.
+  rvh()->GetWidget()->FilterDropData(&dropped_data);
+  rvh()->GetWidget()->DragTargetDragEnter(
+      dropped_data, client_point, screen_point, blink::WebDragOperationNone, 0);
 
   int id = process()->GetID();
   ChildProcessSecurityPolicyImpl* policy =
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 33766b7..aa54fac 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -76,9 +76,12 @@
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/service/gpu_switches.h"
 #include "gpu/ipc/common/gpu_messages.h"
+#include "net/base/filename_util.h"
 #include "skia/ext/image_operations.h"
 #include "skia/ext/platform_canvas.h"
+#include "storage/browser/fileapi/isolated_context.h"
 #include "third_party/WebKit/public/web/WebCompositionUnderline.h"
+#include "ui/base/clipboard/clipboard.h"
 #include "ui/events/blink/web_input_event_traits.h"
 #include "ui/events/event.h"
 #include "ui/events/keycodes/keyboard_codes.h"
@@ -97,6 +100,7 @@
 using base::Time;
 using base::TimeDelta;
 using base::TimeTicks;
+using blink::WebDragOperationsMask;
 using blink::WebGestureEvent;
 using blink::WebInputEvent;
 using blink::WebKeyboardEvent;
@@ -182,6 +186,56 @@
   return wrap_gesture_scroll_end;
 }
 
+std::vector<DropData::Metadata> DropDataToMetaData(const DropData& drop_data) {
+  std::vector<DropData::Metadata> metadata;
+  if (!drop_data.text.is_null()) {
+    metadata.push_back(DropData::Metadata::CreateForMimeType(
+        DropData::Kind::STRING,
+        base::ASCIIToUTF16(ui::Clipboard::kMimeTypeText)));
+  }
+
+  if (drop_data.url.is_valid()) {
+    metadata.push_back(DropData::Metadata::CreateForMimeType(
+        DropData::Kind::STRING,
+        base::ASCIIToUTF16(ui::Clipboard::kMimeTypeURIList)));
+  }
+
+  if (!drop_data.html.is_null()) {
+    metadata.push_back(DropData::Metadata::CreateForMimeType(
+        DropData::Kind::STRING,
+        base::ASCIIToUTF16(ui::Clipboard::kMimeTypeHTML)));
+  }
+
+  // On Aura, filenames are available before drop.
+  for (const auto& file_info : drop_data.filenames) {
+    if (!file_info.path.empty()) {
+      metadata.push_back(DropData::Metadata::CreateForFilePath(file_info.path));
+    }
+  }
+
+  // On Android, only files' mime types are available before drop.
+  for (const auto& mime_type : drop_data.file_mime_types) {
+    if (!mime_type.empty()) {
+      metadata.push_back(DropData::Metadata::CreateForMimeType(
+          DropData::Kind::FILENAME, mime_type));
+    }
+  }
+
+  for (const auto& file_system_file : drop_data.file_system_files) {
+    if (!file_system_file.url.is_empty()) {
+      metadata.push_back(
+          DropData::Metadata::CreateForFileSystemUrl(file_system_file.url));
+    }
+  }
+
+  for (const auto& custom_data_item : drop_data.custom_data) {
+    metadata.push_back(DropData::Metadata::CreateForMimeType(
+        DropData::Kind::STRING, custom_data_item.first));
+  }
+
+  return metadata;
+}
+
 }  // namespace
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1278,6 +1332,61 @@
   Send(new ViewMsg_HandleCompositorProto(GetRoutingID(), proto));
 }
 
+void RenderWidgetHostImpl::DragTargetDragEnter(
+    const DropData& drop_data,
+    const gfx::Point& client_pt,
+    const gfx::Point& screen_pt,
+    WebDragOperationsMask operations_allowed,
+    int key_modifiers) {
+  DragTargetDragEnterWithMetaData(DropDataToMetaData(drop_data), client_pt,
+                                  screen_pt, operations_allowed, key_modifiers);
+}
+
+void RenderWidgetHostImpl::DragTargetDragEnterWithMetaData(
+    const std::vector<DropData::Metadata>& metadata,
+    const gfx::Point& client_pt,
+    const gfx::Point& screen_pt,
+    WebDragOperationsMask operations_allowed,
+    int key_modifiers) {
+  Send(new DragMsg_TargetDragEnter(GetRoutingID(), metadata, client_pt,
+                                   screen_pt, operations_allowed,
+                                   key_modifiers));
+}
+
+void RenderWidgetHostImpl::DragTargetDragOver(
+    const gfx::Point& client_pt,
+    const gfx::Point& screen_pt,
+    WebDragOperationsMask operations_allowed,
+    int key_modifiers) {
+  Send(new DragMsg_TargetDragOver(GetRoutingID(), client_pt, screen_pt,
+                                  operations_allowed, key_modifiers));
+}
+
+void RenderWidgetHostImpl::DragTargetDragLeave() {
+  Send(new DragMsg_TargetDragLeave(GetRoutingID()));
+}
+
+void RenderWidgetHostImpl::DragTargetDrop(const DropData& drop_data,
+                                          const gfx::Point& client_pt,
+                                          const gfx::Point& screen_pt,
+                                          int key_modifiers) {
+  DropData drop_data_with_permissions(drop_data);
+  GrantFileAccessFromDropData(&drop_data_with_permissions);
+  Send(new DragMsg_TargetDrop(GetRoutingID(), drop_data_with_permissions,
+                              client_pt, screen_pt, key_modifiers));
+}
+
+void RenderWidgetHostImpl::FilterDropData(DropData* drop_data) {
+#if DCHECK_IS_ON()
+  drop_data->view_id = GetRoutingID();
+#endif  // DCHECK_IS_ON()
+
+  GetProcess()->FilterURL(true, &drop_data->url);
+  if (drop_data->did_originate_from_renderer) {
+    drop_data->filenames.clear();
+  }
+}
+
 void RenderWidgetHostImpl::NotifyScreenInfoChanged() {
   if (delegate_)
     delegate_->ScreenInfoChanged();
@@ -1352,6 +1461,9 @@
     const SkBitmap& bitmap,
     const gfx::Vector2d& bitmap_offset_in_dip,
     const DragEventSourceInfo& event_info) {
+  // TODO(paulmeyer): Stop relying on RenderViewHost once
+  // DragSourceSystemDragEnded is moved into RenderWidgetHost. See
+  // crbug.com/647249.
   RenderViewHost* rvh = RenderViewHost::From(this);
   if (!rvh)
     return;
@@ -1387,9 +1499,7 @@
   }
 
   storage::FileSystemContext* file_system_context =
-      BrowserContext::GetStoragePartition(GetProcess()->GetBrowserContext(),
-                                          rvh->GetSiteInstance())
-      ->GetFileSystemContext();
+      GetProcess()->GetStoragePartition()->GetFileSystemContext();
   filtered_data.file_system_files.clear();
   for (size_t i = 0; i < drop_data.file_system_files.size(); ++i) {
     storage::FileSystemURL file_system_url =
@@ -2279,4 +2389,86 @@
       delegate_->GetOrCreateRootBrowserAccessibilityManager() : NULL;
 }
 
+void RenderWidgetHostImpl::GrantFileAccessFromDropData(DropData* drop_data) {
+  DCHECK_EQ(GetRoutingID(), drop_data->view_id);
+  const int renderer_id = GetProcess()->GetID();
+  ChildProcessSecurityPolicyImpl* policy =
+      ChildProcessSecurityPolicyImpl::GetInstance();
+
+#if defined(OS_CHROMEOS)
+  // The externalfile:// scheme is used in Chrome OS to open external files in a
+  // browser tab.
+  if (drop_data->url.SchemeIs(content::kExternalFileScheme))
+    policy->GrantRequestURL(renderer_id, drop_data->url);
+#endif
+
+  // The filenames vector represents a capability to access the given files.
+  storage::IsolatedContext::FileInfoSet files;
+  for (auto& filename : drop_data->filenames) {
+    // Make sure we have the same display_name as the one we register.
+    if (filename.display_name.empty()) {
+      std::string name;
+      files.AddPath(filename.path, &name);
+      filename.display_name = base::FilePath::FromUTF8Unsafe(name);
+    } else {
+      files.AddPathWithName(filename.path,
+                            filename.display_name.AsUTF8Unsafe());
+    }
+    // A dragged file may wind up as the value of an input element, or it
+    // may be used as the target of a navigation instead.  We don't know
+    // which will happen at this point, so generously grant both access
+    // and request permissions to the specific file to cover both cases.
+    // We do not give it the permission to request all file:// URLs.
+    policy->GrantRequestSpecificFileURL(renderer_id,
+                                        net::FilePathToFileURL(filename.path));
+
+    // If the renderer already has permission to read these paths, we don't need
+    // to re-grant them. This prevents problems with DnD for files in the CrOS
+    // file manager--the file manager already had read/write access to those
+    // directories, but dragging a file would cause the read/write access to be
+    // overwritten with read-only access, making them impossible to delete or
+    // rename until the renderer was killed.
+    if (!policy->CanReadFile(renderer_id, filename.path))
+      policy->GrantReadFile(renderer_id, filename.path);
+  }
+
+  storage::IsolatedContext* isolated_context =
+      storage::IsolatedContext::GetInstance();
+  DCHECK(isolated_context);
+
+  if (!files.fileset().empty()) {
+    std::string filesystem_id =
+        isolated_context->RegisterDraggedFileSystem(files);
+    if (!filesystem_id.empty()) {
+      // Grant the permission iff the ID is valid.
+      policy->GrantReadFileSystem(renderer_id, filesystem_id);
+    }
+    drop_data->filesystem_id = base::UTF8ToUTF16(filesystem_id);
+  }
+
+  storage::FileSystemContext* file_system_context =
+      GetProcess()->GetStoragePartition()->GetFileSystemContext();
+  for (auto& file_system_file : drop_data->file_system_files) {
+    storage::FileSystemURL file_system_url =
+        file_system_context->CrackURL(file_system_file.url);
+
+    std::string register_name;
+    std::string filesystem_id = isolated_context->RegisterFileSystemForPath(
+        file_system_url.type(), file_system_url.filesystem_id(),
+        file_system_url.path(), &register_name);
+
+    if (!filesystem_id.empty()) {
+      // Grant the permission iff the ID is valid.
+      policy->GrantReadFileSystem(renderer_id, filesystem_id);
+    }
+
+    // Note: We are using the origin URL provided by the sender here. It may be
+    // different from the receiver's.
+    file_system_file.url =
+        GURL(storage::GetIsolatedFileSystemRootURIString(
+                 file_system_url.origin(), filesystem_id, std::string())
+                 .append(register_name));
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index fdae90bef..7f809291 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -40,6 +40,7 @@
 #include "content/public/browser/readback_types.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/common/page_zoom.h"
+#include "content/public/common/url_constants.h"
 #include "ipc/ipc_listener.h"
 #include "third_party/WebKit/public/platform/WebDisplayMode.h"
 #include "ui/base/ime/text_input_mode.h"
@@ -174,6 +175,31 @@
       RenderWidgetHost::InputEventObserver* observer) override;
   void GetScreenInfo(content::ScreenInfo* result) override;
   void HandleCompositorProto(const std::vector<uint8_t>& proto) override;
+  // |drop_data| must have been filtered. The embedder should call
+  // FilterDropData before passing the drop data to RWHI.
+  void DragTargetDragEnter(const DropData& drop_data,
+                           const gfx::Point& client_pt,
+                           const gfx::Point& screen_pt,
+                           blink::WebDragOperationsMask operations_allowed,
+                           int key_modifiers) override;
+  void DragTargetDragEnterWithMetaData(
+      const std::vector<DropData::Metadata>& metadata,
+      const gfx::Point& client_pt,
+      const gfx::Point& screen_pt,
+      blink::WebDragOperationsMask operations_allowed,
+      int key_modifiers) override;
+  void DragTargetDragOver(const gfx::Point& client_pt,
+                          const gfx::Point& screen_pt,
+                          blink::WebDragOperationsMask operations_allowed,
+                          int key_modifiers) override;
+  void DragTargetDragLeave() override;
+  // |drop_data| must have been filtered. The embedder should call
+  // FilterDropData before passing the drop data to RWHI.
+  void DragTargetDrop(const DropData& drop_data,
+                      const gfx::Point& client_pt,
+                      const gfx::Point& screen_pt,
+                      int key_modifiers) override;
+  void FilterDropData(DropData* drop_data) override;
 
   // Notification that the screen info has changed.
   void NotifyScreenInfoChanged();
@@ -667,6 +693,12 @@
       int snapshot_id,
       scoped_refptr<base::RefCountedBytes> png_data);
 
+  // 1. Grants permissions to URL (if any)
+  // 2. Grants permissions to filenames
+  // 3. Grants permissions to file system files.
+  // 4. Register the files with the IsolatedContext.
+  void GrantFileAccessFromDropData(DropData* drop_data);
+
   // true if a renderer has once been valid. We use this flag to display a sad
   // tab only when we lose our renderer and not if a paint occurs during
   // initialization.
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index af8bd376..f418163 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -3017,12 +3017,6 @@
   return rect;
 }
 
-- (NSRange)selectedRange {
-  if (selectedRange_.location == NSNotFound || selectedRange_.length == 0)
-    return NSMakeRange(NSNotFound, 0);
-  return selectedRange_;
-}
-
 - (NSRange)markedRange {
   // An input method calls this method to check if an application really has
   // a text being composed when hasMarkedText call returns true.
@@ -3035,17 +3029,9 @@
 
 - (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range
     actualRange:(NSRangePointer)actualRange {
-  // Prepare |actualRange| as if the proposed range is invalid. If it is valid,
-  // then |actualRange| will be updated again.
+  // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery.
   if (actualRange)
-    *actualRange = NSMakeRange(NSNotFound, 0);
-
-  // The caller of this method is allowed to pass nonsensical ranges. These
-  // can't even be converted into gfx::Ranges.
-  if (range.location == NSNotFound || range.length == 0)
-    return nil;
-  if (range.length >= std::numeric_limits<NSUInteger>::max() - range.location)
-    return nil;
+    *actualRange = range;
 
   const gfx::Range requested_range(range);
   if (requested_range.is_reversed())
@@ -3063,16 +3049,15 @@
     expected_range = gfx::Range(offset, offset + expected_text->size());
   }
 
-  gfx::Range actual_range = expected_range.Intersect(requested_range);
-  if (!actual_range.IsValid())
+  if (!expected_range.Contains(requested_range))
     return nil;
 
   // Gets the raw bytes to avoid unnecessary string copies for generating
   // NSString.
   const base::char16* bytes =
-      &(*expected_text)[actual_range.start() - expected_range.start()];
+      &(*expected_text)[requested_range.start() - expected_range.start()];
   // Avoid integer overflow.
-  base::CheckedNumeric<size_t> requested_len = actual_range.length();
+  base::CheckedNumeric<size_t> requested_len = requested_range.length();
   requested_len *= sizeof(base::char16);
   NSUInteger bytes_len = base::strict_cast<NSUInteger, size_t>(
       requested_len.ValueOrDefault(0));
@@ -3080,9 +3065,6 @@
       [[NSString alloc] initWithBytes:bytes
                                length:bytes_len
                              encoding:NSUTF16LittleEndianStringEncoding]);
-  if (actualRange)
-    *actualRange = actual_range.ToNSRange();
-
   return [[[NSAttributedString alloc] initWithString:ns_string] autorelease];
 }
 
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
index 020abdb..d98d707a 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
@@ -317,29 +317,6 @@
   EXPECT_TRUE([rwhv_cocoa_.get() acceptsFirstResponder]);
 }
 
-// This test verifies that RenderWidgetHostViewCocoa's implementation of
-// NSTextInputClientConformance conforms to requirements.
-TEST_F(RenderWidgetHostViewMacTest, NSTextInputClientConformance) {
-  NSRange selectedRange = [rwhv_cocoa_ selectedRange];
-  EXPECT_EQ(NSNotFound, selectedRange.location);
-  EXPECT_EQ(0u, selectedRange.length);
-
-  NSRange actualRange = NSMakeRange(1u, 2u);
-  NSAttributedString* actualString = [rwhv_cocoa_
-      attributedSubstringForProposedRange:NSMakeRange(NSNotFound, 0u)
-                              actualRange:&actualRange];
-  EXPECT_EQ(nil, actualString);
-  EXPECT_EQ(NSNotFound, actualRange.location);
-  EXPECT_EQ(0u, actualRange.length);
-
-  actualString = [rwhv_cocoa_
-      attributedSubstringForProposedRange:NSMakeRange(NSNotFound, 15u)
-                              actualRange:&actualRange];
-  EXPECT_EQ(nil, actualString);
-  EXPECT_EQ(NSNotFound, actualRange.location);
-  EXPECT_EQ(0u, actualRange.length);
-}
-
 TEST_F(RenderWidgetHostViewMacTest, Fullscreen) {
   rwhv_mac_->InitAsFullscreen(NULL);
   EXPECT_TRUE(rwhv_mac_->pepper_fullscreen_window());
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index a5550ff..367951ac 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -4654,14 +4654,14 @@
     rwh->Focus();
 }
 
-bool WebContentsImpl::AddMessageToConsole(int32_t level,
-                                          const base::string16& message,
-                                          int32_t line_no,
-                                          const base::string16& source_id) {
+bool WebContentsImpl::DidAddMessageToConsole(int32_t level,
+                                             const base::string16& message,
+                                             int32_t line_no,
+                                             const base::string16& source_id) {
   if (!delegate_)
     return false;
-  return delegate_->AddMessageToConsole(this, level, message, line_no,
-                                        source_id);
+  return delegate_->DidAddMessageToConsole(this, level, message, line_no,
+                                           source_id);
 }
 
 void WebContentsImpl::OnUserInteraction(
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 705372f5..91349dc 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -512,10 +512,10 @@
   void DidCancelLoading() override;
   void DocumentAvailableInMainFrame(RenderViewHost* render_view_host) override;
   void RouteCloseEvent(RenderViewHost* rvh) override;
-  bool AddMessageToConsole(int32_t level,
-                           const base::string16& message,
-                           int32_t line_no,
-                           const base::string16& source_id) override;
+  bool DidAddMessageToConsole(int32_t level,
+                              const base::string16& message,
+                              int32_t line_no,
+                              const base::string16& source_id) override;
   RendererPreferences GetRendererPrefs(
       BrowserContext* browser_context) const override;
   void OnUserInteraction(RenderWidgetHostImpl* render_widget_host,
diff --git a/content/browser/web_contents/web_contents_view_android.cc b/content/browser/web_contents/web_contents_view_android.cc
index 338d07e..49a6e02 100644
--- a/content/browser/web_contents/web_contents_view_android.cc
+++ b/content/browser/web_contents/web_contents_view_android.cc
@@ -304,6 +304,10 @@
   // Intentional no-op because Android does not have cursor.
 }
 
+// TODO(paulmeyer): The drag-and-drop calls on GetRenderViewHost()->GetWidget()
+// in the following functions will need to be targeted to specific
+// RenderWidgetHosts in order to work with OOPIFs. See crbug.com/647249.
+
 void WebContentsViewAndroid::OnDragEntered(
     const std::vector<DropData::Metadata>& metadata,
     const gfx::Point& location,
@@ -311,8 +315,9 @@
   blink::WebDragOperationsMask allowed_ops =
       static_cast<blink::WebDragOperationsMask>(blink::WebDragOperationCopy |
                                                 blink::WebDragOperationMove);
-  web_contents_->GetRenderViewHost()->DragTargetDragEnterWithMetaData(
-      metadata, location, screen_location, allowed_ops, 0);
+  web_contents_->GetRenderViewHost()->GetWidget()->
+      DragTargetDragEnterWithMetaData(metadata, location, screen_location,
+                                      allowed_ops, 0);
 }
 
 void WebContentsViewAndroid::OnDragUpdated(const gfx::Point& location,
@@ -320,20 +325,20 @@
   blink::WebDragOperationsMask allowed_ops =
       static_cast<blink::WebDragOperationsMask>(blink::WebDragOperationCopy |
                                                 blink::WebDragOperationMove);
-  web_contents_->GetRenderViewHost()->DragTargetDragOver(
+  web_contents_->GetRenderViewHost()->GetWidget()->DragTargetDragOver(
       location, screen_location, allowed_ops, 0);
 }
 
 void WebContentsViewAndroid::OnDragExited() {
-  web_contents_->GetRenderViewHost()->DragTargetDragLeave();
+  web_contents_->GetRenderViewHost()->GetWidget()->DragTargetDragLeave();
 }
 
 void WebContentsViewAndroid::OnPerformDrop(DropData* drop_data,
                                            const gfx::Point& location,
                                            const gfx::Point& screen_location) {
-  web_contents_->GetRenderViewHost()->FilterDropData(drop_data);
-  web_contents_->GetRenderViewHost()->DragTargetDrop(*drop_data, location,
-                                                     screen_location, 0);
+  web_contents_->GetRenderViewHost()->GetWidget()->FilterDropData(drop_data);
+  web_contents_->GetRenderViewHost()->GetWidget()->DragTargetDrop(
+      *drop_data, location, screen_location, 0);
 }
 
 void WebContentsViewAndroid::OnDragEnded() {
diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc
index aaa8913..bbbff9c43 100644
--- a/content/browser/web_contents/web_contents_view_aura.cc
+++ b/content/browser/web_contents/web_contents_view_aura.cc
@@ -1108,13 +1108,18 @@
 ////////////////////////////////////////////////////////////////////////////////
 // WebContentsViewAura, aura::client::DragDropDelegate implementation:
 
+// TODO(paulmeyer): The drag-and-drop calls on GetRenderViewHost()->GetWidget()
+// in the following functions will need to be targeted to specific
+// RenderWidgetHosts in order to work with OOPIFs. See crbug.com/647249.
+
 void WebContentsViewAura::OnDragEntered(const ui::DropTargetEvent& event) {
   current_rvh_for_drag_ = web_contents_->GetRenderViewHost();
   current_drop_data_.reset(new DropData());
 
   PrepareDropData(current_drop_data_.get(), event.data());
 
-  web_contents_->GetRenderViewHost()->FilterDropData(current_drop_data_.get());
+  web_contents_->GetRenderViewHost()->GetWidget()->
+      FilterDropData(current_drop_data_.get());
 
   blink::WebDragOperationsMask op = ConvertToWeb(event.source_operations());
 
@@ -1130,7 +1135,7 @@
     drag_dest_delegate_->DragInitialize(web_contents_);
 
   gfx::Point screen_pt = display::Screen::GetScreen()->GetCursorScreenPoint();
-  web_contents_->GetRenderViewHost()->DragTargetDragEnter(
+  web_contents_->GetRenderViewHost()->GetWidget()->DragTargetDragEnter(
       *current_drop_data_, event.location(), screen_pt, op,
       ConvertAuraEventFlagsToWebInputEventModifiers(event.flags()));
 
@@ -1150,7 +1155,7 @@
 
   blink::WebDragOperationsMask op = ConvertToWeb(event.source_operations());
   gfx::Point screen_pt = event.root_location();
-  web_contents_->GetRenderViewHost()->DragTargetDragOver(
+  web_contents_->GetRenderViewHost()->GetWidget()->DragTargetDragOver(
       event.location(), screen_pt, op,
       ConvertAuraEventFlagsToWebInputEventModifiers(event.flags()));
 
@@ -1168,7 +1173,7 @@
   if (!current_drop_data_)
     return;
 
-  web_contents_->GetRenderViewHost()->DragTargetDragLeave();
+  web_contents_->GetRenderViewHost()->GetWidget()->DragTargetDragLeave();
   if (drag_dest_delegate_)
     drag_dest_delegate_->OnDragLeave();
 
@@ -1183,7 +1188,7 @@
   if (!current_drop_data_)
     return ui::DragDropTypes::DRAG_NONE;
 
-  web_contents_->GetRenderViewHost()->DragTargetDrop(
+  web_contents_->GetRenderViewHost()->GetWidget()->DragTargetDrop(
       *current_drop_data_, event.location(),
       display::Screen::GetScreen()->GetCursorScreenPoint(),
       ConvertAuraEventFlagsToWebInputEventModifiers(event.flags()));
diff --git a/content/browser/web_contents/web_drag_dest_mac.mm b/content/browser/web_contents/web_drag_dest_mac.mm
index 9e595764..efeaa5cd 100644
--- a/content/browser/web_contents/web_drag_dest_mac.mm
+++ b/content/browser/web_contents/web_drag_dest_mac.mm
@@ -130,7 +130,9 @@
   dropData.reset(new DropData());
   [self populateDropData:dropData.get()
              fromPasteboard:[info draggingPasteboard]];
-  currentRVH_->FilterDropData(dropData.get());
+  // TODO(paulmeyer): This will need to target the correct specific
+  // RenderWidgetHost to work with OOPIFs. See crbug.com/647249.
+  currentRVH_->GetWidget()->FilterDropData(dropData.get());
 
   NSDragOperation mask = [info draggingSourceOperationMask];
 
@@ -160,7 +162,9 @@
   NSPoint windowPoint = [info draggingLocation];
   NSPoint viewPoint = [self flipWindowPointToView:windowPoint view:view];
   NSPoint screenPoint = [self flipWindowPointToScreen:windowPoint view:view];
-  webContents_->GetRenderViewHost()->DragTargetDragEnter(
+  // TODO(paulmeyer): This will need to target the correct specific
+  // RenderWidgetHost to work with OOPIFs. See crbug.com/647249.
+  webContents_->GetRenderViewHost()->GetWidget()->DragTargetDragEnter(
       *dropData_,
       gfx::Point(viewPoint.x, viewPoint.y),
       gfx::Point(screenPoint.x, screenPoint.y),
@@ -187,7 +191,9 @@
   if (delegate_)
     delegate_->OnDragLeave();
 
-  webContents_->GetRenderViewHost()->DragTargetDragLeave();
+  // TODO(paulmeyer): This will need to target the correct specific
+  // RenderWidgetHost to work with OOPIFs. See crbug.com/647249.
+  webContents_->GetRenderViewHost()->GetWidget()->DragTargetDragLeave();
   dropData_.reset();
 }
 
@@ -212,7 +218,9 @@
   NSPoint viewPoint = [self flipWindowPointToView:windowPoint view:view];
   NSPoint screenPoint = [self flipWindowPointToScreen:windowPoint view:view];
   NSDragOperation mask = [info draggingSourceOperationMask];
-  webContents_->GetRenderViewHost()->DragTargetDragOver(
+  // TODO(paulmeyer): This will need to target the correct specific
+  // RenderWidgetHost to work with OOPIFs. See crbug.com/647249.
+  webContents_->GetRenderViewHost()->GetWidget()->DragTargetDragOver(
       gfx::Point(viewPoint.x, viewPoint.y),
       gfx::Point(screenPoint.x, screenPoint.y),
       static_cast<WebDragOperationsMask>(mask),
@@ -254,7 +262,9 @@
   NSPoint windowPoint = [info draggingLocation];
   NSPoint viewPoint = [self flipWindowPointToView:windowPoint view:view];
   NSPoint screenPoint = [self flipWindowPointToScreen:windowPoint view:view];
-  webContents_->GetRenderViewHost()->DragTargetDrop(
+  // TODO(paulmeyer): This will need to target the correct specific
+  // RenderWidgetHost to work with OOPIFs. See crbug.com/647249.
+  webContents_->GetRenderViewHost()->GetWidget()->DragTargetDrop(
       *dropData_, gfx::Point(viewPoint.x, viewPoint.y),
       gfx::Point(screenPoint.x, screenPoint.y), GetModifierFlags());
 
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index d9e9e302..5d0a671 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -927,7 +927,7 @@
 
 // Blink and JavaScript error messages to log to the console
 // or debugger UI.
-IPC_MESSAGE_ROUTED4(FrameHostMsg_AddMessageToConsole,
+IPC_MESSAGE_ROUTED4(FrameHostMsg_DidAddMessageToConsole,
                     int32_t,        /* log level */
                     base::string16, /* msg */
                     int32_t,        /* line number */
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java
index d63f7ed..5953108 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java
@@ -145,9 +145,9 @@
 
         if (!view.hasWindowFocus()) mCheckInvalidator.invalidate();
 
-        if (mProxyView == null) {
-            mProxyView = createProxyView(mHandler, view);
-        }
+        // We cannot reuse the existing proxy view, if any, due to crbug.com/664402.
+        mProxyView = createProxyView(mHandler, view);
+
         mReentrantTriggering = true;
         // This does not affect view focus of the real views.
         mProxyView.requestFocus();
diff --git a/content/public/browser/render_view_host.h b/content/public/browser/render_view_host.h
index fd4c029..bcdb2e33 100644
--- a/content/public/browser/render_view_host.h
+++ b/content/public/browser/render_view_host.h
@@ -115,31 +115,6 @@
   // This allows the renderer to reset some state.
   virtual void DragSourceSystemDragEnded() = 0;
 
-  // D&d drop target messages that get sent to WebKit.
-  virtual void DragTargetDragEnter(
-      const DropData& drop_data,
-      const gfx::Point& client_pt,
-      const gfx::Point& screen_pt,
-      blink::WebDragOperationsMask operations_allowed,
-      int key_modifiers) = 0;
-  virtual void DragTargetDragEnterWithMetaData(
-      const std::vector<DropData::Metadata>& metadata,
-      const gfx::Point& client_pt,
-      const gfx::Point& screen_pt,
-      blink::WebDragOperationsMask operations_allowed,
-      int key_modifiers) = 0;
-  virtual void DragTargetDragOver(
-      const gfx::Point& client_pt,
-      const gfx::Point& screen_pt,
-      blink::WebDragOperationsMask operations_allowed,
-      int key_modifiers) = 0;
-  virtual void DragTargetDragLeave() = 0;
-  virtual void DragTargetDrop(const DropData& drop_data,
-                              const gfx::Point& client_pt,
-                              const gfx::Point& screen_pt,
-                              int key_modifiers) = 0;
-  virtual void FilterDropData(DropData* drop_data) = 0;
-
   // Instructs the RenderView to automatically resize and send back updates
   // for the new size.
   virtual void EnableAutoResize(const gfx::Size& min_size,
diff --git a/content/public/browser/render_widget_host.h b/content/public/browser/render_widget_host.h
index b75fd87..2c6c7c36 100644
--- a/content/public/browser/render_widget_host.h
+++ b/content/public/browser/render_widget_host.h
@@ -259,6 +259,33 @@
 
   // Sends a compositor proto to the render widget.
   virtual void HandleCompositorProto(const std::vector<uint8_t>& proto) = 0;
+
+  // Drag-and-drop drop target messages that get sent to Blink.
+  virtual void DragTargetDragEnter(
+      const DropData& drop_data,
+      const gfx::Point& client_pt,
+      const gfx::Point& screen_pt,
+      blink::WebDragOperationsMask operations_allowed,
+      int key_modifiers) {}
+  virtual void DragTargetDragEnterWithMetaData(
+      const std::vector<DropData::Metadata>& metadata,
+      const gfx::Point& client_pt,
+      const gfx::Point& screen_pt,
+      blink::WebDragOperationsMask operations_allowed,
+      int key_modifiers) {};
+  virtual void DragTargetDragOver(
+      const gfx::Point& client_pt,
+      const gfx::Point& screen_pt,
+      blink::WebDragOperationsMask operations_allowed,
+      int key_modifiers) {}
+  virtual void DragTargetDragLeave() {}
+  virtual void DragTargetDrop(const DropData& drop_data,
+                              const gfx::Point& client_pt,
+                              const gfx::Point& screen_pt,
+                              int key_modifiers) {}
+
+  // Filters drop data before it is passed to RenderWidgetHost.
+  virtual void FilterDropData(DropData* drop_data) {}
 };
 
 }  // namespace content
diff --git a/content/public/browser/web_contents_delegate.cc b/content/public/browser/web_contents_delegate.cc
index 050f1a7..b86ec61 100644
--- a/content/public/browser/web_contents_delegate.cc
+++ b/content/public/browser/web_contents_delegate.cc
@@ -45,11 +45,12 @@
   return false;
 }
 
-bool WebContentsDelegate::AddMessageToConsole(WebContents* source,
-                                              int32_t level,
-                                              const base::string16& message,
-                                              int32_t line_no,
-                                              const base::string16& source_id) {
+bool WebContentsDelegate::DidAddMessageToConsole(
+    WebContents* source,
+    int32_t level,
+    const base::string16& message,
+    int32_t line_no,
+    const base::string16& source_id) {
   return false;
 }
 
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index 19a14dd5..2beb83c 100644
--- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h
@@ -188,14 +188,14 @@
   // Defaults to false.
   virtual bool ShouldPreserveAbortedURLs(WebContents* source);
 
-  // Add a message to the console. Returning true indicates that the delegate
-  // handled the message. If false is returned the default logging mechanism
-  // will be used for the message.
-  virtual bool AddMessageToConsole(WebContents* source,
-                                   int32_t level,
-                                   const base::string16& message,
-                                   int32_t line_no,
-                                   const base::string16& source_id);
+  // A message was added to the console of a frame of the page. Returning true
+  // indicates that the delegate handled the message. If false is returned the
+  // default logging mechanism will be used for the message.
+  virtual bool DidAddMessageToConsole(WebContents* source,
+                                      int32_t level,
+                                      const base::string16& message,
+                                      int32_t line_no,
+                                      const base::string16& source_id);
 
   // Tells us that we've finished firing this tab's beforeunload event.
   // The proceed bool tells us whether the user chose to proceed closing the
diff --git a/content/public/renderer/render_view_observer.cc b/content/public/renderer/render_view_observer.cc
index f90a805..305fbd7d4 100644
--- a/content/public/renderer/render_view_observer.cc
+++ b/content/public/renderer/render_view_observer.cc
@@ -14,7 +14,7 @@
   // |render_view_| can be null on unit testing or if Observe() is used.
   if (render_view_) {
     routing_id_ = render_view_->GetRoutingID();
-    // TODO(jam): bring this back DCHECK_NE(routing_id_, MSG_ROUTING_NONE);
+    // TODO(jam/nick): bring this back DCHECK_NE(routing_id_, MSG_ROUTING_NONE);
     render_view_->AddObserver(this);
   }
 }
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index b0c0766c..139301f 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -1757,7 +1757,7 @@
   message_loop_runner_->Run();
 }
 
-bool ConsoleObserverDelegate::AddMessageToConsole(
+bool ConsoleObserverDelegate::DidAddMessageToConsole(
     WebContents* source,
     int32_t level,
     const base::string16& message,
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index 2dea18e..499a8da 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -693,11 +693,11 @@
   ~ConsoleObserverDelegate() override;
 
   // WebContentsDelegate method:
-  bool AddMessageToConsole(WebContents* source,
-                           int32_t level,
-                           const base::string16& message,
-                           int32_t line_no,
-                           const base::string16& source_id) override;
+  bool DidAddMessageToConsole(WebContents* source,
+                              int32_t level,
+                              const base::string16& message,
+                              int32_t line_no,
+                              const base::string16& source_id) override;
 
   // Returns the most recent message sent to the console.
   std::string message() { return message_; }
diff --git a/content/renderer/input/input_event_filter.cc b/content/renderer/input/input_event_filter.cc
index a92e00e..063df1e 100644
--- a/content/renderer/input/input_event_filter.cc
+++ b/content/renderer/input/input_event_filter.cc
@@ -178,9 +178,10 @@
       return false;
   }
 
-  target_task_runner_->PostTask(
+  CHECK(target_task_runner_->PostTask(
       FROM_HERE, base::Bind(&InputEventFilter::ForwardToHandler, this, message,
-                            received_time));
+                            received_time)))
+      << "PostTask failed";
   return true;
 }
 
@@ -198,7 +199,9 @@
         "input",
         "InputEventFilter::ForwardToHandler::ForwardToMainListener",
         TRACE_EVENT_SCOPE_THREAD);
-    main_task_runner_->PostTask(FROM_HERE, base::Bind(main_listener_, message));
+    CHECK(main_task_runner_->PostTask(FROM_HERE,
+                                      base::Bind(main_listener_, message)))
+        << "PostTask failed";
     return;
   }
 
@@ -259,9 +262,10 @@
 }
 
 void InputEventFilter::SendMessage(std::unique_ptr<IPC::Message> message) {
-  io_task_runner_->PostTask(
+  CHECK(io_task_runner_->PostTask(
       FROM_HERE, base::Bind(&InputEventFilter::SendMessageOnIOThread, this,
-                            base::Passed(&message)));
+                            base::Passed(&message))))
+      << "PostTask failed";
 }
 
 void InputEventFilter::SendMessageOnIOThread(
@@ -271,7 +275,7 @@
   if (!sender_)
     return;  // Filter was removed.
 
-  sender_->Send(message.release());
+  CHECK(sender_->Send(message.release())) << "Send message failed";
 }
 
 void InputEventFilter::HandleEventOnMainThread(
@@ -303,9 +307,10 @@
     return;
   }
 
-  target_task_runner_->PostTask(
+  CHECK(target_task_runner_->PostTask(
       FROM_HERE,
-      base::Bind(&InputEventFilter::NeedsMainFrame, this, routing_id));
+      base::Bind(&InputEventFilter::NeedsMainFrame, this, routing_id)))
+      << "PostTask failed";
 }
 
 }  // namespace content
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 6ddfae756d..9dfaccb 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -3203,7 +3203,7 @@
     }
   }
 
-  Send(new FrameHostMsg_AddMessageToConsole(
+  Send(new FrameHostMsg_DidAddMessageToConsole(
       routing_id_, static_cast<int32_t>(log_severity), message.text,
       static_cast<int32_t>(source_line), source_name));
 }
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 670a2238..b045f2ec 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -60,7 +60,6 @@
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_constants.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/drop_data.h"
 #include "content/public/common/favicon_url.h"
 #include "content/public/common/page_importance_signals.h"
 #include "content/public/common/page_state.h"
@@ -111,7 +110,6 @@
 #include "third_party/WebKit/public/platform/FilePathConversion.h"
 #include "third_party/WebKit/public/platform/URLConversion.h"
 #include "third_party/WebKit/public/platform/WebConnectionType.h"
-#include "third_party/WebKit/public/platform/WebDragData.h"
 #include "third_party/WebKit/public/platform/WebHTTPBody.h"
 #include "third_party/WebKit/public/platform/WebImage.h"
 #include "third_party/WebKit/public/platform/WebMessagePortChannel.h"
@@ -162,7 +160,6 @@
 #include "third_party/WebKit/public/web/WebWindowFeatures.h"
 #include "third_party/icu/source/common/unicode/uchar.h"
 #include "third_party/icu/source/common/unicode/uscript.h"
-#include "ui/base/clipboard/clipboard.h"
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/events/latency_info.h"
 #include "ui/gfx/geometry/point.h"
@@ -207,9 +204,7 @@
 using blink::WebData;
 using blink::WebDataSource;
 using blink::WebDocument;
-using blink::WebDragData;
 using blink::WebDragOperation;
-using blink::WebDragOperationsMask;
 using blink::WebElement;
 using blink::WebFileChooserCompletion;
 using blink::WebFormControlElement;
@@ -379,128 +374,6 @@
 
 namespace {
 
-WebDragData DropMetaDataToWebDragData(
-    const std::vector<DropData::Metadata>& drop_meta_data) {
-  std::vector<WebDragData::Item> item_list;
-  for (const auto& meta_data_item : drop_meta_data) {
-    if (meta_data_item.kind == DropData::Kind::STRING) {
-      WebDragData::Item item;
-      item.storageType = WebDragData::Item::StorageTypeString;
-      item.stringType = meta_data_item.mime_type;
-      // Have to pass a dummy URL here instead of an empty URL because the
-      // DropData received by browser_plugins goes through a round trip:
-      // DropData::MetaData --> WebDragData-->DropData. In the end, DropData
-      // will contain an empty URL (which means no URL is dragged) if the URL in
-      // WebDragData is empty.
-      if (base::EqualsASCII(meta_data_item.mime_type,
-                            ui::Clipboard::kMimeTypeURIList)) {
-        item.stringData = WebString::fromUTF8("about:dragdrop-placeholder");
-      }
-      item_list.push_back(item);
-      continue;
-    }
-
-    // TODO(hush): crbug.com/584789. Blink needs to support creating a file with
-    // just the mimetype. This is needed to drag files to WebView on Android
-    // platform.
-    if ((meta_data_item.kind == DropData::Kind::FILENAME) &&
-        !meta_data_item.filename.empty()) {
-      WebDragData::Item item;
-      item.storageType = WebDragData::Item::StorageTypeFilename;
-      item.filenameData = meta_data_item.filename.AsUTF16Unsafe();
-      item_list.push_back(item);
-      continue;
-    }
-
-    if (meta_data_item.kind == DropData::Kind::FILESYSTEMFILE) {
-      WebDragData::Item item;
-      item.storageType = WebDragData::Item::StorageTypeFileSystemFile;
-      item.fileSystemURL = meta_data_item.file_system_url;
-      item_list.push_back(item);
-      continue;
-    }
-  }
-
-  WebDragData result;
-  result.initialize();
-  result.setItems(item_list);
-  return result;
-}
-
-WebDragData DropDataToWebDragData(const DropData& drop_data) {
-  std::vector<WebDragData::Item> item_list;
-
-  // These fields are currently unused when dragging into WebKit.
-  DCHECK(drop_data.download_metadata.empty());
-  DCHECK(drop_data.file_contents.empty());
-  DCHECK(drop_data.file_description_filename.empty());
-
-  if (!drop_data.text.is_null()) {
-    WebDragData::Item item;
-    item.storageType = WebDragData::Item::StorageTypeString;
-    item.stringType = WebString::fromUTF8(ui::Clipboard::kMimeTypeText);
-    item.stringData = drop_data.text.string();
-    item_list.push_back(item);
-  }
-
-  if (!drop_data.url.is_empty()) {
-    WebDragData::Item item;
-    item.storageType = WebDragData::Item::StorageTypeString;
-    item.stringType = WebString::fromUTF8(ui::Clipboard::kMimeTypeURIList);
-    item.stringData = WebString::fromUTF8(drop_data.url.spec());
-    item.title = drop_data.url_title;
-    item_list.push_back(item);
-  }
-
-  if (!drop_data.html.is_null()) {
-    WebDragData::Item item;
-    item.storageType = WebDragData::Item::StorageTypeString;
-    item.stringType = WebString::fromUTF8(ui::Clipboard::kMimeTypeHTML);
-    item.stringData = drop_data.html.string();
-    item.baseURL = drop_data.html_base_url;
-    item_list.push_back(item);
-  }
-
-  for (std::vector<ui::FileInfo>::const_iterator it =
-           drop_data.filenames.begin();
-       it != drop_data.filenames.end();
-       ++it) {
-    WebDragData::Item item;
-    item.storageType = WebDragData::Item::StorageTypeFilename;
-    item.filenameData = it->path.AsUTF16Unsafe();
-    item.displayNameData = it->display_name.AsUTF16Unsafe();
-    item_list.push_back(item);
-  }
-
-  for (std::vector<DropData::FileSystemFileInfo>::const_iterator it =
-           drop_data.file_system_files.begin();
-       it != drop_data.file_system_files.end();
-       ++it) {
-    WebDragData::Item item;
-    item.storageType = WebDragData::Item::StorageTypeFileSystemFile;
-    item.fileSystemURL = it->url;
-    item.fileSystemFileSize = it->size;
-    item_list.push_back(item);
-  }
-
-  for (std::map<base::string16, base::string16>::const_iterator it =
-           drop_data.custom_data.begin();
-       it != drop_data.custom_data.end();
-       ++it) {
-    WebDragData::Item item;
-    item.storageType = WebDragData::Item::StorageTypeString;
-    item.stringType = it->first;
-    item.stringData = it->second;
-    item_list.push_back(item);
-  }
-
-  WebDragData result;
-  result.initialize();
-  result.setItems(item_list);
-  result.setFilesystemId(drop_data.filesystem_id);
-  return result;
-}
-
 typedef void (*SetFontFamilyWrapper)(blink::WebSettings*,
                                      const base::string16&,
                                      UScriptCode);
@@ -697,21 +570,20 @@
 
 void RenderViewImpl::Initialize(const mojom::CreateViewParams& params,
                                 bool was_created_by_renderer) {
-  SetRoutingID(params.view_id);
+  RenderWidget::InitRoutingID(params.view_id);
 
-  int opener_view_routing_id;
+  int opener_view_routing_id = MSG_ROUTING_NONE;
   WebFrame* opener_frame = RenderFrameImpl::ResolveOpener(
       params.opener_frame_route_id, &opener_view_routing_id);
-  if (opener_view_routing_id != MSG_ROUTING_NONE && was_created_by_renderer)
-    opener_id_ = opener_view_routing_id;
+  if (!was_created_by_renderer)
+    opener_view_routing_id = MSG_ROUTING_NONE;
 
   display_mode_ = params.initial_size.display_mode;
 
   webview_ =
       WebView::create(this, is_hidden() ? blink::WebPageVisibilityStateHidden
                                         : blink::WebPageVisibilityStateVisible);
-  RenderWidget::DoInit(MSG_ROUTING_NONE, webview_->widget(),
-                       CreateWidgetCallback());
+  RenderWidget::Init(opener_view_routing_id, webview_->widget());
 
   g_view_map.Get().insert(std::make_pair(webview(), this));
   g_routing_id_view_map.Get().insert(std::make_pair(GetRoutingID(), this));
@@ -1316,10 +1188,6 @@
                         OnScrollFocusedEditableNodeIntoRect)
     IPC_MESSAGE_HANDLER(ViewMsg_SetPageScale, OnSetPageScale)
     IPC_MESSAGE_HANDLER(ViewMsg_Zoom, OnZoom)
-    IPC_MESSAGE_HANDLER(DragMsg_TargetDragEnter, OnDragTargetDragEnter)
-    IPC_MESSAGE_HANDLER(DragMsg_TargetDragOver, OnDragTargetDragOver)
-    IPC_MESSAGE_HANDLER(DragMsg_TargetDragLeave, OnDragTargetDragLeave)
-    IPC_MESSAGE_HANDLER(DragMsg_TargetDrop, OnDragTargetDrop)
     IPC_MESSAGE_HANDLER(DragMsg_SourceEnded, OnDragSourceEnded)
     IPC_MESSAGE_HANDLER(DragMsg_SourceSystemDragEnded,
                         OnDragSourceSystemDragEnded)
@@ -2085,13 +1953,6 @@
   return has_added_input_handler_;
 }
 
-gfx::Point RenderViewImpl::ConvertWindowPointToViewport(
-    const gfx::Point& point) {
-  blink::WebFloatRect point_in_viewport(point.x(), point.y(), 0, 0);
-  convertWindowToViewport(&point_in_viewport);
-  return gfx::Point(point_in_viewport.x, point_in_viewport.y);
-}
-
 void RenderViewImpl::didChangeIcon(WebLocalFrame* frame,
                                    WebIconURL::Type icon_type) {
   if (frame->parent())
@@ -2258,45 +2119,6 @@
     main_render_frame_->MaybeEnableMojoBindings();
 }
 
-void RenderViewImpl::OnDragTargetDragEnter(
-    const std::vector<DropData::Metadata>& drop_meta_data,
-    const gfx::Point& client_point,
-    const gfx::Point& screen_point,
-    WebDragOperationsMask ops,
-    int key_modifiers) {
-  WebDragOperation operation = webview()->dragTargetDragEnter(
-      DropMetaDataToWebDragData(drop_meta_data), client_point, screen_point,
-      ops, key_modifiers);
-
-  Send(new DragHostMsg_UpdateDragCursor(GetRoutingID(), operation));
-}
-
-void RenderViewImpl::OnDragTargetDragOver(const gfx::Point& client_point,
-                                          const gfx::Point& screen_point,
-                                          WebDragOperationsMask ops,
-                                          int key_modifiers) {
-  WebDragOperation operation = webview()->dragTargetDragOver(
-      ConvertWindowPointToViewport(client_point),
-      screen_point,
-      ops,
-      key_modifiers);
-
-  Send(new DragHostMsg_UpdateDragCursor(GetRoutingID(), operation));
-}
-
-void RenderViewImpl::OnDragTargetDragLeave() {
-  webview()->dragTargetDragLeave();
-}
-
-void RenderViewImpl::OnDragTargetDrop(const DropData& drop_data,
-                                      const gfx::Point& client_point,
-                                      const gfx::Point& screen_point,
-                                      int key_modifiers) {
-  webview()->dragTargetDrop(DropDataToWebDragData(drop_data),
-                            ConvertWindowPointToViewport(client_point),
-                            screen_point, key_modifiers);
-}
-
 void RenderViewImpl::OnDragSourceEnded(const gfx::Point& client_point,
                                        const gfx::Point& screen_point,
                                        WebDragOperation op) {
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index 85695d0..190effc4 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -381,8 +381,6 @@
   gfx::RectF ElementBoundsInWindow(const blink::WebElement& element) override;
   bool HasAddedInputHandler() const override;
 
-  gfx::Point ConvertWindowPointToViewport(const gfx::Point& point);
-
   bool uses_temporary_zoom_level() const { return uses_temporary_zoom_level_; }
 
   // Please do not add your stuff randomly to the end here. If there is an
@@ -536,22 +534,6 @@
                          const gfx::Point& screen_point,
                          blink::WebDragOperation drag_operation);
   void OnDragSourceSystemDragEnded();
-  void OnDragTargetDrop(const DropData& drop_data,
-                        const gfx::Point& client_pt,
-                        const gfx::Point& screen_pt,
-                        int key_modifiers);
-  // Real data that is dragged is not included at DragEnter time.
-  void OnDragTargetDragEnter(
-      const std::vector<DropData::Metadata>& drop_meta_data,
-      const gfx::Point& client_pt,
-      const gfx::Point& screen_pt,
-      blink::WebDragOperationsMask operations_allowed,
-      int key_modifiers);
-  void OnDragTargetDragLeave();
-  void OnDragTargetDragOver(const gfx::Point& client_pt,
-                            const gfx::Point& screen_pt,
-                            blink::WebDragOperationsMask operations_allowed,
-                            int key_modifiers);
   void OnEnablePreferredSizeChangedMode();
   void OnEnableAutoResize(const gfx::Size& min_size, const gfx::Size& max_size);
   void OnDisableAutoResize(const gfx::Size& new_size);
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 471efa2..690331e 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -38,6 +38,7 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/context_menu_params.h"
+#include "content/public/common/drop_data.h"
 #include "content/renderer/cursor_utils.h"
 #include "content/renderer/devtools/render_widget_screen_metrics_emulator.h"
 #include "content/renderer/drop_data_builder.h"
@@ -60,6 +61,8 @@
 #include "ipc/ipc_sync_message.h"
 #include "skia/ext/platform_canvas.h"
 #include "third_party/WebKit/public/platform/WebCursorInfo.h"
+#include "third_party/WebKit/public/platform/WebDragData.h"
+#include "third_party/WebKit/public/platform/WebDragOperation.h"
 #include "third_party/WebKit/public/platform/WebPoint.h"
 #include "third_party/WebKit/public/platform/WebRect.h"
 #include "third_party/WebKit/public/platform/WebSize.h"
@@ -78,6 +81,7 @@
 #include "third_party/WebKit/public/web/WebView.h"
 #include "third_party/WebKit/public/web/WebWidget.h"
 #include "third_party/skia/include/core/SkShader.h"
+#include "ui/base/clipboard/clipboard.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/rect_conversions.h"
@@ -107,8 +111,10 @@
 using blink::WebCompositionUnderline;
 using blink::WebCursorInfo;
 using blink::WebDeviceEmulationParams;
+using blink::WebDragOperation;
 using blink::WebDragOperationsMask;
 using blink::WebDragData;
+using blink::WebFrameWidget;
 using blink::WebGestureEvent;
 using blink::WebImage;
 using blink::WebInputEvent;
@@ -126,12 +132,15 @@
 using blink::WebRange;
 using blink::WebRect;
 using blink::WebSize;
+using blink::WebString;
 using blink::WebTextDirection;
 using blink::WebTouchEvent;
 using blink::WebTouchPoint;
 using blink::WebVector;
 using blink::WebWidget;
 
+namespace content {
+
 namespace {
 
 typedef std::map<std::string, ui::TextInputMode> TextInputModeMap;
@@ -183,6 +192,128 @@
   return widget;
 }
 
+WebDragData DropMetaDataToWebDragData(
+    const std::vector<DropData::Metadata>& drop_meta_data) {
+  std::vector<WebDragData::Item> item_list;
+  for (const auto& meta_data_item : drop_meta_data) {
+    if (meta_data_item.kind == DropData::Kind::STRING) {
+      WebDragData::Item item;
+      item.storageType = WebDragData::Item::StorageTypeString;
+      item.stringType = meta_data_item.mime_type;
+      // Have to pass a dummy URL here instead of an empty URL because the
+      // DropData received by browser_plugins goes through a round trip:
+      // DropData::MetaData --> WebDragData-->DropData. In the end, DropData
+      // will contain an empty URL (which means no URL is dragged) if the URL in
+      // WebDragData is empty.
+      if (base::EqualsASCII(meta_data_item.mime_type,
+                            ui::Clipboard::kMimeTypeURIList)) {
+        item.stringData = WebString::fromUTF8("about:dragdrop-placeholder");
+      }
+      item_list.push_back(item);
+      continue;
+    }
+
+    // TODO(hush): crbug.com/584789. Blink needs to support creating a file with
+    // just the mimetype. This is needed to drag files to WebView on Android
+    // platform.
+    if ((meta_data_item.kind == DropData::Kind::FILENAME) &&
+        !meta_data_item.filename.empty()) {
+      WebDragData::Item item;
+      item.storageType = WebDragData::Item::StorageTypeFilename;
+      item.filenameData = meta_data_item.filename.AsUTF16Unsafe();
+      item_list.push_back(item);
+      continue;
+    }
+
+    if (meta_data_item.kind == DropData::Kind::FILESYSTEMFILE) {
+      WebDragData::Item item;
+      item.storageType = WebDragData::Item::StorageTypeFileSystemFile;
+      item.fileSystemURL = meta_data_item.file_system_url;
+      item_list.push_back(item);
+      continue;
+    }
+  }
+
+  WebDragData result;
+  result.initialize();
+  result.setItems(item_list);
+  return result;
+}
+
+WebDragData DropDataToWebDragData(const DropData& drop_data) {
+  std::vector<WebDragData::Item> item_list;
+
+  // These fields are currently unused when dragging into WebKit.
+  DCHECK(drop_data.download_metadata.empty());
+  DCHECK(drop_data.file_contents.empty());
+  DCHECK(drop_data.file_description_filename.empty());
+
+  if (!drop_data.text.is_null()) {
+    WebDragData::Item item;
+    item.storageType = WebDragData::Item::StorageTypeString;
+    item.stringType = WebString::fromUTF8(ui::Clipboard::kMimeTypeText);
+    item.stringData = drop_data.text.string();
+    item_list.push_back(item);
+  }
+
+  if (!drop_data.url.is_empty()) {
+    WebDragData::Item item;
+    item.storageType = WebDragData::Item::StorageTypeString;
+    item.stringType = WebString::fromUTF8(ui::Clipboard::kMimeTypeURIList);
+    item.stringData = WebString::fromUTF8(drop_data.url.spec());
+    item.title = drop_data.url_title;
+    item_list.push_back(item);
+  }
+
+  if (!drop_data.html.is_null()) {
+    WebDragData::Item item;
+    item.storageType = WebDragData::Item::StorageTypeString;
+    item.stringType = WebString::fromUTF8(ui::Clipboard::kMimeTypeHTML);
+    item.stringData = drop_data.html.string();
+    item.baseURL = drop_data.html_base_url;
+    item_list.push_back(item);
+  }
+
+  for (std::vector<ui::FileInfo>::const_iterator it =
+           drop_data.filenames.begin();
+       it != drop_data.filenames.end();
+       ++it) {
+    WebDragData::Item item;
+    item.storageType = WebDragData::Item::StorageTypeFilename;
+    item.filenameData = it->path.AsUTF16Unsafe();
+    item.displayNameData = it->display_name.AsUTF16Unsafe();
+    item_list.push_back(item);
+  }
+
+  for (std::vector<DropData::FileSystemFileInfo>::const_iterator it =
+           drop_data.file_system_files.begin();
+       it != drop_data.file_system_files.end();
+       ++it) {
+    WebDragData::Item item;
+    item.storageType = WebDragData::Item::StorageTypeFileSystemFile;
+    item.fileSystemURL = it->url;
+    item.fileSystemFileSize = it->size;
+    item_list.push_back(item);
+  }
+
+  for (std::map<base::string16, base::string16>::const_iterator it =
+           drop_data.custom_data.begin();
+       it != drop_data.custom_data.end();
+       ++it) {
+    WebDragData::Item item;
+    item.storageType = WebDragData::Item::StorageTypeString;
+    item.stringType = it->first;
+    item.stringData = it->second;
+    item_list.push_back(item);
+  }
+
+  WebDragData result;
+  result.initialize();
+  result.setItems(item_list);
+  result.setFilesystemId(drop_data.filesystem_id);
+  return result;
+}
+
 content::RenderWidget::CreateRenderWidgetFunction g_create_render_widget =
     nullptr;
 
@@ -205,8 +336,6 @@
 
 }  // namespace
 
-namespace content {
-
 // RenderWidget ---------------------------------------------------------------
 
 RenderWidget::RenderWidget(CompositorDependencies* compositor_deps,
@@ -295,26 +424,34 @@
                                    blink::WebPopupType popup_type,
                                    const ScreenInfo& screen_info) {
   DCHECK(opener_id != MSG_ROUTING_NONE);
+
+  // Do a synchronous IPC to obtain a routing ID.
+  int32_t routing_id = MSG_ROUTING_NONE;
+  if (!RenderThreadImpl::current_render_message_filter()->CreateNewWidget(
+          opener_id, popup_type, &routing_id)) {
+    return nullptr;
+  }
+
   scoped_refptr<RenderWidget> widget(new RenderWidget(
       compositor_deps, popup_type, screen_info, false, false, false));
-  if (widget->Init(opener_id)) {  // adds reference on success.
-    return widget.get();
-  }
-  return NULL;
+  widget->InitRoutingID(routing_id);
+  widget->Init(opener_id, RenderWidget::CreateWebWidget(widget.get()));
+  DCHECK(!widget->HasOneRef());  // RenderWidget::Init() adds a reference.
+  return widget.get();
 }
 
 // static
 RenderWidget* RenderWidget::CreateForFrame(
-    int routing_id,
+    int widget_routing_id,
     bool hidden,
     const ScreenInfo& screen_info,
     CompositorDependencies* compositor_deps,
     blink::WebLocalFrame* frame) {
-  CHECK_NE(routing_id, MSG_ROUTING_NONE);
+  CHECK_NE(widget_routing_id, MSG_ROUTING_NONE);
   // TODO(avi): Before RenderViewImpl has-a RenderWidget, the browser passes the
   // same routing ID for both the view routing ID and the main frame widget
   // routing ID. https://crbug.com/545684
-  RenderViewImpl* view = RenderViewImpl::FromRoutingID(routing_id);
+  RenderViewImpl* view = RenderViewImpl::FromRoutingID(widget_routing_id);
   if (view) {
     view->AttachWebFrameWidget(
         RenderWidget::CreateWebFrameWidget(view->GetWidget(), frame));
@@ -326,18 +463,16 @@
                                    screen_info, false, hidden, false)
           : new RenderWidget(compositor_deps, blink::WebPopupTypeNone,
                              screen_info, false, hidden, false));
-  widget->SetRoutingID(routing_id);
   widget->for_oopif_ = true;
-  // DoInit increments the reference count on |widget|, keeping it alive after
+  // Init increments the reference count on |widget|, keeping it alive after
   // this function returns.
-  if (widget->DoInit(MSG_ROUTING_NONE,
-                     RenderWidget::CreateWebFrameWidget(widget.get(), frame),
-                     CreateWidgetCallback())) {
-    if (g_render_widget_initialized)
-      g_render_widget_initialized(widget.get());
-    return widget.get();
-  }
-  return nullptr;
+  widget->InitRoutingID(widget_routing_id);
+  widget->Init(MSG_ROUTING_NONE,
+               RenderWidget::CreateWebFrameWidget(widget.get(), frame));
+
+  if (g_render_widget_initialized)
+    g_render_widget_initialized(widget.get());
+  return widget.get();
 }
 
 // static
@@ -369,12 +504,6 @@
   OnClose();
 }
 
-void RenderWidget::SetRoutingID(int32_t routing_id) {
-  routing_id_ = routing_id;
-  input_handler_.reset(new RenderWidgetInputHandler(
-      GetRenderWidgetInputHandlerDelegate(this), this));
-}
-
 void RenderWidget::SetSwappedOut(bool is_swapped_out) {
   // We should only toggle between states.
   DCHECK(is_swapped_out_ != is_swapped_out);
@@ -389,29 +518,16 @@
     RenderProcess::current()->AddRefProcess();
 }
 
-bool RenderWidget::CreateWidget(int32_t opener_id,
-                                blink::WebPopupType popup_type,
-                                int32_t* routing_id) {
-  RenderThreadImpl::current_render_message_filter()->CreateNewWidget(
-      opener_id, popup_type, routing_id);
-  return true;
+void RenderWidget::InitRoutingID(int32_t routing_id) {
+  DCHECK_EQ(routing_id_, MSG_ROUTING_NONE);
+  routing_id_ = routing_id;
+  input_handler_.reset(new RenderWidgetInputHandler(
+      GetRenderWidgetInputHandlerDelegate(this), this));
 }
 
-bool RenderWidget::Init(int32_t opener_id) {
-  bool success = DoInit(opener_id, RenderWidget::CreateWebWidget(this),
-      base::Bind(&RenderWidget::CreateWidget, base::Unretained(this),
-           opener_id, popup_type_, &routing_id_));
-  if (success) {
-    SetRoutingID(routing_id_);
-    return true;
-  }
-  return false;
-}
-
-bool RenderWidget::DoInit(int32_t opener_id,
-                          WebWidget* web_widget,
-                          CreateWidgetCallback create_widget_callback) {
+void RenderWidget::Init(int32_t opener_id, WebWidget* web_widget) {
   DCHECK(!webwidget_internal_);
+  DCHECK_NE(routing_id_, MSG_ROUTING_NONE);
 
   if (opener_id != MSG_ROUTING_NONE)
     opener_id_ = opener_id;
@@ -421,25 +537,14 @@
       new WebWidgetLockTarget(webwidget_internal_));
   mouse_lock_dispatcher_.reset(new RenderWidgetMouseLockDispatcher(this));
 
-  bool result = true;
-  if (!create_widget_callback.is_null())
-    result = std::move(create_widget_callback).Run();
-
-  if (result) {
-    RenderThread::Get()->AddRoute(routing_id_, this);
-    // Take a reference on behalf of the RenderThread.  This will be balanced
-    // when we receive ViewMsg_Close.
-    AddRef();
-    if (RenderThreadImpl::current()) {
-      RenderThreadImpl::current()->WidgetCreated();
-      if (is_hidden_)
-        RenderThreadImpl::current()->WidgetHidden();
-    }
-
-    return true;
-  } else {
-    // The above Send can fail when the tab is closing.
-    return false;
+  RenderThread::Get()->AddRoute(routing_id_, this);
+  // Take a reference on behalf of the RenderThread.  This will be balanced
+  // when we receive ViewMsg_Close.
+  AddRef();
+  if (RenderThreadImpl::current()) {
+    RenderThreadImpl::current()->WidgetCreated();
+    if (is_hidden_)
+      RenderThreadImpl::current()->WidgetHidden();
   }
 }
 
@@ -521,12 +626,16 @@
                         OnWaitNextFrameForTests)
     IPC_MESSAGE_HANDLER(InputMsg_RequestCompositionUpdate,
                         OnRequestCompositionUpdate)
+    IPC_MESSAGE_HANDLER(ViewMsg_HandleCompositorProto, OnHandleCompositorProto)
+    IPC_MESSAGE_HANDLER(DragMsg_TargetDragEnter, OnDragTargetDragEnter)
+    IPC_MESSAGE_HANDLER(DragMsg_TargetDragOver, OnDragTargetDragOver)
+    IPC_MESSAGE_HANDLER(DragMsg_TargetDragLeave, OnDragTargetDragLeave)
+    IPC_MESSAGE_HANDLER(DragMsg_TargetDrop, OnDragTargetDrop)
 #if defined(OS_ANDROID)
     IPC_MESSAGE_HANDLER(InputMsg_ImeEventAck, OnImeEventAck)
     IPC_MESSAGE_HANDLER(InputMsg_RequestTextInputStateUpdate,
                         OnRequestTextInputStateUpdate)
 #endif
-    IPC_MESSAGE_HANDLER(ViewMsg_HandleCompositorProto, OnHandleCompositorProto)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
   return handled;
@@ -549,6 +658,11 @@
   return RenderThread::Get()->Send(message);
 }
 
+void RenderWidget::SendOrCrash(IPC::Message* message) {
+  bool result = Send(message);
+  CHECK(closing_ || result) << "Failed to send message";
+}
+
 void RenderWidget::SetWindowRectSynchronously(
     const gfx::Rect& new_window_rect) {
   ResizeParams params;
@@ -882,7 +996,8 @@
 
 void RenderWidget::OnInputEventAck(
     std::unique_ptr<InputEventAck> input_event_ack) {
-  Send(new InputHostMsg_HandleInputEvent_ACK(routing_id_, *input_event_ack));
+  SendOrCrash(
+      new InputHostMsg_HandleInputEvent_ACK(routing_id_, *input_event_ack));
 }
 
 void RenderWidget::NotifyInputEventHandled(
@@ -1537,6 +1652,60 @@
     compositor_->OnHandleCompositorProto(proto);
 }
 
+void RenderWidget::OnDragTargetDragEnter(
+    const std::vector<DropData::Metadata>& drop_meta_data,
+    const gfx::Point& client_point,
+    const gfx::Point& screen_point,
+    WebDragOperationsMask ops,
+    int key_modifiers) {
+  if (!GetWebWidget())
+    return;
+
+  DCHECK(GetWebWidget()->isWebFrameWidget());
+  WebDragOperation operation = static_cast<WebFrameWidget*>(GetWebWidget())->
+      dragTargetDragEnter(DropMetaDataToWebDragData(drop_meta_data),
+                          client_point, screen_point, ops, key_modifiers);
+
+  Send(new DragHostMsg_UpdateDragCursor(routing_id(), operation));
+}
+
+void RenderWidget::OnDragTargetDragOver(const gfx::Point& client_point,
+                                        const gfx::Point& screen_point,
+                                        WebDragOperationsMask ops,
+                                        int key_modifiers) {
+  if (!GetWebWidget())
+    return;
+
+  DCHECK(GetWebWidget()->isWebFrameWidget());
+  WebDragOperation operation =
+      static_cast<WebFrameWidget*>(GetWebWidget())->dragTargetDragOver(
+          ConvertWindowPointToViewport(client_point),
+          screen_point, ops, key_modifiers);
+
+  Send(new DragHostMsg_UpdateDragCursor(routing_id(), operation));
+}
+
+void RenderWidget::OnDragTargetDragLeave() {
+  if (!GetWebWidget())
+    return;
+  DCHECK(GetWebWidget()->isWebFrameWidget());
+  static_cast<WebFrameWidget*>(GetWebWidget())->dragTargetDragLeave();
+}
+
+void RenderWidget::OnDragTargetDrop(const DropData& drop_data,
+                                    const gfx::Point& client_point,
+                                    const gfx::Point& screen_point,
+                                    int key_modifiers) {
+  if (!GetWebWidget())
+    return;
+
+  DCHECK(GetWebWidget()->isWebFrameWidget());
+  static_cast<WebFrameWidget*>(GetWebWidget())->dragTargetDrop(
+      DropDataToWebDragData(drop_data),
+      ConvertWindowPointToViewport(client_point),
+      screen_point, key_modifiers);
+}
+
 void RenderWidget::showImeIfNeeded() {
 #if defined(OS_ANDROID) || defined(USE_AURA)
   UpdateTextInputState(ShowIme::IF_NEEDED, ChangeSource::FROM_NON_IME);
@@ -2075,6 +2244,13 @@
       device_scale_factor_;
 }
 
+gfx::Point RenderWidget::ConvertWindowPointToViewport(
+    const gfx::Point& point) {
+  blink::WebFloatRect point_in_viewport(point.x(), point.y(), 0, 0);
+  convertWindowToViewport(&point_in_viewport);
+  return gfx::Point(point_in_viewport.x, point_in_viewport.y);
+}
+
 bool RenderWidget::requestPointerLock() {
   return mouse_lock_dispatcher_->LockMouse(webwidget_mouse_lock_target_.get());
 }
@@ -2098,7 +2274,7 @@
   DropData drop_data(DropDataBuilder::Build(data));
   drop_data.referrer_policy = policy;
   gfx::Vector2d imageOffset(offset_in_window.x, offset_in_window.y);
-  Send(new DragHostMsg_StartDragging(routing_id_, drop_data, mask,
+  Send(new DragHostMsg_StartDragging(routing_id(), drop_data, mask,
                                      image.getSkBitmap(), imageOffset,
                                      possible_drag_event_info_));
 }
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index dc1b377..1cfacff 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -25,6 +25,7 @@
 #include "content/common/drag_event_source_info.h"
 #include "content/common/edit_command.h"
 #include "content/common/input/synthetic_gesture_params.h"
+#include "content/public/common/drop_data.h"
 #include "content/public/common/screen_info.h"
 #include "content/renderer/devtools/render_widget_screen_metrics_emulator_delegate.h"
 #include "content/renderer/gpu/render_widget_compositor_delegate.h"
@@ -34,6 +35,7 @@
 #include "content/renderer/mouse_lock_dispatcher.h"
 #include "content/renderer/render_widget_mouse_lock_dispatcher.h"
 #include "ipc/ipc_listener.h"
+#include "ipc/ipc_message.h"
 #include "ipc/ipc_sender.h"
 #include "third_party/WebKit/public/platform/WebDisplayMode.h"
 #include "third_party/WebKit/public/platform/WebInputEvent.h"
@@ -133,7 +135,7 @@
                               const ScreenInfo& screen_info);
 
   // Creates a new RenderWidget that will be attached to a RenderFrame.
-  static RenderWidget* CreateForFrame(int routing_id,
+  static RenderWidget* CreateForFrame(int widget_routing_id,
                                       bool hidden,
                                       const ScreenInfo& screen_info,
                                       CompositorDependencies* compositor_deps,
@@ -158,8 +160,9 @@
   // https://crbug.com/545684
   virtual void CloseForFrame();
 
-  int32_t routing_id() const { return routing_id_; }
-  void SetRoutingID(int32_t routing_id);
+  int32_t routing_id() const {
+    return routing_id_;
+  }
 
   CompositorDependencies* compositor_deps() const { return compositor_deps_; }
   virtual blink::WebWidget* GetWebWidget() const;
@@ -411,6 +414,9 @@
   // When emulated, this returns original device scale factor.
   float GetOriginalDeviceScaleFactor() const;
 
+  // Helper to convert |point| using ConvertWindowToViewport().
+  gfx::Point ConvertWindowPointToViewport(const gfx::Point& point);
+
  protected:
   // Friend RefCounted so that the dtor can be non-public. Using this class
   // without ref-counting is an error.
@@ -419,8 +425,6 @@
   // For unit tests.
   friend class RenderWidgetTest;
 
-  using CreateWidgetCallback = base::OnceCallback<bool()>;
-
   enum ResizeAck {
     SEND_RESIZE_ACK,
     NO_RESIZE_ACK,
@@ -442,13 +446,12 @@
   // Creates a WebWidget based on the popup type.
   static blink::WebWidget* CreateWebWidget(RenderWidget* render_widget);
 
-  // Initializes this view with the given opener.
-  bool Init(int32_t opener_id);
+  // Called by Create() functions and subclasses, after the routing_id is
+  // available. Must be called before Init().
+  void InitRoutingID(int32_t routing_id);
 
-  // Called by Init and subclasses to perform initialization.
-  bool DoInit(int32_t opener_id,
-              blink::WebWidget* web_widget,
-              CreateWidgetCallback create_widget_callback);
+  // Called by Create() functions and subclasses to finish initialization.
+  void Init(int32_t opener_id, blink::WebWidget* web_widget);
 
   // Allows the process to exit once the unload handler has finished, if there
   // are no other active RenderWidgets.
@@ -513,6 +516,22 @@
                            const gfx::Rect& window_screen_rect);
   void OnUpdateWindowScreenRect(const gfx::Rect& window_screen_rect);
   void OnHandleCompositorProto(const std::vector<uint8_t>& proto);
+  // Real data that is dragged is not included at DragEnter time.
+  void OnDragTargetDragEnter(
+      const std::vector<DropData::Metadata>& drop_meta_data,
+      const gfx::Point& client_pt,
+      const gfx::Point& screen_pt,
+      blink::WebDragOperationsMask operations_allowed,
+      int key_modifiers);
+  void OnDragTargetDragOver(const gfx::Point& client_pt,
+                            const gfx::Point& screen_pt,
+                            blink::WebDragOperationsMask operations_allowed,
+                            int key_modifiers);
+  void OnDragTargetDragLeave();
+  void OnDragTargetDrop(const DropData& drop_data,
+                        const gfx::Point& client_pt,
+                        const gfx::Point& screen_pt,
+                        int key_modifiers);
 
 #if defined(OS_ANDROID)
   // Called when we send IME event that expects an ACK.
@@ -812,6 +831,13 @@
                     blink::WebPopupType popup_type,
                     int32_t* routing_id);
 
+  // A variant of Send but is fatal if it fails. The browser may
+  // be waiting for this IPC Message and if the send fails the browser will
+  // be left in a state waiting for something that never comes. And if it
+  // never comes then it may later determine this is a hung renderer; so
+  // instead fail right away.
+  void SendOrCrash(IPC::Message* msg);
+
   // Indicates whether this widget has focus.
   bool has_focus_;
 
diff --git a/content/renderer/render_widget_fullscreen.cc b/content/renderer/render_widget_fullscreen.cc
index 8576a00..4c49439 100644
--- a/content/renderer/render_widget_fullscreen.cc
+++ b/content/renderer/render_widget_fullscreen.cc
@@ -41,28 +41,19 @@
   return RenderWidget::CreateWebWidget(this);
 }
 
-bool RenderWidgetFullscreen::CreateFullscreenWidget(int32_t opener_id,
-                                                    int32_t* routing_id) {
-  RenderThreadImpl::current_render_message_filter()->CreateFullscreenWidget(
-      opener_id, routing_id);
-  return true;
-}
-
 bool RenderWidgetFullscreen::Init(int32_t opener_id) {
   DCHECK(!GetWebWidget());
 
-  bool success = RenderWidget::DoInit(
-      opener_id, CreateWebWidget(),
-      base::Bind(&RenderWidgetFullscreen::CreateFullscreenWidget,
-          base::Unretained(this), opener_id, &routing_id_));
-
-  if (success) {
-    // TODO(fsamuel): This is a bit ugly. The |create_widget_message| should
-    // probably be factored out of RenderWidget::DoInit.
-    SetRoutingID(routing_id_);
-    return true;
+  // Synchronous IPC to obtain a routing id for ourselves.
+  int32_t routing_id = MSG_ROUTING_NONE;
+  if (!RenderThreadImpl::current_render_message_filter()
+           ->CreateFullscreenWidget(opener_id, &routing_id)) {
+    return false;
   }
-  return false;
+
+  RenderWidget::InitRoutingID(routing_id);
+  RenderWidget::Init(opener_id, CreateWebWidget());
+  return true;
 }
 
 }  // namespace content
diff --git a/content/renderer/render_widget_fullscreen.h b/content/renderer/render_widget_fullscreen.h
index 46346f9..a73f9db 100644
--- a/content/renderer/render_widget_fullscreen.h
+++ b/content/renderer/render_widget_fullscreen.h
@@ -27,9 +27,6 @@
   virtual blink::WebWidget* CreateWebWidget();
 
   bool Init(int32_t opener_id);
-
- private:
-  bool CreateFullscreenWidget(int32_t opener_id, int32_t* routing_id);
 };
 
 }  // namespace content
diff --git a/content/renderer/render_widget_unittest.cc b/content/renderer/render_widget_unittest.cc
index e9efceed..1cc29f4f 100644
--- a/content/renderer/render_widget_unittest.cc
+++ b/content/renderer/render_widget_unittest.cc
@@ -61,8 +61,7 @@
                      false),
         always_overscroll_(false) {
     webwidget_internal_ = &mock_webwidget_;
-    // A RenderWidget is not fully initialized until it has a routing ID.
-    SetRoutingID(++next_routing_id_);
+    InitRoutingID(++next_routing_id_);
   }
 
   void SetTouchRegion(const std::vector<gfx::Rect>& rects) {
@@ -378,8 +377,7 @@
                      false,
                      false) {
     webwidget_internal_ = &mock_webwidget_;
-    // A RenderWidget is not fully initialized until it has a routing ID.
-    SetRoutingID(1);
+    InitRoutingID(1);
     did_show_ = true;
   }
 
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index df332c7..12a46c48 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -324,6 +324,7 @@
 
   if (use_aura) {
     deps += [
+      "//services/navigation",
       "//ui/aura",
       "//ui/aura:test_support",
       "//ui/events",
diff --git a/content/shell/browser/DEPS b/content/shell/browser/DEPS
index d6be83d..d31c2b2 100644
--- a/content/shell/browser/DEPS
+++ b/content/shell/browser/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "-content/shell/browser/layout_test",
   "+device/geolocation",
+  "+services/navigation",
   "+services/service_manager/public/cpp",
 ]
 
diff --git a/content/shell/browser/shell.cc b/content/shell/browser/shell.cc
index 9d74a1c..f4178709 100644
--- a/content/shell/browser/shell.cc
+++ b/content/shell/browser/shell.cc
@@ -460,11 +460,11 @@
   return nullptr;
 }
 
-bool Shell::AddMessageToConsole(WebContents* source,
-                                int32_t level,
-                                const base::string16& message,
-                                int32_t line_no,
-                                const base::string16& source_id) {
+bool Shell::DidAddMessageToConsole(WebContents* source,
+                                   int32_t level,
+                                   const base::string16& message,
+                                   int32_t line_no,
+                                   const base::string16& source_id) {
   return switches::IsRunLayoutTestSwitchPresent();
 }
 
diff --git a/content/shell/browser/shell.h b/content/shell/browser/shell.h
index 6f36810..545af23 100644
--- a/content/shell/browser/shell.h
+++ b/content/shell/browser/shell.h
@@ -158,11 +158,11 @@
   void HandleKeyboardEvent(WebContents* source,
                            const NativeWebKeyboardEvent& event) override;
 #endif
-  bool AddMessageToConsole(WebContents* source,
-                           int32_t level,
-                           const base::string16& message,
-                           int32_t line_no,
-                           const base::string16& source_id) override;
+  bool DidAddMessageToConsole(WebContents* source,
+                              int32_t level,
+                              const base::string16& message,
+                              int32_t line_no,
+                              const base::string16& source_id) override;
   void RendererUnresponsive(
       WebContents* source,
       const WebContentsUnresponsiveState& unresponsive_state) override;
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index e5475cd..7c7613f 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -66,6 +66,10 @@
 #include "media/mojo/services/media_service_factory.h"  // nogncheck
 #endif
 
+#if defined(USE_AURA)
+#include "services/navigation/navigation.h"
+#endif
+
 namespace content {
 
 namespace {
@@ -193,9 +197,18 @@
 void ShellContentBrowserClient::RegisterInProcessServices(
     StaticServiceMap* services) {
 #if (ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS)
-  content::ServiceInfo info;
-  info.factory = base::Bind(&media::CreateMediaServiceForTesting);
-  services->insert(std::make_pair("service:media", info));
+  {
+    content::ServiceInfo info;
+    info.factory = base::Bind(&media::CreateMediaServiceForTesting);
+    services->insert(std::make_pair("service:media", info));
+  }
+#endif
+#if defined(USE_AURA)
+  {
+    content::ServiceInfo info;
+    info.factory = base::Bind(&navigation::CreateNavigationService);
+    services->insert(std::make_pair("service:navigation", info));
+  }
 #endif
 }
 
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
index 82a7dfb..82088cd 100644
--- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -131,10 +131,6 @@
 
     # Mac only.
 
-    # Fixed on OSX 10.11
-    self.Fail('deqp/functional/gles3/shaderoperator/common_functions.html',
-        ['mac', 'amd'], bug=483282)
-
     self.Fail('conformance2/rendering/' +
         'framebuffer-completeness-unaffected.html',
         ['mac'], bug=630800)
@@ -143,9 +139,6 @@
     # self.Fail('deqp/functional/gles3/fbocompleteness.html',
     #     ['mac', ('nvidia', 0xfe9)], bug=616562)
 
-    self.Fail('deqp/data/gles3/shaders/linkage.html',
-        ['mac'], bug=641129)
-
     self.Fail('conformance2/renderbuffers/framebuffer-test.html',
         ['mac'], bug=641149)
 
@@ -202,9 +195,6 @@
     self.Fail('deqp/functional/gles3/shaderindexing/varying.html',
         ['mac', ('nvidia', 0xfe9)], bug=483282)
     self.Fail('deqp/functional/gles3/texturespecification/' +
-        'basic_copyteximage2d.html',
-        ['mac', ('nvidia', 0xfe9)], bug=620067)
-    self.Fail('deqp/functional/gles3/texturespecification/' +
         'teximage2d_pbo_2d_00.html',
         ['mac', ('nvidia', 0xfe9)], bug=614174)
     self.Fail('deqp/functional/gles3/texturespecification/' +
@@ -310,79 +300,73 @@
     self.Fail('deqp/functional/gles3/primitiverestart/01.html',
         ['mac', 'amd'], bug=598930)
     self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'array_interleaved_lines.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'array_interleaved_points.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'array_interleaved_triangles.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'array_separate_lines.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'array_separate_points.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'array_separate_triangles.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'basic_types_interleaved_lines.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'basic_types_interleaved_points.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'basic_types_interleaved_triangles.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'basic_types_separate_lines.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'basic_types_separate_points.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'basic_types_separate_triangles.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'interpolation_centroid.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'interpolation_flat.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'interpolation_smooth.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'point_size.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'position.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'random_interleaved_lines.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'random_interleaved_points.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'random_interleaved_triangles.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'random_separate_lines.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'random_separate_points.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/transformfeedback/' +
-              'random_separate_triangles.html',
-              ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/shadertexturefunction/' +
-        'textureprojlodoffset.html',
+        'array_interleaved_lines.html',
         ['mac', 'amd'], bug=483282)
-    self.Fail('deqp/functional/gles3/shadertexturefunction/' +
-        'textureprojlod.html',
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'array_interleaved_points.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'array_interleaved_triangles.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'array_separate_lines.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'array_separate_points.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'array_separate_triangles.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'basic_types_interleaved_lines.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'basic_types_interleaved_points.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'basic_types_interleaved_triangles.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'basic_types_separate_lines.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'basic_types_separate_points.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'basic_types_separate_triangles.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'interpolation_centroid.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'interpolation_flat.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'interpolation_smooth.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'point_size.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'position.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'random_interleaved_lines.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'random_interleaved_points.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'random_interleaved_triangles.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'random_separate_lines.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'random_separate_points.html',
+        ['mac', 'amd'], bug=483282)
+    self.Fail('deqp/functional/gles3/transformfeedback/' +
+        'random_separate_triangles.html',
         ['mac', 'amd'], bug=483282)
     self.Fail('deqp/functional/gles3/vertexarrays/' +
         'single_attribute.normalize.html',
@@ -397,10 +381,6 @@
     self.Fail('deqp/functional/gles3/shaderoperator/geometric.html',
         ['mac', 'amd'], bug=483282)
 
-    # Fixed on OSX 10.12
-    self.Fail('deqp/functional/gles3/uniformbuffers/random.html',
-        ['mac', 'amd'], bug=618464)
-
     self.Flaky('deqp/functional/gles3/shaderindexing/mat_02.html',
         ['mac', 'amd'], bug=644360)
 
@@ -414,6 +394,10 @@
     self.Fail('deqp/functional/gles3/shadertexturefunction/' +
         'texturesize.html',
         ['mac', ('amd', 0x679e)], bug=640506)
+    self.Fail('deqp/functional/gles3/uniformbuffers/random.html',
+        ['mac', ('amd', 0x679e)], bug=618464)
+    self.Fail('deqp/functional/gles3/shaderoperator/common_functions.html',
+        ['mac', ('amd', 0x679e)], bug=483282)
 
     # Mac Intel
     self.Fail('deqp/functional/gles3/fbomultisample*',
diff --git a/device/vr/BUILD.gn b/device/vr/BUILD.gn
index d07f626..bf2372b 100644
--- a/device/vr/BUILD.gn
+++ b/device/vr/BUILD.gn
@@ -9,67 +9,70 @@
   import("//build/config/android/rules.gni")  # For generate_jni().
 }
 
-component("vr") {
-  output_name = "device_vr"
+if (current_cpu == "arm" || current_cpu == "arm64") {
+  component("vr") {
+    output_name = "device_vr"
 
-  sources = [
-    "vr_device.cc",
-    "vr_device.h",
-    "vr_device_manager.cc",
-    "vr_device_manager.h",
-    "vr_device_provider.h",
-    "vr_display_impl.cc",
-    "vr_display_impl.h",
-    "vr_service_impl.cc",
-    "vr_service_impl.h",
-  ]
-
-  deps = [
-    ":mojo_bindings",
-    "//base",
-    "//mojo/public/cpp/bindings",
-    "//ui/gfx",
-  ]
-
-  defines = [ "DEVICE_VR_IMPLEMENTATION" ]
-
-  if (is_android) {
-    sources += [
-      "android/gvr/gvr_delegate.cc",
-      "android/gvr/gvr_delegate.h",
-      "android/gvr/gvr_device.cc",
-      "android/gvr/gvr_device.h",
-      "android/gvr/gvr_device_provider.cc",
-      "android/gvr/gvr_device_provider.h",
-      "android/gvr/gvr_gamepad_data_fetcher.cc",
-      "android/gvr/gvr_gamepad_data_fetcher.h",
+    sources = [
+      "vr_device.cc",
+      "vr_device.h",
+      "vr_device_manager.cc",
+      "vr_device_manager.h",
+      "vr_device_provider.h",
+      "vr_display_impl.cc",
+      "vr_display_impl.h",
+      "vr_service_impl.cc",
+      "vr_service_impl.h",
     ]
 
-    deps += [
-      "//device/gamepad",
-      "//third_party/WebKit/public:blink_headers",
-      "//third_party/gvr-android-sdk:libgvr",
+    deps = [
+      ":mojo_bindings",
+      "//base",
+      "//mojo/public/cpp/bindings",
+      "//ui/gfx",
     ]
-    configs += [ "//third_party/gvr-android-sdk:libgvr_config" ]
+
+    defines = [ "DEVICE_VR_IMPLEMENTATION" ]
+
+    if (is_android) {
+      sources += [
+        "android/gvr/gvr_delegate.cc",
+        "android/gvr/gvr_delegate.h",
+        "android/gvr/gvr_device.cc",
+        "android/gvr/gvr_device.h",
+        "android/gvr/gvr_device_provider.cc",
+        "android/gvr/gvr_device_provider.h",
+        "android/gvr/gvr_gamepad_data_fetcher.cc",
+        "android/gvr/gvr_gamepad_data_fetcher.h",
+      ]
+
+      deps += [
+        "//device/gamepad",
+        "//third_party/WebKit/public:blink_headers",
+      ]
+      ldflags = [ "-landroid" ]
+      libs = [ "//third_party/gvr-android-sdk/libgvr_shim_static_${current_cpu}.a" ]
+      configs += [ "//third_party/gvr-android-sdk:libgvr_config" ]
+    }
   }
-}
 
-static_library("fakes") {
-  testonly = true
+  static_library("fakes") {
+    testonly = true
 
-  sources = [
-    "test/fake_vr_device.cc",
-    "test/fake_vr_device.h",
-    "test/fake_vr_device_provider.cc",
-    "test/fake_vr_device_provider.h",
-  ]
+    sources = [
+      "test/fake_vr_device.cc",
+      "test/fake_vr_device.h",
+      "test/fake_vr_device_provider.cc",
+      "test/fake_vr_device_provider.h",
+    ]
 
-  public_deps = [
-    ":mojo_bindings",
-    ":vr",
-    "//base",
-    "//mojo/public/cpp/bindings",
-  ]
+    public_deps = [
+      ":mojo_bindings",
+      ":vr",
+      "//base",
+      "//mojo/public/cpp/bindings",
+    ]
+  }
 }
 
 mojom("mojo_bindings") {
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn
index 623a01a..cdc3b3f 100644
--- a/extensions/BUILD.gn
+++ b/extensions/BUILD.gn
@@ -1,6 +1,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//extensions/features/features.gni")
 import("//testing/test.gni")
 import("//tools/grit/grit_rule.gni")
 import("//tools/grit/repack.gni")
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index 0e56510..1fe3f5e 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/features.gni")
+import("//extensions/features/features.gni")
 
 source_set("browser") {
   sources = []
@@ -25,6 +26,7 @@
     "//extensions/browser/api:api_registration",
     "//extensions/common",
     "//extensions/common/api",
+    "//extensions/features",
     "//extensions/strings",
     "//google_apis",
     "//ui/display",
@@ -436,6 +438,7 @@
     "//extensions:test_support",
     "//extensions/common",
     "//extensions/common/api",
+    "//extensions/features",
     "//ipc:test_support",
     "//net:test_support",
     "//third_party/leveldatabase",
diff --git a/extensions/browser/api/alarms/alarm_manager.h b/extensions/browser/api/alarms/alarm_manager.h
index 7abbbf3..3978534 100644
--- a/extensions/browser/api/alarms/alarm_manager.h
+++ b/extensions/browser/api/alarms/alarm_manager.h
@@ -19,6 +19,7 @@
 #include "extensions/browser/browser_context_keyed_api_factory.h"
 #include "extensions/browser/extension_registry_observer.h"
 #include "extensions/common/api/alarms.h"
+#include "extensions/common/extension_id.h"
 
 namespace base {
 class Clock;
@@ -135,7 +136,6 @@
                            PollFrequencyFromStoredAlarm);
   friend class BrowserContextKeyedAPIFactory<AlarmManager>;
 
-  typedef std::string ExtensionId;
   typedef std::map<ExtensionId, AlarmList> AlarmMap;
 
   typedef base::Callback<void(const std::string&)> ReadyAction;
diff --git a/extensions/browser/api/declarative/declarative_rule.h b/extensions/browser/api/declarative/declarative_rule.h
index e18f2bb..cbe55e9 100644
--- a/extensions/browser/api/declarative/declarative_rule.h
+++ b/extensions/browser/api/declarative/declarative_rule.h
@@ -25,6 +25,7 @@
 #include "components/url_matcher/url_matcher.h"
 #include "extensions/common/api/events.h"
 #include "extensions/common/extension.h"
+#include "extensions/common/extension_id.h"
 
 namespace base {
 class Time;
@@ -199,7 +200,6 @@
 template<typename ConditionT, typename ActionT>
 class DeclarativeRule {
  public:
-  typedef std::string ExtensionId;
   typedef std::string RuleId;
   typedef std::pair<ExtensionId, RuleId> GlobalRuleId;
   typedef int Priority;
diff --git a/extensions/browser/api/declarative/rules_registry.h b/extensions/browser/api/declarative/rules_registry.h
index a1d4a98..99dfde2 100644
--- a/extensions/browser/api/declarative/rules_registry.h
+++ b/extensions/browser/api/declarative/rules_registry.h
@@ -22,6 +22,7 @@
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "extensions/common/api/events.h"
+#include "extensions/common/extension_id.h"
 #include "extensions/common/one_shot_event.h"
 
 namespace content {
@@ -156,7 +157,6 @@
   friend class base::RefCountedThreadSafe<RulesRegistry>;
   friend class RulesCacheDelegate;
 
-  typedef std::string ExtensionId;
   typedef std::string RuleId;
   typedef std::pair<ExtensionId, RuleId> RulesDictionaryKey;
   typedef std::map<RulesDictionaryKey, linked_ptr<api::events::Rule>>
diff --git a/extensions/browser/api/declarative_webrequest/webrequest_rules_registry.cc b/extensions/browser/api/declarative_webrequest/webrequest_rules_registry.cc
index 0907ebd..a9f370a 100644
--- a/extensions/browser/api/declarative_webrequest/webrequest_rules_registry.cc
+++ b/extensions/browser/api/declarative_webrequest/webrequest_rules_registry.cc
@@ -99,7 +99,6 @@
   // for rules of that extension. Initially, this priority is -infinite and
   // will be increased when the rules are processed and raise the bar via
   // WebRequestIgnoreRulesActions.
-  typedef std::string ExtensionId;
   typedef std::map<ExtensionId, WebRequestRule::Priority> MinPriorities;
   typedef std::map<ExtensionId, std::set<std::string> > IgnoreTags;
   MinPriorities min_priorities;
@@ -289,8 +288,8 @@
     return false;
 
   // Now all the registered rules for each extensions.
-  for (const std::pair<WebRequestRule::ExtensionId, RulesMap>&
-           extension_id_rules_map_pair : webrequest_rules_) {
+  for (const std::pair<ExtensionId, RulesMap>& extension_id_rules_map_pair :
+       webrequest_rules_) {
     if (!extension_id_rules_map_pair.second.empty())
       return false;
   }
diff --git a/extensions/browser/api/declarative_webrequest/webrequest_rules_registry.h b/extensions/browser/api/declarative_webrequest/webrequest_rules_registry.h
index 9af31c719..d1405419 100644
--- a/extensions/browser/api/declarative_webrequest/webrequest_rules_registry.h
+++ b/extensions/browser/api/declarative_webrequest/webrequest_rules_registry.h
@@ -24,6 +24,7 @@
 #include "extensions/browser/api/declarative_webrequest/webrequest_action.h"
 #include "extensions/browser/api/declarative_webrequest/webrequest_condition.h"
 #include "extensions/browser/info_map.h"
+#include "extensions/common/extension_id.h"
 
 class WebRequestPermissions;
 
@@ -182,7 +183,7 @@
   // separately.
   std::set<const WebRequestRule*> rules_with_untriggered_conditions_;
 
-  std::map<WebRequestRule::ExtensionId, RulesMap> webrequest_rules_;
+  std::map<ExtensionId, RulesMap> webrequest_rules_;
 
   url_matcher::URLMatcher url_matcher_;
 
diff --git a/extensions/browser/extension_registry.h b/extensions/browser/extension_registry.h
index 09c8a70..1100007 100644
--- a/extensions/browser/extension_registry.h
+++ b/extensions/browser/extension_registry.h
@@ -14,8 +14,9 @@
 #include "components/keyed_service/core/keyed_service.h"
 #include "extensions/browser/uninstall_reason.h"
 #include "extensions/common/extension_set.h"
+#include "extensions/features/features.h"
 
-#if !defined(ENABLE_EXTENSIONS)
+#if !BUILDFLAG(ENABLE_EXTENSIONS)
 #error "Extensions must be enabled"
 #endif
 
diff --git a/extensions/browser/extension_system.h b/extensions/browser/extension_system.h
index b318e964..8dad9af9 100644
--- a/extensions/browser/extension_system.h
+++ b/extensions/browser/extension_system.h
@@ -12,8 +12,9 @@
 #include "build/build_config.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "extensions/common/extension.h"
+#include "extensions/features/features.h"
 
-#if !defined(ENABLE_EXTENSIONS)
+#if !BUILDFLAG(ENABLE_EXTENSIONS)
 #error "Extensions must be enabled"
 #endif
 
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.cc b/extensions/browser/guest_view/web_view/web_view_guest.cc
index c9fcff6..25f3249 100644
--- a/extensions/browser/guest_view/web_view/web_view_guest.cc
+++ b/extensions/browser/guest_view/web_view/web_view_guest.cc
@@ -527,11 +527,11 @@
     GetOpener()->pending_new_windows_.erase(this);
 }
 
-bool WebViewGuest::AddMessageToConsole(WebContents* source,
-                                       int32_t level,
-                                       const base::string16& message,
-                                       int32_t line_no,
-                                       const base::string16& source_id) {
+bool WebViewGuest::DidAddMessageToConsole(WebContents* source,
+                                          int32_t level,
+                                          const base::string16& message,
+                                          int32_t line_no,
+                                          const base::string16& source_id) {
   std::unique_ptr<base::DictionaryValue> args(new base::DictionaryValue());
   // Log levels are from base/logging.h: LogSeverity.
   args->SetInteger(webview::kLevel, level);
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.h b/extensions/browser/guest_view/web_view/web_view_guest.h
index 2996529..8ce6665a 100644
--- a/extensions/browser/guest_view/web_view/web_view_guest.h
+++ b/extensions/browser/guest_view/web_view/web_view_guest.h
@@ -205,11 +205,11 @@
                const content::NotificationDetails& details) final;
 
   // WebContentsDelegate implementation.
-  bool AddMessageToConsole(content::WebContents* source,
-                           int32_t level,
-                           const base::string16& message,
-                           int32_t line_no,
-                           const base::string16& source_id) final;
+  bool DidAddMessageToConsole(content::WebContents* source,
+                              int32_t level,
+                              const base::string16& message,
+                              int32_t line_no,
+                              const base::string16& source_id) final;
   void CloseContents(content::WebContents* source) final;
   bool HandleContextMenu(const content::ContextMenuParams& params) final;
   void HandleKeyboardEvent(content::WebContents* source,
diff --git a/extensions/browser/lazy_background_task_queue.h b/extensions/browser/lazy_background_task_queue.h
index 1840cc8b..b6c532c8 100644
--- a/extensions/browser/lazy_background_task_queue.h
+++ b/extensions/browser/lazy_background_task_queue.h
@@ -18,6 +18,7 @@
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "extensions/browser/extension_registry_observer.h"
+#include "extensions/common/extension_id.h"
 
 namespace content {
 class BrowserContext;
@@ -72,8 +73,7 @@
 
   // A map between a BrowserContext/extension_id pair and the queue of tasks
   // pending the load of its background page.
-  typedef std::string ExtensionID;
-  typedef std::pair<content::BrowserContext*, ExtensionID> PendingTasksKey;
+  typedef std::pair<content::BrowserContext*, ExtensionId> PendingTasksKey;
   typedef std::vector<PendingTask> PendingTasksList;
   using PendingTasksMap =
       std::map<PendingTasksKey, std::unique_ptr<PendingTasksList>>;
diff --git a/extensions/browser/notification_types.h b/extensions/browser/notification_types.h
index b99454cf..5c63f8a 100644
--- a/extensions/browser/notification_types.h
+++ b/extensions/browser/notification_types.h
@@ -8,8 +8,9 @@
 #include <string>
 
 #include "content/public/browser/notification_types.h"
+#include "extensions/features/features.h"
 
-#if !defined(ENABLE_EXTENSIONS)
+#if !BUILDFLAG(ENABLE_EXTENSIONS)
 #error "Extensions must be enabled"
 #endif
 
diff --git a/extensions/browser/quota_service.h b/extensions/browser/quota_service.h
index ab45e08..433ea32 100644
--- a/extensions/browser/quota_service.h
+++ b/extensions/browser/quota_service.h
@@ -28,6 +28,7 @@
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "base/values.h"
+#include "extensions/common/extension_id.h"
 
 class ExtensionFunction;
 
@@ -73,7 +74,6 @@
   };
 
  private:
-  using ExtensionId = std::string;
   using FunctionName = std::string;
   // All QuotaLimitHeuristic instances in this map are owned by us.
   using FunctionHeuristicsMap = std::map<FunctionName, QuotaLimitHeuristics>;
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn
index ee0d8ac..b63e6b0 100644
--- a/extensions/common/BUILD.gn
+++ b/extensions/common/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/features.gni")
+import("//extensions/features/features.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 
 source_set("common_constants") {
diff --git a/extensions/common/api/BUILD.gn b/extensions/common/api/BUILD.gn
index dc63a7bb..d026d34f 100644
--- a/extensions/common/api/BUILD.gn
+++ b/extensions/common/api/BUILD.gn
@@ -5,6 +5,7 @@
 import("//build/config/features.gni")
 import("//build/json_schema_api.gni")
 import("//extensions/common/api/schema.gni")
+import("//extensions/features/features.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 
 assert(enable_extensions)
@@ -18,6 +19,10 @@
   uncompiled_sources = extensions_api_uncompiled_sources
   uncompiled_bundle_schema_sources =
       extensions_api_uncompiled_bundle_schema_sources
+  deps = [
+    "//base",
+    "//extensions/features",
+  ]
 }
 
 mojom("mojom") {
@@ -30,5 +35,6 @@
   public_deps = [
     ":generated_api",
     ":mojom",
+    "//extensions/features",
   ]
 }
diff --git a/extensions/common/extension.h b/extensions/common/extension.h
index 308b4d88..41c8f3dd 100644
--- a/extensions/common/extension.h
+++ b/extensions/common/extension.h
@@ -21,9 +21,10 @@
 #include "extensions/common/install_warning.h"
 #include "extensions/common/manifest.h"
 #include "extensions/common/url_pattern_set.h"
+#include "extensions/features/features.h"
 #include "url/gurl.h"
 
-#if !defined(ENABLE_EXTENSIONS)
+#if !BUILDFLAG(ENABLE_EXTENSIONS)
 #error "Extensions must be enabled"
 #endif
 
diff --git a/extensions/features/BUILD.gn b/extensions/features/BUILD.gn
new file mode 100644
index 0000000..5be8600f
--- /dev/null
+++ b/extensions/features/BUILD.gn
@@ -0,0 +1,16 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/buildflag_header.gni")
+import("//extensions/features/features.gni")
+
+# This file is in a separate directory so all targets in the build can refer to
+# the buildflag header to get the necessary preprocessor defines without
+# bringing in all of extensions. Other targets can depend on this target
+# regardless of whether extensions are enabled.
+
+buildflag_header("features") {
+  header = "features.h"
+  flags = [ "ENABLE_EXTENSIONS=$enable_extensions" ]
+}
diff --git a/extensions/features/features.gni b/extensions/features/features.gni
new file mode 100644
index 0000000..43da5b5
--- /dev/null
+++ b/extensions/features/features.gni
@@ -0,0 +1,9 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/features.gni")
+
+declare_args() {
+  enable_extensions = !is_android && !is_ios
+}
diff --git a/extensions/renderer/BUILD.gn b/extensions/renderer/BUILD.gn
index 65774b6..cf7430f 100644
--- a/extensions/renderer/BUILD.gn
+++ b/extensions/renderer/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/features.gni")
+import("//extensions/features/features.gni")
 
 assert(enable_extensions)
 
diff --git a/extensions/shell/BUILD.gn b/extensions/shell/BUILD.gn
index 41b3eb5..f50122b 100644
--- a/extensions/shell/BUILD.gn
+++ b/extensions/shell/BUILD.gn
@@ -5,6 +5,7 @@
 import("//build/config/features.gni")
 import("//build/config/ui.gni")
 import("//build/util/process_version.gni")
+import("//extensions/features/features.gni")
 import("//testing/test.gni")
 import("//tools/grit/grit_rule.gni")
 
diff --git a/extensions/shell/common/api/BUILD.gn b/extensions/shell/common/api/BUILD.gn
index 3eafd8f..378906e 100644
--- a/extensions/shell/common/api/BUILD.gn
+++ b/extensions/shell/common/api/BUILD.gn
@@ -23,6 +23,7 @@
 
   deps = [
     ":api",
+    "//extensions/common",
   ]
 }
 
diff --git a/extensions/strings/BUILD.gn b/extensions/strings/BUILD.gn
index 19ddf4e..b32b998 100644
--- a/extensions/strings/BUILD.gn
+++ b/extensions/strings/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//extensions/features/features.gni")
 import("//tools/grit/grit_rule.gni")
 
 assert(enable_extensions)
diff --git a/extensions/utility/BUILD.gn b/extensions/utility/BUILD.gn
index facc846..7057a1e 100644
--- a/extensions/utility/BUILD.gn
+++ b/extensions/utility/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/features.gni")
+import("//extensions/features/features.gni")
 
 assert(enable_extensions)
 
diff --git a/google_apis/BUILD.gn b/google_apis/BUILD.gn
index 7be06fd..c3ad17d1 100644
--- a/google_apis/BUILD.gn
+++ b/google_apis/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/features.gni")
+import("//extensions/features/features.gni")
 import("//testing/test.gni")
 
 declare_args() {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 8be63b09..6f0d2e8d 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -13677,11 +13677,24 @@
     return error::kNoError;
   }
 
-  // Note: There is no need to deal with texture cleared tracking here
-  // because the validation above means you can only get here if the level
-  // is already a matching compressed format and in that case
-  // CompressedTexImage{2|3}D already cleared the texture.
-  DCHECK(texture->IsLevelCleared(target, level));
+  if (!texture->IsLevelCleared(target, level)) {
+    // This can only happen if the compressed texture was allocated
+    // using TexStorage{2|3}D.
+    DCHECK(texture->IsImmutable());
+    GLsizei level_width = 0, level_height = 0, level_depth = 0;
+    bool success = texture->GetLevelSize(
+        target, level, &level_width, &level_height, &level_depth);
+    DCHECK(success);
+    if (xoffset == 0 && width == level_width &&
+        yoffset == 0 && height == level_height &&
+        zoffset == 0 && depth == level_depth) {
+      // We can skip the clear if we're uploading the entire level.
+      texture_manager()->SetLevelCleared(texture_ref, target, level, true);
+    } else {
+      texture_manager()->ClearTextureLevel(this, texture_ref, target, level);
+    }
+    DCHECK(texture->IsLevelCleared(target, level));
+  }
 
   const CompressedFormatInfo* format_info =
       GetCompressedFormatInfo(internal_format);
diff --git a/gpu/ipc/service/gpu_command_buffer_stub.cc b/gpu/ipc/service/gpu_command_buffer_stub.cc
index 5b29e2c..b639619 100644
--- a/gpu/ipc/service/gpu_command_buffer_stub.cc
+++ b/gpu/ipc/service/gpu_command_buffer_stub.cc
@@ -293,6 +293,8 @@
   send_params.ca_context_id = params.ca_context_id;
   send_params.fullscreen_low_power_ca_context_valid =
       params.fullscreen_low_power_ca_context_valid;
+  send_params.fullscreen_low_power_ca_context_id =
+      params.fullscreen_low_power_ca_context_id;
   send_params.io_surface = params.io_surface;
   send_params.pixel_size = params.pixel_size;
   send_params.scale_factor = params.scale_factor;
diff --git a/gpu/ipc/service/pass_through_image_transport_surface.cc b/gpu/ipc/service/pass_through_image_transport_surface.cc
index 1e3f153..e269cd2 100644
--- a/gpu/ipc/service/pass_through_image_transport_surface.cc
+++ b/gpu/ipc/service/pass_through_image_transport_surface.cc
@@ -142,8 +142,6 @@
 void PassThroughImageTransportSurface::SendVSyncUpdateIfAvailable() {
   gfx::VSyncProvider* vsync_provider = GetVSyncProvider();
   if (vsync_provider) {
-    // PassThroughImageTransportSurface owns the VSyncProvider and will
-    // outlive it. Thus, base::Unretained is safe here.
     vsync_provider->GetVSyncParameters(base::Bind(
         &ImageTransportSurfaceDelegate::UpdateVSyncParameters, delegate_));
   }
diff --git a/infra/config/recipes.cfg b/infra/config/recipes.cfg
index cbba25d..2bf7cbf 100644
--- a/infra/config/recipes.cfg
+++ b/infra/config/recipes.cfg
@@ -5,13 +5,13 @@
   project_id: "build"
   url: "https://chromium.googlesource.com/chromium/tools/build.git"
   branch: "master"
-  revision: "86356c164014cf93140c7548236b2761fe6dc93c"
+  revision: "55e3762e84b6ffa98fbb334b68aa2df622e74972"
 }
 deps {
   project_id: "depot_tools"
   url: "https://chromium.googlesource.com/chromium/tools/depot_tools.git"
   branch: "master"
-  revision: "4625b5adb8bd2bb5767ab4bccc59fc51284066a6"
+  revision: "9b654aa0848af49cc42985476acb58e03f0b72f5"
 }
 deps {
   project_id: "recipe_engine"
diff --git a/mash/BUILD.gn b/mash/BUILD.gn
index c21cdbdb..b4337e0 100644
--- a/mash/BUILD.gn
+++ b/mash/BUILD.gn
@@ -62,7 +62,6 @@
 }
 
 service_manifest("unittests_manifest") {
-  type = "exe"
   name = "mash_unittests"
   source = "unittests_manifest.json"
 }
diff --git a/mash/example/window_type_launcher/window_type_launcher.cc b/mash/example/window_type_launcher/window_type_launcher.cc
index c0d1155..c650f06c 100644
--- a/mash/example/window_type_launcher/window_type_launcher.cc
+++ b/mash/example/window_type_launcher/window_type_launcher.cc
@@ -474,6 +474,7 @@
 }
 
 void WindowTypeLauncher::OnStart(service_manager::ServiceContext* context) {
+  context_ = context;
   aura_init_ = base::MakeUnique<views::AuraInit>(
       context->connector(), context->identity(), "views_mus_resources.pak",
       std::string(), nullptr, views::AuraInit::Mode::AURA_MUS);
diff --git a/mash/unittests_manifest.json b/mash/unittests_manifest.json
index 047b862..d3a4f80 100644
--- a/mash/unittests_manifest.json
+++ b/mash/unittests_manifest.json
@@ -1,5 +1,5 @@
 {
-  "name": "exe:mash_unittests",
+  "name": "service:mash_unittests",
   "display_name": "Mash Unittests",
   "interface_provider_specs": {
     "service_manager:connector": {
diff --git a/media/blink/webcontentdecryptionmodulesession_impl.cc b/media/blink/webcontentdecryptionmodulesession_impl.cc
index ab138e7..7499874 100644
--- a/media/blink/webcontentdecryptionmodulesession_impl.cc
+++ b/media/blink/webcontentdecryptionmodulesession_impl.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/logging.h"
+#include "base/memory/ptr_util.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -32,13 +33,15 @@
 
 namespace media {
 
+namespace {
+
 const char kCloseSessionUMAName[] = "CloseSession";
 const char kGenerateRequestUMAName[] = "GenerateRequest";
 const char kLoadSessionUMAName[] = "LoadSession";
 const char kRemoveSessionUMAName[] = "RemoveSession";
 const char kUpdateSessionUMAName[] = "UpdateSession";
 
-static blink::WebContentDecryptionModuleSession::Client::MessageType
+blink::WebContentDecryptionModuleSession::Client::MessageType
 convertMessageType(MediaKeys::MessageType message_type) {
   switch (message_type) {
     case media::MediaKeys::LICENSE_REQUEST:
@@ -57,7 +60,7 @@
       LicenseRequest;
 }
 
-static blink::WebEncryptedMediaKeyInformation::KeyStatus convertStatus(
+blink::WebEncryptedMediaKeyInformation::KeyStatus convertStatus(
     media::CdmKeyInformation::KeyStatus status) {
   switch (status) {
     case media::CdmKeyInformation::USABLE:
@@ -82,7 +85,7 @@
   return blink::WebEncryptedMediaKeyInformation::KeyStatus::InternalError;
 }
 
-static MediaKeys::SessionType convertSessionType(
+MediaKeys::SessionType convertSessionType(
     blink::WebEncryptedMediaSessionType session_type) {
   switch (session_type) {
     case blink::WebEncryptedMediaSessionType::Temporary:
@@ -99,11 +102,11 @@
   return MediaKeys::TEMPORARY_SESSION;
 }
 
-static bool SanitizeInitData(EmeInitDataType init_data_type,
-                             const unsigned char* init_data,
-                             size_t init_data_length,
-                             std::vector<uint8_t>* sanitized_init_data,
-                             std::string* error_message) {
+bool SanitizeInitData(EmeInitDataType init_data_type,
+                      const unsigned char* init_data,
+                      size_t init_data_length,
+                      std::vector<uint8_t>* sanitized_init_data,
+                      std::string* error_message) {
   DCHECK_GT(init_data_length, 0u);
   if (init_data_length > limits::kMaxInitDataLength) {
     error_message->assign("Initialization data too long.");
@@ -163,8 +166,8 @@
   return false;
 }
 
-static bool SanitizeSessionId(const blink::WebString& session_id,
-                              std::string* sanitized_session_id) {
+bool SanitizeSessionId(const blink::WebString& session_id,
+                       std::string* sanitized_session_id) {
   // The user agent should thoroughly validate the sessionId value before
   // passing it to the CDM. At a minimum, this should include checking that
   // the length and value (e.g. alphanumeric) are reasonable.
@@ -183,10 +186,10 @@
   return true;
 }
 
-static bool SanitizeResponse(const std::string& key_system,
-                             const uint8_t* response,
-                             size_t response_length,
-                             std::vector<uint8_t>* sanitized_response) {
+bool SanitizeResponse(const std::string& key_system,
+                      const uint8_t* response,
+                      size_t response_length,
+                      std::vector<uint8_t>* sanitized_response) {
   // The user agent should thoroughly validate the response before passing it
   // to the CDM. This may include verifying values are within reasonable limits,
   // stripping irrelevant data or fields, pre-parsing it, sanitizing it,
@@ -224,16 +227,55 @@
   return true;
 }
 
+// If we need to close the session while destroying this object, we need a
+// dummy promise that won't call back into this object (or try to pass
+// something back to blink).
+class IgnoreResponsePromise : public SimpleCdmPromise {
+ public:
+  IgnoreResponsePromise() {}
+  ~IgnoreResponsePromise() override {}
+
+  // SimpleCdmPromise implementation.
+  void resolve() final { MarkPromiseSettled(); }
+  void reject(CdmPromise::Exception exception_code,
+              uint32_t system_code,
+              const std::string& error_message) final {
+    MarkPromiseSettled();
+  }
+};
+
+}  // namespace
+
 WebContentDecryptionModuleSessionImpl::WebContentDecryptionModuleSessionImpl(
     const scoped_refptr<CdmSessionAdapter>& adapter)
-    : adapter_(adapter), is_closed_(false), weak_ptr_factory_(this) {
-}
+    : adapter_(adapter),
+      has_close_been_called_(false),
+      is_closed_(false),
+      weak_ptr_factory_(this) {}
 
 WebContentDecryptionModuleSessionImpl::
     ~WebContentDecryptionModuleSessionImpl() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (!session_id_.empty())
+
+  if (!session_id_.empty()) {
     adapter_->UnregisterSession(session_id_);
+
+    // From http://w3c.github.io/encrypted-media/#mediakeysession-interface
+    // "If a MediaKeySession object is not closed when it becomes inaccessible
+    // to the page, the CDM shall close the key session associated with the
+    // object."
+    //
+    // This object is destroyed when the corresponding blink object is no
+    // longer needed (which may be due to it becoming inaccessible to the
+    // page), so if the session is not closed and CloseSession() has not yet
+    // been called, call CloseSession() now. Since this object is being
+    // destroyed, there is no need for the promise to do anything as this
+    // session will be gone.
+    if (!is_closed_ && !has_close_been_called_) {
+      adapter_->CloseSession(session_id_,
+                             base::MakeUnique<IgnoreResponsePromise>());
+    }
+  }
 }
 
 void WebContentDecryptionModuleSessionImpl::setClientInterface(Client* client) {
@@ -392,8 +434,16 @@
 
 void WebContentDecryptionModuleSessionImpl::close(
     blink::WebContentDecryptionModuleResult result) {
+  // close() shouldn't be called if the session is already closed. blink
+  // prevents a second call to close(), but since the operation is
+  // asynchronous, there is a window where close() was called just before
+  // the closed event arrives. The CDM should handle the case where
+  // close() is called after it has already closed the session.
   DCHECK(!session_id_.empty());
+  DCHECK(!has_close_been_called_);
   DCHECK(thread_checker_.CalledOnValidThread());
+
+  has_close_been_called_ = true;
   adapter_->CloseSession(
       session_id_,
       std::unique_ptr<SimpleCdmPromise>(new CdmResultPromise<>(
@@ -445,6 +495,8 @@
 
 void WebContentDecryptionModuleSessionImpl::OnSessionClosed() {
   DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Only send one closed event to blink.
   if (is_closed_)
     return;
 
diff --git a/media/blink/webcontentdecryptionmodulesession_impl.h b/media/blink/webcontentdecryptionmodulesession_impl.h
index 18db472..6824e6cc 100644
--- a/media/blink/webcontentdecryptionmodulesession_impl.h
+++ b/media/blink/webcontentdecryptionmodulesession_impl.h
@@ -75,9 +75,14 @@
   // promise.
   std::string session_id_;
 
-  // Don't pass more than 1 close() event to blink::
-  // TODO(jrummell): Remove this once blink tests handle close() promise and
-  // closed() event.
+  // Keep track of whether the session has been closed or not. The session
+  // may be closed as a result of an application calling close(), or the CDM
+  // may close the session at any point.
+  // https://w3c.github.io/encrypted-media/#session-closed
+  // |has_close_been_called_| is used to keep track of whether close() has
+  // been called or not. |is_closed_| is used to keep track of whether the
+  // close event has been received or not.
+  bool has_close_been_called_;
   bool is_closed_;
 
   base::ThreadChecker thread_checker_;
diff --git a/media/capture/content/thread_safe_capture_oracle.cc b/media/capture/content/thread_safe_capture_oracle.cc
index 50a0d6ae..c406396e 100644
--- a/media/capture/content/thread_safe_capture_oracle.cc
+++ b/media/capture/content/thread_safe_capture_oracle.cc
@@ -168,7 +168,7 @@
     return false;
   }
 
-  capture_callback.Run(frame, refresh_time, true);
+  capture_callback.Run(std::move(frame), refresh_time, true);
   return true;
 }
 
@@ -201,7 +201,7 @@
     std::unique_ptr<VideoCaptureDevice::Client::Buffer> buffer,
     base::TimeTicks capture_begin_time,
     base::TimeDelta estimated_frame_duration,
-    const scoped_refptr<VideoFrame>& frame,
+    scoped_refptr<VideoFrame> frame,
     base::TimeTicks reference_time,
     bool success) {
   TRACE_EVENT_ASYNC_END2("gpu.capture", "Capture", buffer.get(), "success",
@@ -232,7 +232,7 @@
         base::Bind(&ThreadSafeCaptureOracle::DidConsumeFrame, this,
                    frame_number, frame->metadata()));
 
-    client_->OnIncomingCapturedVideoFrame(std::move(buffer), frame);
+    client_->OnIncomingCapturedVideoFrame(std::move(buffer), std::move(frame));
   }
 }
 
diff --git a/media/capture/content/thread_safe_capture_oracle.h b/media/capture/content/thread_safe_capture_oracle.h
index f54315b..7cc554d 100644
--- a/media/capture/content/thread_safe_capture_oracle.h
+++ b/media/capture/content/thread_safe_capture_oracle.h
@@ -38,9 +38,10 @@
   // If |success| is true then |frame| is valid and |timestamp| indicates when
   // the frame was painted.
   // If |success| is false, all other parameters are invalid.
-  typedef base::Callback<void(const scoped_refptr<VideoFrame>& frame,
+  typedef base::Callback<void(scoped_refptr<VideoFrame> frame,
                               base::TimeTicks timestamp,
-                              bool success)> CaptureFrameCallback;
+                              bool success)>
+      CaptureFrameCallback;
 
   // Record a change |event| along with its |damage_rect| and |event_time|, and
   // then make a decision whether to proceed with capture. The decision is based
@@ -100,7 +101,7 @@
       std::unique_ptr<VideoCaptureDevice::Client::Buffer> buffer,
       base::TimeTicks capture_begin_time,
       base::TimeDelta estimated_frame_duration,
-      const scoped_refptr<VideoFrame>& frame,
+      scoped_refptr<VideoFrame> frame,
       base::TimeTicks reference_time,
       bool success);
 
diff --git a/media/capture/video/fake_video_capture_device_unittest.cc b/media/capture/video/fake_video_capture_device_unittest.cc
index 44bfaeb..5b6fe3a 100644
--- a/media/capture/video/fake_video_capture_device_unittest.cc
+++ b/media/capture/video/fake_video_capture_device_unittest.cc
@@ -96,9 +96,8 @@
                                 base::TimeDelta timestamp) {
     frame_cb_.Run(frame_format);
   }
-  void OnIncomingCapturedVideoFrame(
-      std::unique_ptr<Buffer> buffer,
-      const scoped_refptr<media::VideoFrame>& frame) {
+  void OnIncomingCapturedVideoFrame(std::unique_ptr<Buffer> buffer,
+                                    scoped_refptr<media::VideoFrame> frame) {
     VideoCaptureFormat format(frame->natural_size(), 30.0,
                               PIXEL_FORMAT_I420);
     frame_cb_.Run(format);
diff --git a/media/capture/video/video_capture_device.h b/media/capture/video/video_capture_device.h
index 24d6c36..000b0feb 100644
--- a/media/capture/video/video_capture_device.h
+++ b/media/capture/video/video_capture_device.h
@@ -119,7 +119,7 @@
         base::TimeDelta timestamp) = 0;
     virtual void OnIncomingCapturedVideoFrame(
         std::unique_ptr<Buffer> buffer,
-        const scoped_refptr<VideoFrame>& frame) = 0;
+        scoped_refptr<VideoFrame> frame) = 0;
 
     // Attempts to reserve the same Buffer provided in the last call to one of
     // the OnIncomingCapturedXXX() methods. This will fail if the content of the
diff --git a/media/capture/video/video_capture_device_client.cc b/media/capture/video/video_capture_device_client.cc
index 215785ea..3590c7aa 100644
--- a/media/capture/video/video_capture_device_client.cc
+++ b/media/capture/video/video_capture_device_client.cc
@@ -41,10 +41,9 @@
 // implementation to guarantee proper cleanup on destruction on our side.
 class AutoReleaseBuffer : public media::VideoCaptureDevice::Client::Buffer {
  public:
-  AutoReleaseBuffer(const scoped_refptr<VideoCaptureBufferPool>& pool,
-                    int buffer_id)
+  AutoReleaseBuffer(scoped_refptr<VideoCaptureBufferPool> pool, int buffer_id)
       : id_(buffer_id),
-        pool_(pool),
+        pool_(std::move(pool)),
         buffer_handle_(pool_->GetBufferHandle(buffer_id)) {
     DCHECK(pool_.get());
   }
@@ -74,12 +73,12 @@
 
 VideoCaptureDeviceClient::VideoCaptureDeviceClient(
     std::unique_ptr<VideoFrameReceiver> receiver,
-    const scoped_refptr<VideoCaptureBufferPool>& buffer_pool,
+    scoped_refptr<VideoCaptureBufferPool> buffer_pool,
     const VideoCaptureJpegDecoderFactoryCB& jpeg_decoder_factory)
     : receiver_(std::move(receiver)),
       jpeg_decoder_factory_callback_(jpeg_decoder_factory),
       external_jpeg_decoder_initialized_(false),
-      buffer_pool_(buffer_pool),
+      buffer_pool_(std::move(buffer_pool)),
       last_captured_pixel_format_(media::PIXEL_FORMAT_UNKNOWN) {}
 
 VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {
@@ -312,13 +311,13 @@
                                frame_format.frame_rate);
   frame->metadata()->SetTimeTicks(media::VideoFrameMetadata::REFERENCE_TIME,
                                   reference_time);
-  OnIncomingCapturedVideoFrame(std::move(buffer), frame);
+  OnIncomingCapturedVideoFrame(std::move(buffer), std::move(frame));
 }
 
 void VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame(
     std::unique_ptr<Buffer> buffer,
-    const scoped_refptr<VideoFrame>& frame) {
-  receiver_->OnIncomingCapturedVideoFrame(std::move(buffer), frame);
+    scoped_refptr<VideoFrame> frame) {
+  receiver_->OnIncomingCapturedVideoFrame(std::move(buffer), std::move(frame));
 }
 
 std::unique_ptr<media::VideoCaptureDevice::Client::Buffer>
diff --git a/media/capture/video/video_capture_device_client.h b/media/capture/video/video_capture_device_client.h
index b36ebe5..9a36f9d 100644
--- a/media/capture/video/video_capture_device_client.h
+++ b/media/capture/video/video_capture_device_client.h
@@ -45,7 +45,7 @@
  public:
   VideoCaptureDeviceClient(
       std::unique_ptr<VideoFrameReceiver> receiver,
-      const scoped_refptr<VideoCaptureBufferPool>& buffer_pool,
+      scoped_refptr<VideoCaptureBufferPool> buffer_pool,
       const VideoCaptureJpegDecoderFactoryCB& jpeg_decoder_factory);
   ~VideoCaptureDeviceClient() override;
 
@@ -66,7 +66,7 @@
                                 base::TimeDelta timestamp) override;
   void OnIncomingCapturedVideoFrame(
       std::unique_ptr<Buffer> buffer,
-      const scoped_refptr<media::VideoFrame>& frame) override;
+      scoped_refptr<media::VideoFrame> frame) override;
   std::unique_ptr<Buffer> ResurrectLastOutputBuffer(
       const gfx::Size& dimensions,
       media::VideoPixelFormat format,
diff --git a/media/capture/video/video_capture_device_unittest.cc b/media/capture/video/video_capture_device_unittest.cc
index 226bc59c..6b593dc 100644
--- a/media/capture/video/video_capture_device_unittest.cc
+++ b/media/capture/video/video_capture_device_unittest.cc
@@ -135,9 +135,8 @@
                                 base::TimeDelta timestamp) override {
     DoOnIncomingCapturedBuffer();
   }
-  void OnIncomingCapturedVideoFrame(
-      std::unique_ptr<Buffer> buffer,
-      const scoped_refptr<VideoFrame>& frame) override {
+  void OnIncomingCapturedVideoFrame(std::unique_ptr<Buffer> buffer,
+                                    scoped_refptr<VideoFrame> frame) override {
     DoOnIncomingCapturedVideoFrame();
   }
   std::unique_ptr<Buffer> ResurrectLastOutputBuffer(
diff --git a/media/capture/video/video_capture_jpeg_decoder.h b/media/capture/video/video_capture_jpeg_decoder.h
index e9d91dc..193fabb 100644
--- a/media/capture/video/video_capture_jpeg_decoder.h
+++ b/media/capture/video/video_capture_jpeg_decoder.h
@@ -21,7 +21,7 @@
 
   using DecodeDoneCB = base::Callback<void(
       std::unique_ptr<media::VideoCaptureDevice::Client::Buffer>,
-      const scoped_refptr<media::VideoFrame>&)>;
+      scoped_refptr<media::VideoFrame>)>;
 
   virtual ~VideoCaptureJpegDecoder() {}
 
diff --git a/media/capture/video/video_frame_receiver.h b/media/capture/video/video_frame_receiver.h
index df2dabc..b2af10c6 100644
--- a/media/capture/video/video_frame_receiver.h
+++ b/media/capture/video/video_frame_receiver.h
@@ -15,7 +15,7 @@
 
   virtual void OnIncomingCapturedVideoFrame(
       std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer,
-      const scoped_refptr<media::VideoFrame>& frame) = 0;
+      scoped_refptr<media::VideoFrame> frame) = 0;
   virtual void OnError() = 0;
   virtual void OnLog(const std::string& message) = 0;
   virtual void OnBufferDestroyed(int buffer_id_to_drop) = 0;
diff --git a/media/gpu/android_video_decode_accelerator.cc b/media/gpu/android_video_decode_accelerator.cc
index 3bec303..de67510 100644
--- a/media/gpu/android_video_decode_accelerator.cc
+++ b/media/gpu/android_video_decode_accelerator.cc
@@ -46,10 +46,10 @@
 #include "media/mojo/services/mojo_cdm_service.h"
 #endif
 
-#define POST_ERROR(error_code, error_message)                 \
-  do {                                                        \
-    DLOG(ERROR) << error_message;                             \
-    PostError(FROM_HERE, VideoDecodeAccelerator::error_code); \
+#define NOTIFY_ERROR(error_code, error_message)      \
+  do {                                               \
+    DLOG(ERROR) << error_message;                    \
+    NotifyError(VideoDecodeAccelerator::error_code); \
   } while (0)
 
 namespace media {
@@ -106,11 +106,6 @@
 
 constexpr base::TimeDelta IdleTimerTimeOut = base::TimeDelta::FromSeconds(1);
 
-// Time between when we notice an error, and when we actually notify somebody.
-// This is to prevent codec errors caused by SurfaceView fullscreen transitions
-// from breaking the pipeline, if we're about to be reset anyway.
-constexpr base::TimeDelta ErrorPostingDelay = base::TimeDelta::FromSeconds(2);
-
 }  // namespace
 
 static base::LazyInstance<AVDACodecAllocator>::Leaky g_avda_codec_allocator =
@@ -313,8 +308,6 @@
       media_drm_bridge_cdm_context_(nullptr),
       cdm_registration_id_(0),
       pending_input_buf_index_(-1),
-      error_sequence_token_(0),
-      defer_errors_(false),
       deferred_initialization_pending_(false),
       codec_needs_reset_(false),
       defer_surface_creation_(false),
@@ -509,7 +502,6 @@
 bool AndroidVideoDecodeAccelerator::QueueInput() {
   DCHECK(thread_checker_.CalledOnValidThread());
   TRACE_EVENT0("media", "AVDA::QueueInput");
-  base::AutoReset<bool> auto_reset(&defer_errors_, true);
   if (state_ == ERROR || state_ == WAITING_FOR_CODEC ||
       state_ == WAITING_FOR_KEY) {
     return false;
@@ -531,12 +523,12 @@
       case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER:
         return false;
       case MEDIA_CODEC_ERROR:
-        POST_ERROR(PLATFORM_FAILURE, "Failed to DequeueInputBuffer");
+        NOTIFY_ERROR(PLATFORM_FAILURE, "DequeueInputBuffer failed");
         return false;
       case MEDIA_CODEC_OK:
         break;
       default:
-        NOTREACHED() << "Unknown DequeueInputBuffer status " << status;
+        NOTREACHED();
         return false;
     }
   }
@@ -563,7 +555,7 @@
     shm = std::move(pending_bitstream_records_.front().memory);
 
     if (!shm->Map()) {
-      POST_ERROR(UNREADABLE_INPUT, "Failed to SharedMemoryRegion::Map()");
+      NOTIFY_ERROR(UNREADABLE_INPUT, "SharedMemoryRegion::Map() failed");
       return false;
     }
   }
@@ -631,7 +623,7 @@
   bitstreams_notified_in_advance_.push_back(bitstream_buffer.id());
 
   if (status != MEDIA_CODEC_OK) {
-    POST_ERROR(PLATFORM_FAILURE, "Failed to QueueInputBuffer: " << status);
+    NOTIFY_ERROR(PLATFORM_FAILURE, "QueueInputBuffer failed:" << status);
     return false;
   }
 
@@ -641,7 +633,6 @@
 bool AndroidVideoDecodeAccelerator::DequeueOutput() {
   DCHECK(thread_checker_.CalledOnValidThread());
   TRACE_EVENT0("media", "AVDA::DequeueOutput");
-  base::AutoReset<bool> auto_reset(&defer_errors_, true);
   if (state_ == ERROR || state_ == WAITING_FOR_CODEC)
     return false;
   // If we're draining for reset or destroy, then we don't need picture buffers
@@ -693,7 +684,7 @@
           state_ = ERROR;
           OnDrainCompleted();
         } else {
-          POST_ERROR(PLATFORM_FAILURE, "DequeueOutputBuffer failed.");
+          NOTIFY_ERROR(PLATFORM_FAILURE, "DequeueOutputBuffer failed.");
         }
         return false;
 
@@ -708,7 +699,7 @@
           return true;  // ignore
 
         if (media_codec_->GetOutputSize(&size_) != MEDIA_CODEC_OK) {
-          POST_ERROR(PLATFORM_FAILURE, "GetOutputSize failed.");
+          NOTIFY_ERROR(PLATFORM_FAILURE, "GetOutputSize failed.");
           return false;
         }
 
@@ -764,7 +755,7 @@
     // In 0.01% of playbacks MediaCodec returns a frame before FORMAT_CHANGED.
     // Occurs on JB and M. (See the Media.AVDA.MissingFormatChanged histogram.)
     media_codec_->ReleaseOutputBuffer(buf_index, false);
-    POST_ERROR(PLATFORM_FAILURE, "Dequeued buffers before FORMAT_CHANGED.");
+    NOTIFY_ERROR(PLATFORM_FAILURE, "Dequeued buffers before FORMAT_CHANGED.");
     return false;
   }
 
@@ -814,7 +805,7 @@
   TRACE_EVENT0("media", "AVDA::SendDecodedFrameToClient");
 
   if (!make_context_current_cb_.Run()) {
-    POST_ERROR(PLATFORM_FAILURE, "Failed to make the GL context current.");
+    NOTIFY_ERROR(PLATFORM_FAILURE, "Failed to make the GL context current.");
     return;
   }
 
@@ -824,8 +815,8 @@
 
   const auto it = output_picture_buffers_.find(picture_buffer_id);
   if (it == output_picture_buffers_.end()) {
-    POST_ERROR(PLATFORM_FAILURE,
-               "Can't find PictureBuffer id: " << picture_buffer_id);
+    NOTIFY_ERROR(PLATFORM_FAILURE,
+                 "Can't find PictureBuffer id: " << picture_buffer_id);
     return;
   }
 
@@ -857,8 +848,8 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   if (defer_surface_creation_ && !InitializePictureBufferManager()) {
-    POST_ERROR(PLATFORM_FAILURE,
-               "Failed deferred surface and MediaCodec initialization.");
+    NOTIFY_ERROR(PLATFORM_FAILURE,
+                 "Failed deferred surface and MediaCodec initialization.");
     return;
   }
 
@@ -878,8 +869,8 @@
     base::SharedMemory::CloseHandle(bitstream_buffer.handle());
 
   if (bitstream_buffer.id() < 0) {
-    POST_ERROR(INVALID_ARGUMENT,
-               "Invalid bistream_buffer, id: " << bitstream_buffer.id());
+    NOTIFY_ERROR(INVALID_ARGUMENT,
+                 "Invalid bistream_buffer, id: " << bitstream_buffer.id());
   } else {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
@@ -917,7 +908,7 @@
   DCHECK(free_picture_ids_.empty());
 
   if (buffers.size() < kNumPictureBuffers) {
-    POST_ERROR(INVALID_ARGUMENT, "Not enough picture buffers assigned.");
+    NOTIFY_ERROR(INVALID_ARGUMENT, "Not enough picture buffers assigned.");
     return;
   }
 
@@ -946,8 +937,8 @@
 
   auto it = output_picture_buffers_.find(picture_buffer_id);
   if (it == output_picture_buffers_.end()) {
-    POST_ERROR(PLATFORM_FAILURE, "Can't find PictureBuffer id "
-                                     << picture_buffer_id);
+    NOTIFY_ERROR(PLATFORM_FAILURE, "Can't find PictureBuffer id "
+                                       << picture_buffer_id);
     return;
   }
 
@@ -1079,7 +1070,7 @@
   media_codec_ = std::move(media_codec);
   picture_buffer_manager_.CodecChanged(media_codec_.get());
   if (!media_codec_) {
-    POST_ERROR(PLATFORM_FAILURE, "Failed to create MediaCodec.");
+    NOTIFY_ERROR(PLATFORM_FAILURE, "Failed to create MediaCodec");
     return;
   }
 
@@ -1181,14 +1172,6 @@
     return;
   }
 
-  // We might increment error_sequence_token here to cancel any delayed errors,
-  // but right now it's unclear that it's safe to do so.  If we are in an error
-  // state because of a codec error, then it would be okay.  Otherwise, it's
-  // less obvious that we are exiting the error state.  Since deferred errors
-  // are only intended for fullscreen transitions right now, we take the more
-  // conservative approach and let the errors post.
-  // TODO(liberato): revisit this once we sort out the error state a bit more.
-
   // Flush the codec if possible, or create a new one if not.
   if (!did_codec_error_happen &&
       !MediaCodecUtil::CodecNeedsFlushWorkaround(media_codec_.get())) {
@@ -1235,9 +1218,6 @@
   TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount", 0);
   bitstreams_notified_in_advance_.clear();
 
-  // Any error that is waiting to post can be ignored.
-  error_sequence_token_++;
-
   picture_buffer_manager_.ReleaseCodecBuffers(output_picture_buffers_);
 
   // Some VP8 files require complete MediaCodec drain before we can call
@@ -1370,17 +1350,6 @@
     OnDrainCompleted();
 }
 
-void AndroidVideoDecodeAccelerator::PostError(
-    const ::tracked_objects::Location& from_here,
-    VideoDecodeAccelerator::Error error) {
-  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      from_here,
-      base::Bind(&AndroidVideoDecodeAccelerator::NotifyError,
-                 weak_this_factory_.GetWeakPtr(), error, error_sequence_token_),
-      (defer_errors_ ? ErrorPostingDelay : base::TimeDelta()));
-  state_ = ERROR;
-}
-
 void AndroidVideoDecodeAccelerator::InitializeCdm() {
   DVLOG(2) << __FUNCTION__ << ": " << config_.cdm_id;
 
@@ -1479,14 +1448,8 @@
     client_->NotifyResetDone();
 }
 
-void AndroidVideoDecodeAccelerator::NotifyError(
-    VideoDecodeAccelerator::Error error,
-    int token) {
-  DVLOG(1) << __FUNCTION__ << ": error: " << error << " token: " << token
-           << " current: " << error_sequence_token_;
-  if (token != error_sequence_token_)
-    return;
-
+void AndroidVideoDecodeAccelerator::NotifyError(Error error) {
+  state_ = ERROR;
   if (client_)
     client_->NotifyError(error);
 }
@@ -1636,9 +1599,9 @@
   // Ensure the current context is active when switching surfaces; we may need
   // to create a new texture.
   if (!make_context_current_cb_.Run()) {
-    POST_ERROR(PLATFORM_FAILURE,
-               "Failed to make this decoder's GL context current when "
-               "switching surfaces.");
+    NOTIFY_ERROR(PLATFORM_FAILURE,
+                 "Failed to make this decoder's GL context current when "
+                 "switching surfaces.");
     return false;
   }
 
@@ -1646,13 +1609,13 @@
   codec_config_->surface_ =
       picture_buffer_manager_.Initialize(pending_surface_id_.value());
   if (codec_config_->surface_.IsEmpty()) {
-    POST_ERROR(PLATFORM_FAILURE, "An error occurred while switching surfaces.");
+    NOTIFY_ERROR(PLATFORM_FAILURE, "Failed to switch surfaces.");
     return false;
   }
 
   if (media_codec_ &&
       !media_codec_->SetSurface(codec_config_->surface_.j_surface().obj())) {
-    POST_ERROR(PLATFORM_FAILURE, "MediaCodec failed to switch surfaces.");
+    NOTIFY_ERROR(PLATFORM_FAILURE, "MediaCodec failed to switch surfaces.");
     return false;
   }
 
diff --git a/media/gpu/android_video_decode_accelerator.h b/media/gpu/android_video_decode_accelerator.h
index 2b4d4ae..d4179ac 100644
--- a/media/gpu/android_video_decode_accelerator.h
+++ b/media/gpu/android_video_decode_accelerator.h
@@ -45,6 +45,9 @@
     : public VideoDecodeAccelerator,
       public AVDAStateProvider {
  public:
+  static VideoDecodeAccelerator::Capabilities GetCapabilities(
+      const gpu::GpuPreferences& gpu_preferences);
+
   AndroidVideoDecodeAccelerator(
       const MakeGLContextCurrentCallback& make_context_current_cb,
       const GetGLES2DecoderCallback& get_gles2_decoder_cb);
@@ -68,11 +71,8 @@
   // AVDAStateProvider implementation:
   const gfx::Size& GetSize() const override;
   base::WeakPtr<gpu::gles2::GLES2Decoder> GetGlDecoder() const override;
-  void PostError(const ::tracked_objects::Location& from_here,
-                 VideoDecodeAccelerator::Error error) override;
-
-  static VideoDecodeAccelerator::Capabilities GetCapabilities(
-      const gpu::GpuPreferences& gpu_preferences);
+  // Notifies the client about the error and sets |state_| to |ERROR|.
+  void NotifyError(Error error) override;
 
  private:
   friend class AVDAManager;
@@ -230,14 +230,6 @@
   // Notifies the client that the decoder was reset.
   void NotifyResetDone();
 
-  // Notifies about decoding errors.
-  // Note: you probably don't want to call this directly.  Use PostError or
-  // RETURN_ON_FAILURE, since we can defer error reporting to keep the pipeline
-  // from breaking.  NotifyError will do so immediately, PostError may wait.
-  // |token| has to match |error_sequence_token_|, or else it's assumed to be
-  // from a post that's prior to a previous reset, and ignored.
-  void NotifyError(VideoDecodeAccelerator::Error error, int token);
-
   // Start or stop our work-polling timer based on whether we did any work, and
   // how long it has been since we've done work.  Calling this with true will
   // start the timer.  Calling it with false may stop the timer.
@@ -375,9 +367,6 @@
   // from being sent after a reset.
   int error_sequence_token_;
 
-  // PostError will defer sending an error if and only if this is true.
-  bool defer_errors_;
-
   // True if and only if VDA initialization is deferred, and we have not yet
   // called NotifyInitializationComplete.
   bool deferred_initialization_pending_;
diff --git a/media/gpu/avda_picture_buffer_manager.cc b/media/gpu/avda_picture_buffer_manager.cc
index 4fe59e2d..a77de5a 100644
--- a/media/gpu/avda_picture_buffer_manager.cc
+++ b/media/gpu/avda_picture_buffer_manager.cc
@@ -28,16 +28,15 @@
 #include "ui/gl/scoped_binders.h"
 #include "ui/gl/scoped_make_current.h"
 
-// If !|ptr|, log a message, post an error to |state_provider_|, and
+// If !|ptr|, log a message, notify |state_provider_| of the error, and
 // return an optional value.
-#define RETURN_IF_NULL(ptr, ...)                                         \
-  do {                                                                   \
-    if (!(ptr)) {                                                        \
-      DLOG(ERROR) << "Got null for " << #ptr;                            \
-      state_provider_->PostError(FROM_HERE,                              \
-                                 VideoDecodeAccelerator::ILLEGAL_STATE); \
-      return __VA_ARGS__;                                                \
-    }                                                                    \
+#define RETURN_IF_NULL(ptr, ...)                                           \
+  do {                                                                     \
+    if (!(ptr)) {                                                          \
+      DLOG(ERROR) << "Got null for " << #ptr;                              \
+      state_provider_->NotifyError(VideoDecodeAccelerator::ILLEGAL_STATE); \
+      return __VA_ARGS__;                                                  \
+    }                                                                      \
   } while (0)
 
 namespace media {
diff --git a/media/gpu/avda_state_provider.h b/media/gpu/avda_state_provider.h
index 06498f7..819992d 100644
--- a/media/gpu/avda_state_provider.h
+++ b/media/gpu/avda_state_provider.h
@@ -26,11 +26,9 @@
   virtual const gfx::Size& GetSize() const = 0;
   virtual base::WeakPtr<gpu::gles2::GLES2Decoder> GetGlDecoder() const = 0;
 
-  // Helper function to report an error condition and stop decoding.
-  // This will post NotifyError(), and transition to the error state.
-  // It is meant to be called from the RETURN_ON_FAILURE macro.
-  virtual void PostError(const ::tracked_objects::Location& from_here,
-                         VideoDecodeAccelerator::Error error) = 0;
+  // Report a fatal error. This will post NotifyError(), and transition to the
+  // error state.
+  virtual void NotifyError(VideoDecodeAccelerator::Error error) = 0;
 
  protected:
   ~AVDAStateProvider() = default;
diff --git a/media/mojo/clients/mojo_cdm_factory.cc b/media/mojo/clients/mojo_cdm_factory.cc
index 3837b0f5..7b2da3c 100644
--- a/media/mojo/clients/mojo_cdm_factory.cc
+++ b/media/mojo/clients/mojo_cdm_factory.cc
@@ -41,6 +41,12 @@
     return;
   }
 
+// When MojoRenderer is used, the real Renderer is running in a remote process,
+// which cannot use an AesDecryptor running locally. In this case, always
+// create the MojoCdm, giving the remote CDM a chance to handle |key_system|.
+// Note: We should not run AesDecryptor in the browser process except for
+// testing. See http://crbug.com/441957
+#if !defined(ENABLE_MOJO_RENDERER)
   if (CanUseAesDecryptor(key_system)) {
     scoped_refptr<MediaKeys> cdm(
         new AesDecryptor(security_origin, session_message_cb, session_closed_cb,
@@ -49,6 +55,7 @@
         FROM_HERE, base::Bind(cdm_created_cb, cdm, ""));
     return;
   }
+#endif
 
   mojom::ContentDecryptionModulePtr cdm_ptr;
   service_manager::GetInterface<mojom::ContentDecryptionModule>(
diff --git a/media/mojo/services/BUILD.gn b/media/mojo/services/BUILD.gn
index 4223b61..e098339b1 100644
--- a/media/mojo/services/BUILD.gn
+++ b/media/mojo/services/BUILD.gn
@@ -168,7 +168,6 @@
 
 service_manifest("test_manifest") {
   name = "media_mojo_shell_unittests"
-  type = "exe"
   source = "test_manifest.json"
 }
 
@@ -188,6 +187,5 @@
 
 service_manifest("pipeline_test_manifest") {
   name = "media_pipeline_integration_unittests"
-  type = "exe"
   source = "pipeline_test_manifest.json"
 }
diff --git a/media/mojo/services/media_mojo_unittest.cc b/media/mojo/services/media_mojo_unittest.cc
index 257e6f8..332861d 100644
--- a/media/mojo/services/media_mojo_unittest.cc
+++ b/media/mojo/services/media_mojo_unittest.cc
@@ -65,7 +65,7 @@
 class MediaServiceTest : public service_manager::test::ServiceTest {
  public:
   MediaServiceTest()
-      : ServiceTest("exe:media_mojo_unittests"),
+      : ServiceTest("service:media_mojo_unittests"),
         renderer_client_binding_(&renderer_client_),
         video_stream_(DemuxerStream::VIDEO) {}
   ~MediaServiceTest() override {}
diff --git a/media/mojo/services/pipeline_test_manifest.json b/media/mojo/services/pipeline_test_manifest.json
index 33d1311..bfc0380 100644
--- a/media/mojo/services/pipeline_test_manifest.json
+++ b/media/mojo/services/pipeline_test_manifest.json
@@ -1,5 +1,5 @@
 {
-  "name": "exe:media_pipeline_integration_unittests",
+  "name": "service:media_pipeline_integration_unittests",
   "display_name": "Media Pipeline Integration Unittests",
   "interface_provider_specs": {
     "service_manager:connector": {
diff --git a/media/mojo/services/test_manifest.json b/media/mojo/services/test_manifest.json
index ef5c000..305e0e9 100644
--- a/media/mojo/services/test_manifest.json
+++ b/media/mojo/services/test_manifest.json
@@ -1,5 +1,5 @@
 {
-  "name": "exe:media_mojo_shell_unittests",
+  "name": "service:media_mojo_shell_unittests",
   "display_name": "Media Mojo Shell Unittests",
   "interface_provider_specs": {
     "service_manager:connector": {
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc
index ad92616b..7c8288a0 100644
--- a/media/test/pipeline_integration_test.cc
+++ b/media/test/pipeline_integration_test.cc
@@ -699,7 +699,7 @@
  public:
   PipelineIntegrationTestHost()
       : service_manager::test::ServiceTest(
-            "exe:media_pipeline_integration_shelltests") {}
+            "service:media_pipeline_integration_shelltests") {}
 
   void SetUp() override {
     ServiceTest::SetUp();
diff --git a/mojo/edk/system/broker_win.cc b/mojo/edk/system/broker_win.cc
index cfee14e2..65eadd4 100644
--- a/mojo/edk/system/broker_win.cc
+++ b/mojo/edk/system/broker_win.cc
@@ -7,6 +7,7 @@
 #include <limits>
 #include <utility>
 
+#include "base/debug/alias.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_piece.h"
 #include "mojo/edk/embedder/named_platform_handle.h"
@@ -53,6 +54,11 @@
                            kMaxBrokerMessageSize, &bytes_read, nullptr);
   if (!result) {
     PLOG(ERROR) << "Error reading broker pipe";
+
+    DWORD error = GetLastError();
+    base::debug::Alias(&platform_handle);
+    base::debug::Alias(&error);
+    CHECK(false);
     return nullptr;
   }
 
@@ -60,6 +66,11 @@
       Channel::Message::Deserialize(buffer, static_cast<size_t>(bytes_read));
   if (!message || message->payload_size() < sizeof(BrokerMessageHeader)) {
     LOG(ERROR) << "Invalid broker message";
+
+    base::debug::Alias(&buffer[0]);
+    base::debug::Alias(&bytes_read);
+    base::debug::Alias(message.get());
+    CHECK(false);
     return nullptr;
   }
 
@@ -67,6 +78,11 @@
       reinterpret_cast<const BrokerMessageHeader*>(message->payload());
   if (header->type != expected_type) {
     LOG(ERROR) << "Unexpected broker message type";
+
+    base::debug::Alias(&buffer[0]);
+    base::debug::Alias(&bytes_read);
+    base::debug::Alias(message.get());
+    CHECK(false);
     return nullptr;
   }
 
diff --git a/native_client_sdk/src/build_tools/build_sdk.py b/native_client_sdk/src/build_tools/build_sdk.py
index 55f29cf..7213502 100755
--- a/native_client_sdk/src/build_tools/build_sdk.py
+++ b/native_client_sdk/src/build_tools/build_sdk.py
@@ -866,7 +866,7 @@
     # Only archive sdk_tools/naclport/pnacl_component on linux.
     if platform == 'linux':
       BuildStepArchiveSDKTools()
-      #BuildStepArchivePNaClComponent(chrome_revision)
+      BuildStepArchivePNaClComponent(chrome_revision)
 
   return 0
 
diff --git a/net/net.gypi b/net/net.gypi
index 034954db..f34bff2 100644
--- a/net/net.gypi
+++ b/net/net.gypi
@@ -1953,8 +1953,6 @@
       'tools/quic/quic_simple_client_test.cc',
       'tools/quic/test_tools/mock_quic_server_session_visitor.cc',
       'tools/quic/test_tools/mock_quic_server_session_visitor.h',
-      'tools/quic/test_tools/push_promise_delegate.cc',
-      'tools/quic/test_tools/push_promise_delegate.h',
       'tools/quic/test_tools/quic_in_memory_cache_peer.cc',
       'tools/quic/test_tools/quic_in_memory_cache_peer.h',
       'tools/tld_cleanup/tld_cleanup_util_unittest.cc',
diff --git a/net/quic/chromium/crypto/proof_verifier_chromium.cc b/net/quic/chromium/crypto/proof_verifier_chromium.cc
index 546b0074..160191bf 100644
--- a/net/quic/chromium/crypto/proof_verifier_chromium.cc
+++ b/net/quic/chromium/crypto/proof_verifier_chromium.cc
@@ -426,6 +426,8 @@
     int ct_result = OK;
     if (verify_details_->ct_verify_result.cert_policy_compliance !=
             ct::CertPolicyCompliance::CERT_POLICY_COMPLIES_VIA_SCTS &&
+        verify_details_->ct_verify_result.cert_policy_compliance !=
+            ct::CertPolicyCompliance::CERT_POLICY_BUILD_NOT_TIMELY &&
         transport_security_state_->ShouldRequireCT(
             hostname_, cert_verify_result.verified_cert.get(),
             cert_verify_result.public_key_hashes)) {
diff --git a/net/quic/chromium/quic_chromium_client_session.h b/net/quic/chromium/quic_chromium_client_session.h
index e0a6d0d..04eeda4 100644
--- a/net/quic/chromium/quic_chromium_client_session.h
+++ b/net/quic/chromium/quic_chromium_client_session.h
@@ -307,7 +307,7 @@
 
   void OnPushStreamTimedOut(QuicStreamId stream_id) override;
 
-  void set_push_delegte(ServerPushDelegate* push_delegate) {
+  void set_push_delegate(ServerPushDelegate* push_delegate) {
     push_delegate_ = push_delegate;
   }
 
diff --git a/net/quic/chromium/quic_chromium_client_session_test.cc b/net/quic/chromium/quic_chromium_client_session_test.cc
index aca82cac..b0f1ad3 100644
--- a/net/quic/chromium/quic_chromium_client_session_test.cc
+++ b/net/quic/chromium/quic_chromium_client_session_test.cc
@@ -44,7 +44,6 @@
 #include "net/test/cert_test_util.h"
 #include "net/test/gtest_util.h"
 #include "net/test/test_data_directory.h"
-#include "net/tools/quic/test_tools/push_promise_delegate.h"
 #include "net/udp/datagram_client_socket.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
@@ -168,6 +167,7 @@
   MockCryptoClientStreamFactory crypto_client_stream_factory_;
   QuicClientPushPromiseIndex push_promise_index_;
   std::unique_ptr<QuicChromiumClientSession> session_;
+  TestServerPushDelegate test_push_delegate_;
   QuicConnectionVisitorInterface* visitor_;
   TestCompletionCallback callback_;
   QuicTestPacketMaker client_maker_;
@@ -324,6 +324,7 @@
   socket_data_.reset(new SequencedSocketData(reads, arraysize(reads), writes,
                                              arraysize(writes)));
   Initialize();
+  session_->set_push_delegate(&test_push_delegate_);
 
   ProofVerifyDetailsChromium details;
   details.cert_verify_result.verified_cert =
@@ -353,12 +354,12 @@
 
   // Initiate rendezvous.
   SpdyHeaderBlock client_request = promise_headers.Clone();
-  PushPromiseDelegate delegate(/*match=*/true);
+  TestPushPromiseDelegate delegate(/*match=*/true);
   promised->HandleClientRequest(client_request, &delegate);
 
   // Cancel the push before receiving the response to the pushed request.
   GURL pushed_url("https://www.example.org/pushed.jpg");
-  session_->CancelPush(pushed_url);
+  test_push_delegate_.CancelPush(pushed_url);
   EXPECT_TRUE(session_->GetPromisedByUrl(pushed_url.spec()));
 
   // Reset the stream now before tear down.
@@ -375,6 +376,7 @@
   socket_data_.reset(new SequencedSocketData(reads, arraysize(reads), writes,
                                              arraysize(writes)));
   Initialize();
+  session_->set_push_delegate(&test_push_delegate_);
 
   ProofVerifyDetailsChromium details;
   details.cert_verify_result.verified_cert =
@@ -403,7 +405,7 @@
   EXPECT_TRUE(promised);
   // Cancel the push before receiving the response to the pushed request.
   GURL pushed_url("https://www.example.org/pushed.jpg");
-  session_->CancelPush(pushed_url);
+  test_push_delegate_.CancelPush(pushed_url);
 
   EXPECT_FALSE(session_->GetPromisedByUrl(pushed_url.spec()));
   EXPECT_EQ(0u,
@@ -422,6 +424,7 @@
   socket_data_.reset(new SequencedSocketData(reads, arraysize(reads), writes,
                                              arraysize(writes)));
   Initialize();
+  session_->set_push_delegate(&test_push_delegate_);
 
   ProofVerifyDetailsChromium details;
   details.cert_verify_result.verified_cert =
@@ -455,7 +458,7 @@
   EXPECT_TRUE(promised);
   // Cancel the push after receiving data on the push stream.
   GURL pushed_url("https://www.example.org/pushed.jpg");
-  session_->CancelPush(pushed_url);
+  test_push_delegate_.CancelPush(pushed_url);
 
   EXPECT_FALSE(session_->GetPromisedByUrl(pushed_url.spec()));
   EXPECT_EQ(2u,
diff --git a/net/quic/core/quic_client_promised_info.cc b/net/quic/core/quic_client_promised_info.cc
index 6c099b37..1648a2bc 100644
--- a/net/quic/core/quic_client_promised_info.cc
+++ b/net/quic/core/quic_client_promised_info.cc
@@ -113,6 +113,10 @@
   }
 
   if (is_validating()) {
+    // The push promise has already been matched to another request though
+    // pending for validation. Returns QUIC_FAILURE to the caller as it couldn't
+    // match a new request any more. This will not affect the validation of the
+    // other request.
     return QUIC_FAILURE;
   }
 
diff --git a/net/quic/core/quic_client_promised_info_test.cc b/net/quic/core/quic_client_promised_info_test.cc
index abb93e0..3eee267 100644
--- a/net/quic/core/quic_client_promised_info_test.cc
+++ b/net/quic/core/quic_client_promised_info_test.cc
@@ -12,7 +12,6 @@
 #include "net/quic/test_tools/quic_client_promised_info_peer.h"
 #include "net/test/gtest_util.h"
 #include "net/tools/quic/quic_client_session.h"
-#include "net/tools/quic/test_tools/push_promise_delegate.h"
 
 using std::string;
 using testing::StrictMock;
@@ -190,7 +189,7 @@
   promise_stream->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
                                      headers);
 
-  PushPromiseDelegate delegate(/*match=*/false);
+  TestPushPromiseDelegate delegate(/*match=*/false);
   EXPECT_CALL(*connection_,
               SendRstStream(promise_id_, QUIC_PROMISE_VARY_MISMATCH, 0));
   EXPECT_CALL(session_, CloseStream(promise_id_));
@@ -202,11 +201,13 @@
   ReceivePromise(promise_id_);
 
   QuicClientPromisedInfo* promised = session_.GetPromisedById(promise_id_);
+  EXPECT_FALSE(promised->is_validating());
   ASSERT_NE(promised, nullptr);
 
   // Now initiate rendezvous.
-  PushPromiseDelegate delegate(/*match=*/true);
+  TestPushPromiseDelegate delegate(/*match=*/true);
   promised->HandleClientRequest(client_request_, &delegate);
+  EXPECT_TRUE(promised->is_validating());
 
   // Promise is still there, waiting for response.
   EXPECT_NE(session_.GetPromisedById(promise_id_), nullptr);
@@ -239,7 +240,7 @@
                                      headers);
 
   // Now initiate rendezvous.
-  PushPromiseDelegate delegate(/*match=*/true);
+  TestPushPromiseDelegate delegate(/*match=*/true);
   promised->HandleClientRequest(client_request_, &delegate);
 
   // Promise is gone
@@ -257,7 +258,7 @@
   ASSERT_NE(promised, nullptr);
 
   // Now initiate rendezvous.
-  PushPromiseDelegate delegate(/*match=*/true);
+  TestPushPromiseDelegate delegate(/*match=*/true);
   promised->HandleClientRequest(client_request_, &delegate);
 
   // Promise is still there, waiting for response.
@@ -297,7 +298,7 @@
   session_.SendRstStream(promise_id_, QUIC_STREAM_PEER_GOING_AWAY, 0);
 
   // Now initiate rendezvous.
-  PushPromiseDelegate delegate(/*match=*/true);
+  TestPushPromiseDelegate delegate(/*match=*/true);
   EXPECT_EQ(promised->HandleClientRequest(client_request_, &delegate),
             QUIC_FAILURE);
 
diff --git a/net/quic/core/quic_client_session_base.h b/net/quic/core/quic_client_session_base.h
index 46e0a1e..f4024e2 100644
--- a/net/quic/core/quic_client_session_base.h
+++ b/net/quic/core/quic_client_session_base.h
@@ -65,8 +65,8 @@
   // Called by |QuicSpdyClientStream| on receipt of PUSH_PROMISE, does
   // some session level validation and creates the
   // |QuicClientPromisedInfo| inserting into maps by (promised) id and
-  // url. Returns true if a new push promise is accepted. Reset the promised and
-  // returns false otherwiese.
+  // url. Returns true if a new push promise is accepted. Reset the promised
+  // stream and returns false otherwiese.
   virtual bool HandlePromised(QuicStreamId associated_id,
                               QuicStreamId promised_id,
                               const SpdyHeaderBlock& headers);
diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc
index 695a9f3..c97fe1f 100644
--- a/net/quic/test_tools/quic_test_utils.cc
+++ b/net/quic/test_tools/quic_test_utils.cc
@@ -443,6 +443,22 @@
       QuicServerSessionBase::GetCryptoStream());
 }
 
+TestPushPromiseDelegate::TestPushPromiseDelegate(bool match)
+    : match_(match), rendezvous_fired_(false), rendezvous_stream_(nullptr) {}
+
+bool TestPushPromiseDelegate::CheckVary(
+    const SpdyHeaderBlock& client_request,
+    const SpdyHeaderBlock& promise_request,
+    const SpdyHeaderBlock& promise_response) {
+  DVLOG(1) << "match " << match_;
+  return match_;
+}
+
+void TestPushPromiseDelegate::OnRendezvousResult(QuicSpdyStream* stream) {
+  rendezvous_fired_ = true;
+  rendezvous_stream_ = stream;
+}
+
 TestQuicSpdyClientSession::TestQuicSpdyClientSession(
     QuicConnection* connection,
     const QuicConfig& config,
diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h
index d4043de..f0773530 100644
--- a/net/quic/test_tools/quic_test_utils.h
+++ b/net/quic/test_tools/quic_test_utils.h
@@ -670,6 +670,25 @@
   DISALLOW_COPY_AND_ASSIGN(TestQuicSpdyServerSession);
 };
 
+class TestPushPromiseDelegate : public QuicClientPushPromiseIndex::Delegate {
+ public:
+  explicit TestPushPromiseDelegate(bool match);
+
+  bool CheckVary(const SpdyHeaderBlock& client_request,
+                 const SpdyHeaderBlock& promise_request,
+                 const SpdyHeaderBlock& promise_response) override;
+
+  void OnRendezvousResult(QuicSpdyStream* stream) override;
+
+  QuicSpdyStream* rendezvous_stream() { return rendezvous_stream_; }
+  bool rendezvous_fired() { return rendezvous_fired_; }
+
+ private:
+  bool match_;
+  bool rendezvous_fired_;
+  QuicSpdyStream* rendezvous_stream_;
+};
+
 class TestQuicSpdyClientSession : public QuicClientSessionBase {
  public:
   TestQuicSpdyClientSession(QuicConnection* connection,
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc
index bb76bf8c..a5d2afd 100644
--- a/net/socket/ssl_client_socket_impl.cc
+++ b/net/socket/ssl_client_socket_impl.cc
@@ -1556,6 +1556,8 @@
 
   if (ct_verify_result_.cert_policy_compliance !=
           ct::CertPolicyCompliance::CERT_POLICY_COMPLIES_VIA_SCTS &&
+      ct_verify_result_.cert_policy_compliance !=
+          ct::CertPolicyCompliance::CERT_POLICY_BUILD_NOT_TIMELY &&
       transport_security_state_->ShouldRequireCT(
           host_and_port_.host(), server_cert_verify_result_.verified_cert.get(),
           server_cert_verify_result_.public_key_hashes)) {
diff --git a/net/spdy/hpack/hpack_decoder.cc b/net/spdy/hpack/hpack_decoder.cc
index 16c7918e..575a5c1 100644
--- a/net/spdy/hpack/hpack_decoder.cc
+++ b/net/spdy/hpack/hpack_decoder.cc
@@ -233,6 +233,7 @@
                                   StringPiece* next_name) {
   uint32_t index_or_zero = 0;
   if (!input_stream->DecodeNextUint32(&index_or_zero)) {
+    DVLOG(1) << "Failed to decode the next uint.";
     return false;
   }
 
@@ -263,11 +264,13 @@
     bool result = input_stream->DecodeNextHuffmanString(buffer);
     *output = StringPiece(*buffer);
     return result;
-  }
-  if (input_stream->MatchPrefixAndConsume(kStringLiteralIdentityEncoded)) {
+  } else if (input_stream->MatchPrefixAndConsume(
+                 kStringLiteralIdentityEncoded)) {
     return input_stream->DecodeNextIdentityString(output);
+  } else {
+    DVLOG(1) << "String literal is neither Huffman nor identity encoded!";
+    return false;
   }
-  return false;
 }
 
 }  // namespace net
diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h
index b6ffec6..bebfcca 100644
--- a/net/spdy/spdy_framer.h
+++ b/net/spdy/spdy_framer.h
@@ -510,6 +510,7 @@
   // using zlib anyway.
 
   // For ease of testing and experimentation we can tweak compression on/off.
+  bool enable_compression() const { return enable_compression_; }
   void set_enable_compression(bool value) {
     enable_compression_ = value;
   }
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index f6b2534..8e2d6f09 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -633,6 +633,8 @@
 
   if (ssl_info.ct_cert_policy_compliance !=
           ct::CertPolicyCompliance::CERT_POLICY_COMPLIES_VIA_SCTS &&
+      ssl_info.ct_cert_policy_compliance !=
+          ct::CertPolicyCompliance::CERT_POLICY_BUILD_NOT_TIMELY &&
       transport_security_state->ShouldRequireCT(
           new_hostname, ssl_info.cert.get(), ssl_info.public_key_hashes)) {
     return false;
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
index cf9058d..c51e0c2 100644
--- a/net/spdy/spdy_session_unittest.cc
+++ b/net/spdy/spdy_session_unittest.cc
@@ -43,6 +43,7 @@
 
 using net::test::IsError;
 using net::test::IsOk;
+using net::test::TestServerPushDelegate;
 
 namespace net {
 
@@ -78,28 +79,6 @@
                CTRequirementLevel(const std::string& host));
 };
 
-class TestServerPushDelegate : public ServerPushDelegate {
- public:
-  explicit TestServerPushDelegate() {}
-
-  void OnPush(std::unique_ptr<ServerPushHelper> push_helper) override {
-    push_helpers[push_helper->GetURL()] = std::move(push_helper);
-  }
-
-  bool CancelPush(GURL url) {
-    auto itr = push_helpers.find(url);
-    if (itr == push_helpers.end())
-      return false;
-
-    itr->second->Cancel();
-    push_helpers.erase(itr);
-    return true;
-  }
-
- private:
-  std::map<GURL, std::unique_ptr<ServerPushHelper>> push_helpers;
-};
-
 }  // namespace
 
 class SpdySessionTest : public PlatformTest {
diff --git a/net/spdy/spdy_test_utils.cc b/net/spdy/spdy_test_utils.cc
index 610c091..e2c327ee 100644
--- a/net/spdy/spdy_test_utils.cc
+++ b/net/spdy/spdy_test_utils.cc
@@ -189,5 +189,22 @@
   header_bytes_parsed_ = header_bytes_parsed;
 }
 
+TestServerPushDelegate::TestServerPushDelegate() {}
+
+TestServerPushDelegate::~TestServerPushDelegate() {}
+
+void TestServerPushDelegate::OnPush(
+    std::unique_ptr<ServerPushHelper> push_helper) {
+  push_helpers[push_helper->GetURL()] = std::move(push_helper);
+}
+
+bool TestServerPushDelegate::CancelPush(GURL url) {
+  auto itr = push_helpers.find(url);
+  DCHECK(itr != push_helpers.end());
+  itr->second->Cancel();
+  push_helpers.erase(itr);
+  return true;
+}
+
 }  // namespace test
 }  // namespace net
diff --git a/net/spdy/spdy_test_utils.h b/net/spdy/spdy_test_utils.h
index 667169d..8e68a49c4 100644
--- a/net/spdy/spdy_test_utils.h
+++ b/net/spdy/spdy_test_utils.h
@@ -11,6 +11,7 @@
 #include <string>
 
 #include "base/strings/string_piece.h"
+#include "net/spdy/server_push_delegate.h"
 #include "net/spdy/spdy_bug_tracker.h"
 #include "net/spdy/spdy_header_block.h"
 #include "net/spdy/spdy_headers_handler_interface.h"
@@ -89,6 +90,21 @@
   DISALLOW_COPY_AND_ASSIGN(TestHeadersHandler);
 };
 
+// A test implementation of ServerPushDelegate that caches all the pushed
+// request and provides a interface to cancel the push given url.
+class TestServerPushDelegate : public ServerPushDelegate {
+ public:
+  explicit TestServerPushDelegate();
+  ~TestServerPushDelegate() override;
+
+  void OnPush(std::unique_ptr<ServerPushHelper> push_helper) override;
+
+  bool CancelPush(GURL url);
+
+ private:
+  std::map<GURL, std::unique_ptr<ServerPushHelper>> push_helpers;
+};
+
 }  // namespace test
 }  // namespace net
 
diff --git a/net/tools/quic/test_tools/push_promise_delegate.cc b/net/tools/quic/test_tools/push_promise_delegate.cc
deleted file mode 100644
index a9ba80e..0000000
--- a/net/tools/quic/test_tools/push_promise_delegate.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/tools/quic/test_tools/push_promise_delegate.h"
-
-namespace net {
-namespace test {
-PushPromiseDelegate::PushPromiseDelegate(bool match)
-    : match_(match), rendezvous_fired_(false), rendezvous_stream_(nullptr) {}
-
-bool PushPromiseDelegate::CheckVary(const SpdyHeaderBlock& client_request,
-                                    const SpdyHeaderBlock& promise_request,
-                                    const SpdyHeaderBlock& promise_response) {
-  DVLOG(1) << "match " << match_;
-  return match_;
-}
-
-void PushPromiseDelegate::OnRendezvousResult(QuicSpdyStream* stream) {
-  rendezvous_fired_ = true;
-  rendezvous_stream_ = stream;
-}
-
-}  // namespace test
-}  // namespace net
diff --git a/net/tools/quic/test_tools/push_promise_delegate.h b/net/tools/quic/test_tools/push_promise_delegate.h
deleted file mode 100644
index 4cd34cc..0000000
--- a/net/tools/quic/test_tools/push_promise_delegate.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_TOOLS_QUIC_TEST_TOOLS_PUSH_PROMISE_DELEGATE_H_
-#define NET_TOOLS_QUIC_TEST_TOOLS_PUSH_PROMISE_DELEGATE_H_
-
-#include "net/quic/core/quic_client_push_promise_index.h"
-
-namespace net {
-namespace test {
-
-class PushPromiseDelegate : public QuicClientPushPromiseIndex::Delegate {
- public:
-  explicit PushPromiseDelegate(bool match);
-
-  bool CheckVary(const SpdyHeaderBlock& client_request,
-                 const SpdyHeaderBlock& promise_request,
-                 const SpdyHeaderBlock& promise_response) override;
-
-  void OnRendezvousResult(QuicSpdyStream* stream) override;
-
-  QuicSpdyStream* rendezvous_stream() { return rendezvous_stream_; }
-  bool rendezvous_fired() { return rendezvous_fired_; }
-
- private:
-  bool match_;
-  bool rendezvous_fired_;
-  QuicSpdyStream* rendezvous_stream_;
-};
-
-}  // namespace test
-}  // namespace net
-
-#endif  // NET_TOOLS_QUIC_TEST_TOOLS_PUSH_PROMISE_DELEGATE_H_
diff --git a/remoting/client/plugin/pepper_video_renderer_3d.cc b/remoting/client/plugin/pepper_video_renderer_3d.cc
index 825d91974..aff694a2 100644
--- a/remoting/client/plugin/pepper_video_renderer_3d.cc
+++ b/remoting/client/plugin/pepper_video_renderer_3d.cc
@@ -107,7 +107,7 @@
                                gl_max_viewport_size_[0]),
                  std::min<int>(ceilf(size.height() * scale),
                                gl_max_viewport_size_[1]));
-  graphics_.ResizeBuffers(size.width(), size.height());
+  graphics_.ResizeBuffers(view_size_.width(), view_size_.height());
 
   force_repaint_ = true;
   PaintIfNeeded();
diff --git a/remoting/host/basic_desktop_environment.cc b/remoting/host/basic_desktop_environment.cc
index 849bac2..bc7d2ca 100644
--- a/remoting/host/basic_desktop_environment.cc
+++ b/remoting/host/basic_desktop_environment.cc
@@ -52,7 +52,7 @@
 std::unique_ptr<webrtc::MouseCursorMonitor>
 BasicDesktopEnvironment::CreateMouseCursorMonitor() {
   return base::MakeUnique<MouseCursorMonitorProxy>(video_capture_task_runner_,
-                                                   *desktop_capture_options_);
+                                                   desktop_capture_options());
 }
 
 std::string BasicDesktopEnvironment::GetCapabilities() const {
@@ -72,7 +72,7 @@
 
   std::unique_ptr<DesktopCapturerProxy> result(
       new DesktopCapturerProxy(video_capture_task_runner_));
-  result->CreateCapturer(*desktop_capture_options_);
+  result->CreateCapturer(desktop_capture_options());
   return std::move(result);
 }
 
@@ -80,17 +80,16 @@
     scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> video_capture_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
-    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
+    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
+    const DesktopEnvironmentOptions& options)
     : caller_task_runner_(caller_task_runner),
       video_capture_task_runner_(video_capture_task_runner),
       input_task_runner_(input_task_runner),
       ui_task_runner_(ui_task_runner),
-      desktop_capture_options_(new webrtc::DesktopCaptureOptions(
-          webrtc::DesktopCaptureOptions::CreateDefault())) {
+      options_(options) {
   DCHECK(caller_task_runner_->BelongsToCurrentThread());
-  desktop_capture_options_->set_detect_updated_region(true);
 #if defined(USE_X11)
-  IgnoreXServerGrabs(desktop_capture_options_->x_display()->display(), true);
+  IgnoreXServerGrabs(desktop_capture_options().x_display()->display(), true);
 #endif
 }
 
diff --git a/remoting/host/basic_desktop_environment.h b/remoting/host/basic_desktop_environment.h
index 222c176..24057f9 100644
--- a/remoting/host/basic_desktop_environment.h
+++ b/remoting/host/basic_desktop_environment.h
@@ -46,7 +46,8 @@
       scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
       scoped_refptr<base::SingleThreadTaskRunner> video_capture_task_runner,
       scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
-      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
+      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
+      const DesktopEnvironmentOptions& options);
 
   scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner() const {
     return caller_task_runner_;
@@ -65,8 +66,16 @@
     return ui_task_runner_;
   }
 
-  webrtc::DesktopCaptureOptions* desktop_capture_options() {
-    return desktop_capture_options_.get();
+  webrtc::DesktopCaptureOptions* mutable_desktop_capture_options() {
+    return options_.desktop_capture_options();
+  }
+
+  const webrtc::DesktopCaptureOptions& desktop_capture_options() const {
+    return *options_.desktop_capture_options();
+  }
+
+  const DesktopEnvironmentOptions& desktop_environment_options() const {
+    return options_;
   }
 
  private:
@@ -83,12 +92,7 @@
   // Used to run UI code.
   scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
 
-  // Options shared between |DesktopCapturer| and |MouseCursorMonitor|. It
-  // might contain expensive resources, thus justifying the sharing.
-  // Also: it's dynamically allocated to avoid having to bring in
-  // desktop_capture_options.h which brings in X11 headers which causes hard to
-  // find build errors.
-  std::unique_ptr<webrtc::DesktopCaptureOptions> desktop_capture_options_;
+  DesktopEnvironmentOptions options_;
 
   DISALLOW_COPY_AND_ASSIGN(BasicDesktopEnvironment);
 };
diff --git a/remoting/host/chromoting_host.cc b/remoting/host/chromoting_host.cc
index eb9e182..167fa0f9 100644
--- a/remoting/host/chromoting_host.cc
+++ b/remoting/host/chromoting_host.cc
@@ -140,7 +140,6 @@
     return;
 
   enable_curtaining_ = enable;
-  desktop_environment_factory_->SetEnableCurtaining(enable_curtaining_);
 
   // Disconnect all existing clients because they might be running not
   // curtained.
@@ -273,9 +272,12 @@
         video_encode_task_runner_, audio_task_runner_));
   }
 
+  DesktopEnvironmentOptions options =
+      DesktopEnvironmentOptions::CreateDefault();
+  options.set_enable_curtaining(enable_curtaining_);
   // Create a ClientSession object.
   clients_.push_back(base::MakeUnique<ClientSession>(
-      this, std::move(connection), desktop_environment_factory_,
+      this, std::move(connection), desktop_environment_factory_, options,
       max_session_duration_, pairing_registry_, extensions_.get()));
 }
 
diff --git a/remoting/host/chromoting_host.h b/remoting/host/chromoting_host.h
index 01a6d17..9a2d6e8 100644
--- a/remoting/host/chromoting_host.h
+++ b/remoting/host/chromoting_host.h
@@ -103,6 +103,8 @@
 
   // Enables/disables curtaining when one or more clients are connected.
   // Takes immediate effect if clients are already connected.
+  // TODO(zijiehe): Replace this function with
+  // SetDesktopEnvironmentOptions(const DesktopEnvironmentOptions&).
   void SetEnableCurtaining(bool enable);
 
   // Sets the maximum duration of any session. By default, a session has no
diff --git a/remoting/host/chromoting_host_unittest.cc b/remoting/host/chromoting_host_unittest.cc
index 5c631f0..15e1debb 100644
--- a/remoting/host/chromoting_host_unittest.cc
+++ b/remoting/host/chromoting_host_unittest.cc
@@ -129,7 +129,8 @@
     protocol::ConnectionToClient* connection_ptr = connection.get();
     std::unique_ptr<ClientSession> client(new ClientSession(
         host_.get(), std::move(connection), desktop_environment_factory_.get(),
-        base::TimeDelta(), nullptr, std::vector<HostExtension*>()));
+        DesktopEnvironmentOptions::CreateDefault(), base::TimeDelta(), nullptr,
+        std::vector<HostExtension*>()));
     ClientSession* client_ptr = client.get();
 
     connection_ptr->set_host_stub(client.get());
diff --git a/remoting/host/client_session.cc b/remoting/host/client_session.cc
index f3fcbc3..f8df3b7 100644
--- a/remoting/host/client_session.cc
+++ b/remoting/host/client_session.cc
@@ -46,6 +46,7 @@
     EventHandler* event_handler,
     std::unique_ptr<protocol::ConnectionToClient> connection,
     DesktopEnvironmentFactory* desktop_environment_factory,
+    const DesktopEnvironmentOptions& desktop_environment_options,
     const base::TimeDelta& max_duration,
     scoped_refptr<protocol::PairingRegistry> pairing_registry,
     const std::vector<HostExtension*>& extensions)
@@ -53,6 +54,7 @@
       connection_(std::move(connection)),
       client_jid_(connection_->session()->jid()),
       desktop_environment_factory_(desktop_environment_factory),
+      desktop_environment_options_(desktop_environment_options),
       input_tracker_(&host_input_filter_),
       remote_input_filter_(&input_tracker_),
       mouse_clamping_filter_(&remote_input_filter_),
@@ -241,8 +243,8 @@
 
   // Create the desktop environment. Drop the connection if it could not be
   // created for any reason (for instance the curtain could not initialize).
-  desktop_environment_ =
-      desktop_environment_factory_->Create(weak_factory_.GetWeakPtr());
+  desktop_environment_ = desktop_environment_factory_->Create(
+      weak_factory_.GetWeakPtr(), desktop_environment_options_);
   if (!desktop_environment_) {
     DisconnectSession(protocol::HOST_CONFIGURATION_ERROR);
     return;
diff --git a/remoting/host/client_session.h b/remoting/host/client_session.h
index 8f10358..77d5de6 100644
--- a/remoting/host/client_session.h
+++ b/remoting/host/client_session.h
@@ -18,6 +18,7 @@
 #include "base/timer/timer.h"
 #include "remoting/host/client_session_control.h"
 #include "remoting/host/client_session_details.h"
+#include "remoting/host/desktop_environment_options.h"
 #include "remoting/host/host_extension_session_manager.h"
 #include "remoting/host/remote_input_filter.h"
 #include "remoting/protocol/clipboard_echo_filter.h"
@@ -95,6 +96,7 @@
   ClientSession(EventHandler* event_handler,
                 std::unique_ptr<protocol::ConnectionToClient> connection,
                 DesktopEnvironmentFactory* desktop_environment_factory,
+                const DesktopEnvironmentOptions& desktop_environment_options,
                 const base::TimeDelta& max_duration,
                 scoped_refptr<protocol::PairingRegistry> pairing_registry,
                 const std::vector<HostExtension*>& extensions);
@@ -163,6 +165,9 @@
   // Used to create a DesktopEnvironment instance for this session.
   DesktopEnvironmentFactory* desktop_environment_factory_;
 
+  // The DesktopEnvironmentOptions used to initialize DesktopEnvironment.
+  DesktopEnvironmentOptions desktop_environment_options_;
+
   // The DesktopEnvironment instance for this session.
   std::unique_ptr<DesktopEnvironment> desktop_environment_;
 
diff --git a/remoting/host/client_session_unittest.cc b/remoting/host/client_session_unittest.cc
index a03a9ecd..920f3da 100644
--- a/remoting/host/client_session_unittest.cc
+++ b/remoting/host/client_session_unittest.cc
@@ -188,8 +188,9 @@
 
   client_session_.reset(
       new ClientSession(&session_event_handler_, std::move(connection),
-                        desktop_environment_factory_.get(), base::TimeDelta(),
-                        nullptr, extensions_));
+                        desktop_environment_factory_.get(),
+                        DesktopEnvironmentOptions::CreateDefault(),
+                        base::TimeDelta(), nullptr, extensions_));
 }
 
 void ClientSessionTest::ConnectClientSession() {
diff --git a/remoting/host/desktop_environment.h b/remoting/host/desktop_environment.h
index a98281c..525dfbe 100644
--- a/remoting/host/desktop_environment.h
+++ b/remoting/host/desktop_environment.h
@@ -12,6 +12,7 @@
 #include "base/callback_forward.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "remoting/host/desktop_environment_options.h"
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -70,10 +71,8 @@
   // failed to active for instance). |client_session_control| must outlive
   // the created desktop environment.
   virtual std::unique_ptr<DesktopEnvironment> Create(
-      base::WeakPtr<ClientSessionControl> client_session_control) = 0;
-
-  // Enables or disables the curtain mode.
-  virtual void SetEnableCurtaining(bool enable) {}
+      base::WeakPtr<ClientSessionControl> client_session_control,
+      const DesktopEnvironmentOptions& options) = 0;
 
   // Returns |true| if created |DesktopEnvironment| instances support audio
   // capture.
diff --git a/remoting/host/desktop_environment_options.cc b/remoting/host/desktop_environment_options.cc
index 6b62add..0607a6c1 100644
--- a/remoting/host/desktop_environment_options.cc
+++ b/remoting/host/desktop_environment_options.cc
@@ -4,10 +4,19 @@
 
 #include "remoting/host/desktop_environment_options.h"
 
+#include <utility>
+
 #include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h"
 
 namespace remoting {
 
+// static
+DesktopEnvironmentOptions DesktopEnvironmentOptions::CreateDefault() {
+  DesktopCaptureOptionsPtr capture_options =
+      webrtc::DesktopCaptureOptions::CreateDefault();
+  return DesktopEnvironmentOptions(std::move(capture_options));
+}
+
 DesktopEnvironmentOptions::DesktopEnvironmentOptions() = default;
 DesktopEnvironmentOptions::DesktopEnvironmentOptions(
     DesktopEnvironmentOptions&& other) = default;
@@ -21,10 +30,19 @@
 DesktopEnvironmentOptions::operator=(
     const DesktopEnvironmentOptions& other) = default;
 
+DesktopEnvironmentOptions::DesktopEnvironmentOptions(
+    DesktopCaptureOptionsPtr&& desktop_capture_options)
+    : desktop_capture_options_(std::move(desktop_capture_options)) {}
+
 DesktopEnvironmentOptions::
 DesktopCaptureOptionsPtr::DesktopCaptureOptionsPtr()
-    : desktop_capture_options(new webrtc::DesktopCaptureOptions(
-          webrtc::DesktopCaptureOptions::CreateDefault())) {
+    : DesktopCaptureOptionsPtr(webrtc::DesktopCaptureOptions()) {}
+
+DesktopEnvironmentOptions::
+DesktopCaptureOptionsPtr::DesktopCaptureOptionsPtr(
+    webrtc::DesktopCaptureOptions&& option)
+    : desktop_capture_options(
+        new webrtc::DesktopCaptureOptions(std::move(option))) {
   desktop_capture_options->set_detect_updated_region(true);
 }
 
@@ -36,7 +54,7 @@
 DesktopCaptureOptionsPtr::DesktopCaptureOptionsPtr(
     const DesktopCaptureOptionsPtr& other) {
   desktop_capture_options.reset(new webrtc::DesktopCaptureOptions(
-        *other.desktop_capture_options));
+      *other.desktop_capture_options));
 }
 
 DesktopEnvironmentOptions::
@@ -54,6 +72,11 @@
   return *this;
 }
 
+const webrtc::DesktopCaptureOptions*
+DesktopEnvironmentOptions::desktop_capture_options() const {
+  return desktop_capture_options_.desktop_capture_options.get();
+}
+
 webrtc::DesktopCaptureOptions*
 DesktopEnvironmentOptions::desktop_capture_options() {
   return desktop_capture_options_.desktop_capture_options.get();
diff --git a/remoting/host/desktop_environment_options.h b/remoting/host/desktop_environment_options.h
index 36b56bc..a2376883 100644
--- a/remoting/host/desktop_environment_options.h
+++ b/remoting/host/desktop_environment_options.h
@@ -7,6 +7,8 @@
 
 #include <memory>
 
+#include "base/memory/weak_ptr.h"
+
 namespace webrtc {
 class DesktopCaptureOptions;
 }  // namespace webrtc
@@ -17,6 +19,11 @@
 // control the behavior.
 class DesktopEnvironmentOptions final {
  public:
+  // Returns instance of DesktopEnvironmentOptions with default parameters, and
+  // initializes DesktopCaptureOptions by using
+  // DesktopCaptureOptions::CreateDefault().
+  static DesktopEnvironmentOptions CreateDefault();
+
   DesktopEnvironmentOptions();
   DesktopEnvironmentOptions(DesktopEnvironmentOptions&& other);
   DesktopEnvironmentOptions(const DesktopEnvironmentOptions& other);
@@ -31,6 +38,7 @@
   bool enable_user_interface() const;
   void set_enable_user_interface(bool enabled);
 
+  const webrtc::DesktopCaptureOptions* desktop_capture_options() const;
   webrtc::DesktopCaptureOptions* desktop_capture_options();
 
  private:
@@ -40,6 +48,7 @@
   // break build.
   struct DesktopCaptureOptionsPtr final {
     DesktopCaptureOptionsPtr();
+    DesktopCaptureOptionsPtr(webrtc::DesktopCaptureOptions&& option);
     DesktopCaptureOptionsPtr(DesktopCaptureOptionsPtr&& other);
     DesktopCaptureOptionsPtr(const DesktopCaptureOptionsPtr& other);
     ~DesktopCaptureOptionsPtr();
@@ -50,6 +59,8 @@
     std::unique_ptr<webrtc::DesktopCaptureOptions> desktop_capture_options;
   };
 
+  DesktopEnvironmentOptions(DesktopCaptureOptionsPtr&& desktop_capture_options);
+
   // True if the curtain mode should be enabled by the DesktopEnvironment
   // instances. Note, not all DesktopEnvironments support curtain mode.
   bool enable_curtaining_ = false;
diff --git a/remoting/host/desktop_process.cc b/remoting/host/desktop_process.cc
index 3b2ae57..e7cdb182 100644
--- a/remoting/host/desktop_process.cc
+++ b/remoting/host/desktop_process.cc
@@ -139,9 +139,9 @@
 #endif  // !defined(OS_WIN)
 
   // Create a desktop agent.
-  desktop_agent_ =
-      new DesktopSessionAgent(audio_task_runner, caller_task_runner_,
-                              input_task_runner_, io_task_runner_);
+  desktop_agent_ = new DesktopSessionAgent(
+      audio_task_runner, caller_task_runner_, input_task_runner_,
+      io_task_runner_, DesktopEnvironmentOptions::CreateDefault());
 
   // Start the agent and create an IPC channel to talk to it.
   mojo::ScopedMessagePipeHandle desktop_pipe =
diff --git a/remoting/host/desktop_session_agent.cc b/remoting/host/desktop_session_agent.cc
index df5c1b21..852c78a 100644
--- a/remoting/host/desktop_session_agent.cc
+++ b/remoting/host/desktop_session_agent.cc
@@ -159,11 +159,13 @@
     scoped_refptr<AutoThreadTaskRunner> audio_capture_task_runner,
     scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
     scoped_refptr<AutoThreadTaskRunner> input_task_runner,
-    scoped_refptr<AutoThreadTaskRunner> io_task_runner)
+    scoped_refptr<AutoThreadTaskRunner> io_task_runner,
+    const DesktopEnvironmentOptions& desktop_environment_options)
     : audio_capture_task_runner_(audio_capture_task_runner),
       caller_task_runner_(caller_task_runner),
       input_task_runner_(input_task_runner),
       io_task_runner_(io_task_runner),
+      desktop_environment_options_(desktop_environment_options),
       weak_factory_(this) {
   DCHECK(caller_task_runner_->BelongsToCurrentThread());
 }
@@ -265,13 +267,13 @@
   started_ = true;
   client_jid_ = authenticated_jid;
 
+  DesktopEnvironmentOptions options = desktop_environment_options_;
   // Enable the curtain mode.
-  delegate_->desktop_environment_factory().SetEnableCurtaining(
-      virtual_terminal);
+  options.set_enable_curtaining(virtual_terminal);
 
   // Create a desktop environment for the new session.
   desktop_environment_ = delegate_->desktop_environment_factory().Create(
-      weak_factory_.GetWeakPtr());
+      weak_factory_.GetWeakPtr(), options);
 
   // Create the session controller and set the initial screen resolution.
   screen_controls_ = desktop_environment_->CreateScreenControls();
diff --git a/remoting/host/desktop_session_agent.h b/remoting/host/desktop_session_agent.h
index 9e7afba..d31ad8b 100644
--- a/remoting/host/desktop_session_agent.h
+++ b/remoting/host/desktop_session_agent.h
@@ -19,6 +19,7 @@
 #include "ipc/ipc_listener.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "remoting/host/client_session_control.h"
+#include "remoting/host/desktop_environment_options.h"
 #include "remoting/protocol/clipboard_stub.h"
 #include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h"
 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
@@ -70,7 +71,8 @@
       scoped_refptr<AutoThreadTaskRunner> audio_capture_task_runner,
       scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
       scoped_refptr<AutoThreadTaskRunner> input_task_runner,
-      scoped_refptr<AutoThreadTaskRunner> io_task_runner);
+      scoped_refptr<AutoThreadTaskRunner> io_task_runner,
+      const DesktopEnvironmentOptions& options);
 
   // IPC::Listener implementation.
   bool OnMessageReceived(const IPC::Message& message) override;
@@ -190,6 +192,8 @@
   // before it's received.
   std::unique_ptr<webrtc::DesktopFrame> last_frame_;
 
+  DesktopEnvironmentOptions desktop_environment_options_;
+
   // Used to disable callbacks to |this|.
   base::WeakPtrFactory<DesktopSessionAgent> weak_factory_;
 
diff --git a/remoting/host/fake_desktop_environment.cc b/remoting/host/fake_desktop_environment.cc
index d566b7a..3b1281e 100644
--- a/remoting/host/fake_desktop_environment.cc
+++ b/remoting/host/fake_desktop_environment.cc
@@ -111,7 +111,8 @@
 
 // DesktopEnvironmentFactory implementation.
 std::unique_ptr<DesktopEnvironment> FakeDesktopEnvironmentFactory::Create(
-    base::WeakPtr<ClientSessionControl> client_session_control) {
+    base::WeakPtr<ClientSessionControl> client_session_control,
+    const DesktopEnvironmentOptions& options) {
   std::unique_ptr<FakeDesktopEnvironment> result(
       new FakeDesktopEnvironment(capture_thread_));
   result->set_frame_generator(frame_generator_);
@@ -119,8 +120,6 @@
   return std::move(result);
 }
 
-void FakeDesktopEnvironmentFactory::SetEnableCurtaining(bool enable) {}
-
 bool FakeDesktopEnvironmentFactory::SupportsAudioCapture() const {
   return false;
 }
diff --git a/remoting/host/fake_desktop_environment.h b/remoting/host/fake_desktop_environment.h
index f918878..956ca505 100644
--- a/remoting/host/fake_desktop_environment.h
+++ b/remoting/host/fake_desktop_environment.h
@@ -11,7 +11,6 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
 #include "base/single_thread_task_runner.h"
 #include "remoting/host/desktop_environment.h"
 #include "remoting/host/fake_mouse_cursor_monitor.h"
@@ -123,8 +122,8 @@
 
   // DesktopEnvironmentFactory implementation.
   std::unique_ptr<DesktopEnvironment> Create(
-      base::WeakPtr<ClientSessionControl> client_session_control) override;
-  void SetEnableCurtaining(bool enable) override;
+      base::WeakPtr<ClientSessionControl> client_session_control,
+      const DesktopEnvironmentOptions& options) override;
   bool SupportsAudioCapture() const override;
 
   base::WeakPtr<FakeDesktopEnvironment> last_desktop_environment() {
diff --git a/remoting/host/host_mock_objects.cc b/remoting/host/host_mock_objects.cc
index 3253e46..b6110fc 100644
--- a/remoting/host/host_mock_objects.cc
+++ b/remoting/host/host_mock_objects.cc
@@ -51,7 +51,8 @@
 MockDesktopEnvironmentFactory::~MockDesktopEnvironmentFactory() {}
 
 std::unique_ptr<DesktopEnvironment> MockDesktopEnvironmentFactory::Create(
-    base::WeakPtr<ClientSessionControl> client_session_control) {
+    base::WeakPtr<ClientSessionControl> client_session_control,
+    const DesktopEnvironmentOptions& options) {
   return base::WrapUnique(CreatePtr());
 }
 
diff --git a/remoting/host/host_mock_objects.h b/remoting/host/host_mock_objects.h
index 1af62f4a..47c5343 100644
--- a/remoting/host/host_mock_objects.h
+++ b/remoting/host/host_mock_objects.h
@@ -112,7 +112,8 @@
   MOCK_CONST_METHOD0(SupportsAudioCapture, bool());
 
   std::unique_ptr<DesktopEnvironment> Create(
-      base::WeakPtr<ClientSessionControl> client_session_control) override;
+      base::WeakPtr<ClientSessionControl> client_session_control,
+      const DesktopEnvironmentOptions& options) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockDesktopEnvironmentFactory);
diff --git a/remoting/host/ipc_desktop_environment.cc b/remoting/host/ipc_desktop_environment.cc
index cf1f9a0..21e2319 100644
--- a/remoting/host/ipc_desktop_environment.cc
+++ b/remoting/host/ipc_desktop_environment.cc
@@ -90,19 +90,14 @@
 IpcDesktopEnvironmentFactory::~IpcDesktopEnvironmentFactory() {}
 
 std::unique_ptr<DesktopEnvironment> IpcDesktopEnvironmentFactory::Create(
-    base::WeakPtr<ClientSessionControl> client_session_control) {
+    base::WeakPtr<ClientSessionControl> client_session_control,
+    const DesktopEnvironmentOptions& options) {
   DCHECK(caller_task_runner_->BelongsToCurrentThread());
 
   return base::MakeUnique<IpcDesktopEnvironment>(
       audio_task_runner_, caller_task_runner_, io_task_runner_,
       client_session_control, connector_factory_.GetWeakPtr(),
-      curtain_enabled_);
-}
-
-void IpcDesktopEnvironmentFactory::SetEnableCurtaining(bool enable) {
-  DCHECK(caller_task_runner_->BelongsToCurrentThread());
-
-  curtain_enabled_ = enable;
+      options.enable_curtaining());
 }
 
 bool IpcDesktopEnvironmentFactory::SupportsAudioCapture() const {
diff --git a/remoting/host/ipc_desktop_environment.h b/remoting/host/ipc_desktop_environment.h
index 2e478bf3..b4bb425 100644
--- a/remoting/host/ipc_desktop_environment.h
+++ b/remoting/host/ipc_desktop_environment.h
@@ -82,8 +82,8 @@
 
   // DesktopEnvironmentFactory implementation.
   std::unique_ptr<DesktopEnvironment> Create(
-      base::WeakPtr<ClientSessionControl> client_session_control) override;
-  void SetEnableCurtaining(bool enable) override;
+      base::WeakPtr<ClientSessionControl> client_session_control,
+      const DesktopEnvironmentOptions& options) override;
   bool SupportsAudioCapture() const override;
 
   // DesktopSessionConnector implementation.
@@ -110,9 +110,6 @@
   // Task runner used for running background I/O.
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
 
-  // True if curtain mode is enabled.
-  bool curtain_enabled_ = false;
-
   // IPC channel connected to the daemon process.
   IPC::Sender* daemon_channel_;
 
diff --git a/remoting/host/ipc_desktop_environment_unittest.cc b/remoting/host/ipc_desktop_environment_unittest.cc
index 96236ba..c672933 100644
--- a/remoting/host/ipc_desktop_environment_unittest.cc
+++ b/remoting/host/ipc_desktop_environment_unittest.cc
@@ -310,7 +310,8 @@
   desktop_environment_factory_.reset(new IpcDesktopEnvironmentFactory(
       task_runner_, task_runner_, io_task_runner_, &daemon_channel_));
   desktop_environment_ = desktop_environment_factory_->Create(
-      client_session_control_factory_.GetWeakPtr());
+      client_session_control_factory_.GetWeakPtr(),
+      DesktopEnvironmentOptions());
 
   screen_controls_ = desktop_environment_->CreateScreenControls();
 
@@ -482,7 +483,8 @@
 TEST_F(IpcDesktopEnvironmentTest, TouchEventsCapabilities) {
   // Create an environment with multi touch enabled.
   desktop_environment_ = desktop_environment_factory_->Create(
-      client_session_control_factory_.GetWeakPtr());
+      client_session_control_factory_.GetWeakPtr(),
+      DesktopEnvironmentOptions());
 
   std::unique_ptr<protocol::MockClipboardStub> clipboard_stub(
       new protocol::MockClipboardStub());
diff --git a/remoting/host/it2me_desktop_environment.cc b/remoting/host/it2me_desktop_environment.cc
index fd0b9b9..a733073 100644
--- a/remoting/host/it2me_desktop_environment.cc
+++ b/remoting/host/it2me_desktop_environment.cc
@@ -32,19 +32,19 @@
     scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
     base::WeakPtr<ClientSessionControl> client_session_control,
-    bool enable_user_interface)
+    const DesktopEnvironmentOptions& options)
     : BasicDesktopEnvironment(caller_task_runner,
                               video_capture_task_runner,
                               input_task_runner,
-                              ui_task_runner) {
+                              ui_task_runner,
+                              options) {
   DCHECK(caller_task_runner->BelongsToCurrentThread());
 
   // Create the local input monitor.
-  local_input_monitor_ = LocalInputMonitor::Create(caller_task_runner,
-                                                   input_task_runner,
-                                                   ui_task_runner,
-                                                   client_session_control);
-
+  local_input_monitor_ = LocalInputMonitor::Create(
+      caller_task_runner, input_task_runner, ui_task_runner,
+      client_session_control);
+  bool enable_user_interface = options.enable_user_interface();
   // The host UI should be created on the UI thread.
 #if defined(OS_MACOSX)
   // Don't try to display any UI on top of the system's login screen as this
@@ -84,12 +84,13 @@
 It2MeDesktopEnvironmentFactory::~It2MeDesktopEnvironmentFactory() {}
 
 std::unique_ptr<DesktopEnvironment> It2MeDesktopEnvironmentFactory::Create(
-    base::WeakPtr<ClientSessionControl> client_session_control) {
+    base::WeakPtr<ClientSessionControl> client_session_control,
+    const DesktopEnvironmentOptions& options) {
   DCHECK(caller_task_runner()->BelongsToCurrentThread());
 
   return base::WrapUnique(new It2MeDesktopEnvironment(
       caller_task_runner(), video_capture_task_runner(), input_task_runner(),
-      ui_task_runner(), client_session_control, enable_user_interface_));
+      ui_task_runner(), client_session_control, options));
 }
 
 }  // namespace remoting
diff --git a/remoting/host/it2me_desktop_environment.h b/remoting/host/it2me_desktop_environment.h
index 7adfc73..e247e8b3 100644
--- a/remoting/host/it2me_desktop_environment.h
+++ b/remoting/host/it2me_desktop_environment.h
@@ -30,7 +30,7 @@
       scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
       scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
       base::WeakPtr<ClientSessionControl> client_session_control,
-      bool enable_user_interface);
+      const DesktopEnvironmentOptions& options);
 
  private:
   // Presents the continue window to the local user.
@@ -57,15 +57,10 @@
 
   // DesktopEnvironmentFactory interface.
   std::unique_ptr<DesktopEnvironment> Create(
-      base::WeakPtr<ClientSessionControl> client_session_control) override;
-
-  void set_enable_user_interface(bool enabled) {
-    enable_user_interface_ = enabled;
-  }
+      base::WeakPtr<ClientSessionControl> client_session_control,
+      const DesktopEnvironmentOptions& options) override;
 
  private:
-  bool enable_user_interface_ = true;
-
   DISALLOW_COPY_AND_ASSIGN(It2MeDesktopEnvironmentFactory);
 };
 
diff --git a/remoting/host/me2me_desktop_environment.cc b/remoting/host/me2me_desktop_environment.cc
index aca802a..23d4da02 100644
--- a/remoting/host/me2me_desktop_environment.cc
+++ b/remoting/host/me2me_desktop_environment.cc
@@ -63,29 +63,32 @@
     scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> video_capture_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
-    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
+    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
+    const DesktopEnvironmentOptions& options)
     : BasicDesktopEnvironment(caller_task_runner,
                               video_capture_task_runner,
                               input_task_runner,
-                              ui_task_runner) {
+                              ui_task_runner,
+                              options) {
   DCHECK(caller_task_runner->BelongsToCurrentThread());
 
+  // TODO(zijiehe): This logic should belong to RemotingMe2MeHost, instead of
+  // Me2MeDesktopEnvironment, which does not take response to create a new
+  // session.
   // X DAMAGE is not enabled by default, since it is broken on many systems -
   // see http://crbug.com/73423. It's safe to enable it here because it works
   // properly under Xvfb.
-  desktop_capture_options()->set_use_update_notifications(true);
+  mutable_desktop_capture_options()->set_use_update_notifications(true);
 }
 
 bool Me2MeDesktopEnvironment::InitializeSecurity(
-    base::WeakPtr<ClientSessionControl> client_session_control,
-    bool curtain_enabled) {
+    base::WeakPtr<ClientSessionControl> client_session_control) {
   DCHECK(caller_task_runner()->BelongsToCurrentThread());
 
   // Detach the session from the local console if the caller requested.
-  if (curtain_enabled) {
-    curtain_ = CurtainMode::Create(caller_task_runner(),
-                                   ui_task_runner(),
-                                   client_session_control);
+  if (desktop_environment_options().enable_curtaining()) {
+    curtain_ = CurtainMode::Create(
+        caller_task_runner(), ui_task_runner(), client_session_control);
     if (!curtain_->Activate()) {
       LOG(ERROR) << "Failed to activate the curtain mode.";
       curtain_ = nullptr;
@@ -96,7 +99,6 @@
 
   // Otherwise, if the session is shared with the local user start monitoring
   // the local input and create the in-session UI.
-
 #if defined(OS_LINUX)
   bool want_user_interface = false;
 #elif defined(OS_MACOSX)
@@ -109,16 +111,16 @@
   // function to be used here and in CurtainMode::ActivateCurtain().
   bool want_user_interface = getuid() != 0;
 #else
-  bool want_user_interface = true;
+  bool want_user_interface =
+      desktop_environment_options().enable_user_interface();
 #endif
 
   // Create the disconnect window.
   if (want_user_interface) {
     // Create the local input monitor.
-    local_input_monitor_ = LocalInputMonitor::Create(caller_task_runner(),
-                                                     input_task_runner(),
-                                                     ui_task_runner(),
-                                                     client_session_control);
+    local_input_monitor_ = LocalInputMonitor::Create(
+        caller_task_runner(), input_task_runner(), ui_task_runner(),
+        client_session_control);
 
     disconnect_window_ = HostWindow::CreateDisconnectWindow();
     disconnect_window_.reset(new HostWindowProxy(
@@ -143,25 +145,20 @@
 }
 
 std::unique_ptr<DesktopEnvironment> Me2MeDesktopEnvironmentFactory::Create(
-    base::WeakPtr<ClientSessionControl> client_session_control) {
+    base::WeakPtr<ClientSessionControl> client_session_control,
+    const DesktopEnvironmentOptions& options) {
   DCHECK(caller_task_runner()->BelongsToCurrentThread());
 
   std::unique_ptr<Me2MeDesktopEnvironment> desktop_environment(
       new Me2MeDesktopEnvironment(caller_task_runner(),
                                   video_capture_task_runner(),
-                                  input_task_runner(), ui_task_runner()));
-  if (!desktop_environment->InitializeSecurity(client_session_control,
-                                               curtain_enabled_)) {
+                                  input_task_runner(), ui_task_runner(),
+                                  options));
+  if (!desktop_environment->InitializeSecurity(client_session_control)) {
     return nullptr;
   }
 
   return std::move(desktop_environment);
 }
 
-void Me2MeDesktopEnvironmentFactory::SetEnableCurtaining(bool enable) {
-  DCHECK(caller_task_runner()->BelongsToCurrentThread());
-
-  curtain_enabled_ = enable;
-}
-
 }  // namespace remoting
diff --git a/remoting/host/me2me_desktop_environment.h b/remoting/host/me2me_desktop_environment.h
index 1fcb49f40..e4930d15 100644
--- a/remoting/host/me2me_desktop_environment.h
+++ b/remoting/host/me2me_desktop_environment.h
@@ -30,13 +30,13 @@
       scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
       scoped_refptr<base::SingleThreadTaskRunner> video_capture_task_runner,
       scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
-      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
+      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
+      const DesktopEnvironmentOptions& options);
 
   // Initializes security features of the desktop environment (the curtain mode
   // and in-session UI).
   bool InitializeSecurity(
-      base::WeakPtr<ClientSessionControl> client_session_control,
-      bool curtain_enabled);
+      base::WeakPtr<ClientSessionControl> client_session_control);
 
  private:
   // "Curtains" the session making sure it is disconnected from the local
@@ -64,16 +64,10 @@
 
   // DesktopEnvironmentFactory interface.
   std::unique_ptr<DesktopEnvironment> Create(
-      base::WeakPtr<ClientSessionControl> client_session_control) override;
-  void SetEnableCurtaining(bool enable) override;
-
- protected:
-  bool curtain_enabled() const { return curtain_enabled_; }
+      base::WeakPtr<ClientSessionControl> client_session_control,
+      const DesktopEnvironmentOptions& options) override;
 
  private:
-  // True if curtain mode is enabled.
-  bool curtain_enabled_ = false;
-
   DISALLOW_COPY_AND_ASSIGN(Me2MeDesktopEnvironmentFactory);
 };
 
diff --git a/remoting/host/single_window_desktop_environment.cc b/remoting/host/single_window_desktop_environment.cc
index 892d814..216e85c 100644
--- a/remoting/host/single_window_desktop_environment.cc
+++ b/remoting/host/single_window_desktop_environment.cc
@@ -33,7 +33,9 @@
       scoped_refptr<base::SingleThreadTaskRunner> video_capture_task_runner,
       scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
       scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
-      webrtc::DesktopCapturer::SourceId window_id);
+      webrtc::DesktopCapturer::SourceId window_id,
+      base::WeakPtr<ClientSessionControl> client_session_control,
+      const DesktopEnvironmentOptions& options);
 
  private:
   webrtc::DesktopCapturer::SourceId window_id_;
@@ -73,11 +75,14 @@
     scoped_refptr<base::SingleThreadTaskRunner> video_capture_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
-    webrtc::WindowId window_id)
+    webrtc::WindowId window_id,
+    base::WeakPtr<ClientSessionControl> client_session_control,
+    const DesktopEnvironmentOptions& options)
     : BasicDesktopEnvironment(caller_task_runner,
                               video_capture_task_runner,
                               input_task_runner,
-                              ui_task_runner),
+                              ui_task_runner,
+                              options),
       window_id_(window_id) {}
 
 SingleWindowDesktopEnvironmentFactory::SingleWindowDesktopEnvironmentFactory(
@@ -98,12 +103,13 @@
 
 std::unique_ptr<DesktopEnvironment>
 SingleWindowDesktopEnvironmentFactory::Create(
-    base::WeakPtr<ClientSessionControl> client_session_control) {
+    base::WeakPtr<ClientSessionControl> client_session_control,
+    const DesktopEnvironmentOptions& options) {
   DCHECK(caller_task_runner()->BelongsToCurrentThread());
 
   return base::WrapUnique(new SingleWindowDesktopEnvironment(
       caller_task_runner(), video_capture_task_runner(), input_task_runner(),
-      ui_task_runner(), window_id_));
+      ui_task_runner(), window_id_, client_session_control, options));
 }
 
 }  // namespace remoting
diff --git a/remoting/host/single_window_desktop_environment.h b/remoting/host/single_window_desktop_environment.h
index 5c8d17c..4af38b2 100644
--- a/remoting/host/single_window_desktop_environment.h
+++ b/remoting/host/single_window_desktop_environment.h
@@ -26,7 +26,8 @@
 
   // DesktopEnvironmentFactory interface.
   std::unique_ptr<DesktopEnvironment> Create(
-      base::WeakPtr<ClientSessionControl> client_session_control) override;
+      base::WeakPtr<ClientSessionControl> client_session_control,
+      const DesktopEnvironmentOptions& options) override;
 
  private:
   webrtc::WindowId window_id_;
diff --git a/remoting/host/win/session_desktop_environment.cc b/remoting/host/win/session_desktop_environment.cc
index 24e5a147..c074c0ea 100644
--- a/remoting/host/win/session_desktop_environment.cc
+++ b/remoting/host/win/session_desktop_environment.cc
@@ -35,11 +35,13 @@
     scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
     const base::Closure& inject_sas,
-    const base::Closure& lock_workstation)
+    const base::Closure& lock_workstation,
+    const DesktopEnvironmentOptions& options)
     : Me2MeDesktopEnvironment(caller_task_runner,
                               video_capture_task_runner,
                               input_task_runner,
-                              ui_task_runner),
+                              ui_task_runner,
+                              options),
       inject_sas_(inject_sas),
       lock_workstation_(lock_workstation) {}
 
@@ -62,16 +64,17 @@
 SessionDesktopEnvironmentFactory::~SessionDesktopEnvironmentFactory() {}
 
 std::unique_ptr<DesktopEnvironment> SessionDesktopEnvironmentFactory::Create(
-    base::WeakPtr<ClientSessionControl> client_session_control) {
+    base::WeakPtr<ClientSessionControl> client_session_control,
+    const DesktopEnvironmentOptions& options) {
   DCHECK(caller_task_runner()->BelongsToCurrentThread());
 
   std::unique_ptr<SessionDesktopEnvironment> desktop_environment(
       new SessionDesktopEnvironment(caller_task_runner(),
                                     video_capture_task_runner(),
                                     input_task_runner(), ui_task_runner(),
-                                    inject_sas_, lock_workstation_));
-  if (!desktop_environment->InitializeSecurity(client_session_control,
-                                               curtain_enabled())) {
+                                    inject_sas_, lock_workstation_,
+                                    options));
+  if (!desktop_environment->InitializeSecurity(client_session_control)) {
     return nullptr;
   }
 
diff --git a/remoting/host/win/session_desktop_environment.h b/remoting/host/win/session_desktop_environment.h
index ecc13b758..24f88f226 100644
--- a/remoting/host/win/session_desktop_environment.h
+++ b/remoting/host/win/session_desktop_environment.h
@@ -30,7 +30,8 @@
       scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
       scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
       const base::Closure& inject_sas,
-      const base::Closure& lock_workstation);
+      const base::Closure& lock_workstation,
+      const DesktopEnvironmentOptions& options);
 
   // Used to ask the daemon to inject Secure Attention Sequence.
   base::Closure inject_sas_;
@@ -55,7 +56,8 @@
 
   // DesktopEnvironmentFactory implementation.
   std::unique_ptr<DesktopEnvironment> Create(
-      base::WeakPtr<ClientSessionControl> client_session_control) override;
+      base::WeakPtr<ClientSessionControl> client_session_control,
+      const DesktopEnvironmentOptions& options) override;
 
  private:
   // Used to ask the daemon to inject Secure Attention Sequence.
diff --git a/remoting/test/it2me_standalone_host.cc b/remoting/test/it2me_standalone_host.cc
index f6b1951..e6b74de 100644
--- a/remoting/test/it2me_standalone_host.cc
+++ b/remoting/test/it2me_standalone_host.cc
@@ -54,7 +54,6 @@
     config_(protocol::SessionConfig::ForTestWithAudio()),
 #endif
     event_logger_(&connection_) {
-  factory_.set_enable_user_interface(false);
   EXPECT_CALL(*static_cast<MockSession*>(connection_.session()), jid())
       .WillRepeatedly(testing::ReturnRef(session_jid_));
   EXPECT_CALL(*static_cast<MockSession*>(connection_.session()), config())
@@ -82,9 +81,13 @@
 }
 
 void It2MeStandaloneHost::Connect() {
+  DesktopEnvironmentOptions options =
+      DesktopEnvironmentOptions::CreateDefault();
+  options.set_enable_user_interface(false);
   session_.reset(new ClientSession(
       &handler_, std::unique_ptr<protocol::ConnectionToClient>(&connection_),
-      &factory_, base::TimeDelta(), scoped_refptr<protocol::PairingRegistry>(),
+      &factory_, options, base::TimeDelta(),
+      scoped_refptr<protocol::PairingRegistry>(),
       std::vector<HostExtension*>()));
   session_->OnConnectionAuthenticated();
   session_->OnConnectionChannelsConnected();
diff --git a/services/catalog/catalog.cc b/services/catalog/catalog.cc
index 14749afd..c23af1e 100644
--- a/services/catalog/catalog.cc
+++ b/services/catalog/catalog.cc
@@ -40,23 +40,15 @@
 }
 
 base::FilePath GetPathForApplicationName(const std::string& application_name) {
+  static const char kServicePrefix[] = "service:";
   std::string path = application_name;
-  const bool is_service =
-      base::StartsWith(path, "service:", base::CompareCase::INSENSITIVE_ASCII);
-  const bool is_exe =
-      !is_service &&
-      base::StartsWith(path, "exe:", base::CompareCase::INSENSITIVE_ASCII);
-  if (!is_service && !is_exe)
+  const bool is_service = base::StartsWith(
+      path, kServicePrefix, base::CompareCase::INSENSITIVE_ASCII);
+  if (!is_service)
     return base::FilePath();
   if (path.find('.') != std::string::npos)
     return base::FilePath();
-  if (is_service) {
-    path.erase(path.begin(),
-               path.begin() + strlen(service_manager::kNameType_Service) + 1);
-  } else {
-    path.erase(path.begin(),
-               path.begin() + strlen(service_manager::kNameType_Exe) + 1);
-  }
+  path.erase(path.begin(), path.begin() + strlen(kServicePrefix));
   base::TrimString(path, "/", &path);
   size_t end_of_name = path.find('/');
   if (end_of_name != std::string::npos)
diff --git a/services/catalog/instance.cc b/services/catalog/instance.cc
index 6572d99..2ec2f5bc 100644
--- a/services/catalog/instance.cc
+++ b/services/catalog/instance.cc
@@ -58,14 +58,12 @@
 ////////////////////////////////////////////////////////////////////////////////
 // Instance, service_manager::mojom::Resolver:
 
-void Instance::ResolveMojoName(const std::string& mojo_name,
+void Instance::ResolveMojoName(const std::string& service_name,
                                const ResolveMojoNameCallback& callback) {
   DCHECK(system_cache_);
 
-  std::string type = service_manager::GetNameType(mojo_name);
-  if (type != service_manager::kNameType_Service &&
-      type != service_manager::kNameType_Exe) {
-    std::unique_ptr<Entry> entry(new Entry(mojo_name));
+  if (!service_manager::IsValidName(service_name)) {
+    std::unique_ptr<Entry> entry(new Entry(service_name));
     service_manager::mojom::ResolveResultPtr result =
         service_manager::mojom::ResolveResult::From(*entry);
     callback.Run(std::move(result));
@@ -73,7 +71,7 @@
   }
 
   // TODO(beng): per-user catalogs.
-  auto entry = system_cache_->find(mojo_name);
+  auto entry = system_cache_->find(service_name);
   if (entry != system_cache_->end()) {
     callback.Run(service_manager::mojom::ResolveResult::From(*entry->second));
     return;
@@ -82,9 +80,9 @@
   // Manifests for mojo: names should always be in the catalog by this point.
   // DCHECK(type == service_manager::kNameType_Exe);
   system_reader_->CreateEntryForName(
-      mojo_name, system_cache_,
+      service_name, system_cache_,
       base::Bind(&Instance::OnReadManifest, weak_factory_.GetWeakPtr(),
-                 mojo_name, callback));
+                 service_name, callback));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -168,7 +166,7 @@
 
 // static
 void Instance::OnReadManifest(base::WeakPtr<Instance> instance,
-                              const std::string& mojo_name,
+                              const std::string& service_name,
                               const ResolveMojoNameCallback& callback,
                               service_manager::mojom::ResolveResultPtr result) {
   callback.Run(std::move(result));
diff --git a/services/catalog/instance.h b/services/catalog/instance.h
index 66581af..afc0154 100644
--- a/services/catalog/instance.h
+++ b/services/catalog/instance.h
@@ -37,7 +37,7 @@
 
  private:
   // service_manager::mojom::Resolver:
-  void ResolveMojoName(const std::string& mojo_name,
+  void ResolveMojoName(const std::string& service_name,
                        const ResolveMojoNameCallback& callback) override;
 
   // mojom::Catalog:
@@ -60,7 +60,7 @@
   // Receives the result of manifest parsing, may be received after the
   // catalog object that issued the request is destroyed.
   static void OnReadManifest(base::WeakPtr<Instance> instance,
-                             const std::string& mojo_name,
+                             const std::string& service_name,
                              const ResolveMojoNameCallback& callback,
                              service_manager::mojom::ResolveResultPtr result);
 
diff --git a/services/catalog/reader.cc b/services/catalog/reader.cc
index 0efe5fc..3ae85d6e 100644
--- a/services/catalog/reader.cc
+++ b/services/catalog/reader.cc
@@ -25,40 +25,18 @@
                                const std::string& name,
                                const std::string& package_name_override) {
   // TODO(beng): think more about how this should be done for exe targets.
-  std::string type = service_manager::GetNameType(name);
   std::string path = service_manager::GetNamePath(name);
-  if (type == service_manager::kNameType_Service) {
-    std::string package_name;
-    if (package_name_override.empty())
-      package_name = path;
-    else
-      package_name = package_name_override;
-    return package_dir.AppendASCII(kPackagesDirName).AppendASCII(
-        package_name + "/manifest.json");
-  }
-  if (type == service_manager::kNameType_Exe)
-    return package_dir.AppendASCII(path + "_manifest.json");
-  return base::FilePath();
+  std::string package_name =
+      package_name_override.empty() ? path : package_name_override;
+  return package_dir.AppendASCII(kPackagesDirName).AppendASCII(
+      package_name + "/manifest.json");
 }
 
 base::FilePath GetExecutablePath(const base::FilePath& package_dir,
                                  const std::string& name) {
-  std::string type = service_manager::GetNameType(name);
-  if (type == service_manager::kNameType_Service) {
-    // It's still a mojo: URL, use the default mapping scheme.
-    const std::string host = service_manager::GetNamePath(name);
-    return package_dir.AppendASCII(host + "/" + host + ".library");
-  }
-  if (type == service_manager::kNameType_Exe) {
-#if defined OS_WIN
-    std::string extension = ".exe";
-#else
-    std::string extension;
-#endif
-    return package_dir.AppendASCII(service_manager::GetNamePath(name) +
-                                   extension);
-  }
-  return base::FilePath();
+  // It's still a mojo: URL, use the default mapping scheme.
+  const std::string host = service_manager::GetNamePath(name);
+  return package_dir.AppendASCII(host + "/" + host + ".library");
 }
 
 std::unique_ptr<Entry> ProcessManifest(
diff --git a/services/navigation/BUILD.gn b/services/navigation/BUILD.gn
index 1288e811..6e19b34 100644
--- a/services/navigation/BUILD.gn
+++ b/services/navigation/BUILD.gn
@@ -15,42 +15,7 @@
   ]
 }
 
-executable("navigation") {
-  testonly = true
-  sources = [
-    "main.cc",
-  ]
-
-  deps = [
-    ":lib",
-    ":pak",
-    "//base",
-    "//build/win:default_exe_manifest",
-    "//content",
-    "//content/public/app:both",
-    "//mojo/public/cpp/bindings",
-    "//services/navigation/content_client",
-    "//services/service_manager/runner:init",
-    "//services/service_manager/runner/common",
-    "//services/ui/public/cpp",
-    "//ui/views",
-    "//ui/views/controls/webview",
-    "//ui/views/mus",
-  ]
-
-  data_deps = [
-    ":manifest",
-    "//services/ui/test_wm",
-  ]
-
-  if (is_win) {
-    configs -= [ "//build/config/win:console" ]
-    configs += [ "//build/config/win:windowed" ]
-    deps += [ "//sandbox" ]
-  }
-}
-
-static_library("lib") {
+source_set("navigation") {
   sources = [
     "navigation.cc",
     "navigation.h",
@@ -93,56 +58,18 @@
   ]
 
   data_deps = [
-    ":navigation",
     ":unittest_manifest",
+    "//content/shell:content_shell",
     "//services/ui/test_wm",
   ]
 }
 
 service_manifest("manifest") {
-  type = "exe"
   name = "navigation"
   source = "manifest.json"
 }
 
 service_manifest("unittest_manifest") {
-  type = "exe"
   name = "navigation_unittests"
   source = "unittest_manifest.json"
 }
-
-repack("pak") {
-  sources = [
-    "$root_gen_dir/blink/devtools_resources.pak",
-    "$root_gen_dir/blink/public/resources/blink_image_resources_100_percent.pak",
-    "$root_gen_dir/blink/public/resources/blink_resources.pak",
-    "$root_gen_dir/content/app/resources/content_resources_100_percent.pak",
-    "$root_gen_dir/content/app/strings/content_strings_en-US.pak",
-    "$root_gen_dir/content/browser/tracing/tracing_resources.pak",
-    "$root_gen_dir/content/content_resources.pak",
-    "$root_gen_dir/content/shell/shell_resources.pak",
-    "$root_gen_dir/net/net_resources.pak",
-    "$root_gen_dir/ui/resources/ui_resources_100_percent.pak",
-    "$root_gen_dir/ui/resources/webui_resources.pak",
-    "$root_gen_dir/ui/strings/app_locale_settings_en-US.pak",
-    "$root_gen_dir/ui/strings/ui_strings_en-US.pak",
-    "$root_gen_dir/ui/views/resources/views_resources_100_percent.pak",
-  ]
-
-  deps = [
-    "//content:resources",
-    "//content/app/resources",
-    "//content/app/strings",
-    "//content/browser/devtools:resources",
-    "//content/browser/tracing:resources",
-    "//content/shell:resources",
-    "//net:net_resources",
-    "//third_party/WebKit/public:image_resources",
-    "//third_party/WebKit/public:resources",
-    "//ui/resources",
-    "//ui/strings",
-    "//ui/views/resources",
-  ]
-
-  output = "$root_out_dir/navigation.pak"
-}
diff --git a/services/navigation/content_client/BUILD.gn b/services/navigation/content_client/BUILD.gn
deleted file mode 100644
index 3b6d1cf4..0000000
--- a/services/navigation/content_client/BUILD.gn
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-static_library("content_client") {
-  testonly = true
-  sources = [
-    "browser_main_parts.cc",
-    "browser_main_parts.h",
-    "content_browser_client.cc",
-    "content_browser_client.h",
-    "main_delegate.cc",
-    "main_delegate.h",
-  ]
-
-  deps = [
-    "//base:i18n",
-    "//content",
-    "//content/shell:content_shell_lib",
-    "//net",
-    "//services/navigation:lib",
-    "//ui/views:test_support",
-    "//ui/views/mus",
-  ]
-}
diff --git a/services/navigation/content_client/browser_main_parts.cc b/services/navigation/content_client/browser_main_parts.cc
deleted file mode 100644
index bb3059c..0000000
--- a/services/navigation/content_client/browser_main_parts.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/navigation/content_client/browser_main_parts.h"
-
-#include "base/run_loop.h"
-#include "content/public/common/service_manager_connection.h"
-#include "content/shell/browser/shell_browser_context.h"
-#include "content/shell/browser/shell_net_log.h"
-#include "services/navigation/navigation.h"
-#include "ui/views/mus/window_manager_connection.h"
-#include "ui/views/test/test_views_delegate.h"
-
-namespace navigation {
-
-BrowserMainParts::BrowserMainParts(
-    const content::MainFunctionParams& parameters) {}
-BrowserMainParts::~BrowserMainParts() {}
-
-void BrowserMainParts::ToolkitInitialized() {
-  if (!views::ViewsDelegate::GetInstance())
-    views_delegate_.reset(new views::TestViewsDelegate);
-}
-
-void BrowserMainParts::PreMainMessageLoopRun() {
-  content::ServiceManagerConnection* service_manager_connection =
-      content::ServiceManagerConnection::GetForProcess();
-  if (service_manager_connection) {
-    window_manager_connection_ = views::WindowManagerConnection::Create(
-        service_manager_connection->GetConnector(),
-        service_manager_connection->GetIdentity());
-  }
-  net_log_.reset(new content::ShellNetLog("ash_shell"));
-  browser_context_.reset(
-      new content::ShellBrowserContext(false, net_log_.get()));
-}
-
-void BrowserMainParts::PostMainMessageLoopRun() {
-  views_delegate_.reset();
-  browser_context_.reset();
-}
-
-bool BrowserMainParts::MainMessageLoopRun(int* result_code) {
-  base::RunLoop().Run();
-  return true;
-}
-
-}  // namespace navigation
diff --git a/services/navigation/content_client/browser_main_parts.h b/services/navigation/content_client/browser_main_parts.h
deleted file mode 100644
index 7bca2630..0000000
--- a/services/navigation/content_client/browser_main_parts.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_NAVIGATION_CONTENT_CLIENT_BROWSER_MAIN_PARTS_H_
-#define SERVICES_NAVIGATION_CONTENT_CLIENT_BROWSER_MAIN_PARTS_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "content/public/browser/browser_main_parts.h"
-
-namespace content {
-class ShellBrowserContext;
-struct MainFunctionParams;
-}
-
-namespace net {
-class NetLog;
-}
-
-namespace views {
-class ViewsDelegate;
-class WindowManagerConnection;
-}
-
-namespace navigation {
-
-class Navigation;
-
-class BrowserMainParts : public content::BrowserMainParts {
- public:
-  BrowserMainParts(const content::MainFunctionParams& parameters);
-  ~BrowserMainParts() override;
-
-  // Overridden from content::BrowserMainParts:
-  void ToolkitInitialized() override;
-  void PreMainMessageLoopRun() override;
-  bool MainMessageLoopRun(int* result_code) override;
-  void PostMainMessageLoopRun() override;
-
-  content::ShellBrowserContext* browser_context() {
-    return browser_context_.get();
-  }
-
- private:
-  std::unique_ptr<net::NetLog> net_log_;
-  std::unique_ptr<content::ShellBrowserContext> browser_context_;
-  std::unique_ptr<views::ViewsDelegate> views_delegate_;
-  std::unique_ptr<views::WindowManagerConnection> window_manager_connection_;
-
-  DISALLOW_COPY_AND_ASSIGN(BrowserMainParts);
-};
-
-}  // namespace navigation
-
-#endif  // SERVICES_NAVIGATION_CONTENT_CLIENT_BROWSER_MAIN_PARTS_H_
diff --git a/services/navigation/content_client/content_browser_client.cc b/services/navigation/content_client/content_browser_client.cc
deleted file mode 100644
index c7010b3..0000000
--- a/services/navigation/content_client/content_browser_client.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/navigation/content_client/content_browser_client.h"
-
-#include <utility>
-
-#include "base/command_line.h"
-#include "base/memory/ptr_util.h"
-#include "content/public/common/service_info.h"
-#include "content/public/common/service_manager_connection.h"
-#include "content/shell/browser/shell_browser_context.h"
-#include "services/navigation/content_client/browser_main_parts.h"
-#include "services/navigation/navigation.h"
-
-namespace navigation {
-
-ContentBrowserClient::ContentBrowserClient() {}
-ContentBrowserClient::~ContentBrowserClient() {}
-
-content::BrowserMainParts* ContentBrowserClient::CreateBrowserMainParts(
-    const content::MainFunctionParams& parameters) {
-  browser_main_parts_ = new BrowserMainParts(parameters);
-  return browser_main_parts_;
-}
-
-std::string ContentBrowserClient::GetServiceUserIdForBrowserContext(
-    content::BrowserContext* browser_context) {
-  // Unlike Chrome, where there are different browser contexts for each process,
-  // each with their own userid, here there is only one and we should reuse the
-  // same userid as our own process to avoid having to create multiple shell
-  // connections.
-  return content::ServiceManagerConnection::GetForProcess()->GetIdentity()
-      .user_id();
-}
-
-void ContentBrowserClient::RegisterInProcessServices(
-    StaticServiceMap* services) {
-  content::ServiceManagerConnection::GetForProcess()->AddConnectionFilter(
-      base::MakeUnique<Navigation>());
-}
-
-}  // namespace navigation
diff --git a/services/navigation/content_client/content_browser_client.h b/services/navigation/content_client/content_browser_client.h
deleted file mode 100644
index 7198dd3b..0000000
--- a/services/navigation/content_client/content_browser_client.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_NAVIGATION_CONTENT_CLIENT_CONTENT_BROWSER_CLIENT_H_
-#define SERVICES_NAVIGATION_CONTENT_CLIENT_CONTENT_BROWSER_CLIENT_H_
-
-#include <string>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "content/public/browser/content_browser_client.h"
-
-namespace navigation {
-
-class BrowserMainParts;
-
-class ContentBrowserClient : public content::ContentBrowserClient {
- public:
-  ContentBrowserClient();
-  ~ContentBrowserClient() override;
-
-  // Overridden from content::ContentBrowserClient:
-  content::BrowserMainParts* CreateBrowserMainParts(
-      const content::MainFunctionParams& parameters) override;
-  std::string GetServiceUserIdForBrowserContext(
-      content::BrowserContext* browser_context) override;
-  void RegisterInProcessServices(StaticServiceMap* services) override;
-
- private:
-  BrowserMainParts* browser_main_parts_ = nullptr;
-
-  DISALLOW_COPY_AND_ASSIGN(ContentBrowserClient);
-};
-
-}  // namespace navigation
-
-#endif  // SERVICES_NAVIGATION_CONTENT_CLIENT_CONTENT_BROWSER_CLIENT_H_
diff --git a/services/navigation/content_client/main_delegate.cc b/services/navigation/content_client/main_delegate.cc
deleted file mode 100644
index 167c15da..0000000
--- a/services/navigation/content_client/main_delegate.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/navigation/content_client/main_delegate.h"
-
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/path_service.h"
-#include "content/public/common/content_switches.h"
-#include "services/navigation/content_client/content_browser_client.h"
-#include "ui/base/ime/input_method_initializer.h"
-#include "ui/base/resource/resource_bundle.h"
-
-namespace navigation {
-
-MainDelegate::MainDelegate() {}
-MainDelegate::~MainDelegate() {}
-
-bool MainDelegate::BasicStartupComplete(int* exit_code) {
-  const base::CommandLine& command_line =
-      *base::CommandLine::ForCurrentProcess();
-  std::string process_type =
-      command_line.GetSwitchValueASCII(switches::kProcessType);
-
-  content::SetContentClient(&content_client_);
-
-  return false;
-}
-
-void MainDelegate::PreSandboxStartup() {
-  base::FilePath path;
-  PathService::Get(base::DIR_MODULE, &path);
-  base::FilePath pak_path = path.Append(FILE_PATH_LITERAL("navigation.pak"));
-  ui::ResourceBundle::InitSharedInstanceWithPakPath(pak_path);
-  ui::InitializeInputMethodForTesting();
-}
-
-content::ContentBrowserClient* MainDelegate::CreateContentBrowserClient() {
-  browser_client_.reset(new ContentBrowserClient);
-  return browser_client_.get();
-}
-
-}  // namespace navigation
diff --git a/services/navigation/content_client/main_delegate.h b/services/navigation/content_client/main_delegate.h
deleted file mode 100644
index 455ee58f..0000000
--- a/services/navigation/content_client/main_delegate.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_NAVIGATION_CONTENT_CLIENT_MAIN_DELEGATE_H_
-#define SERVICES_NAVIGATION_CONTENT_CLIENT_MAIN_DELEGATE_H_
-
-#include <memory>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "content/public/app/content_main_delegate.h"
-#include "content/shell/common/shell_content_client.h"
-
-namespace content {
-class ShellContentRendererClient;
-class ShellContentUtilityClient;
-}
-
-namespace navigation {
-
-class ContentBrowserClient;
-
-class MainDelegate : public content::ContentMainDelegate {
- public:
-  MainDelegate();
-  ~MainDelegate() override;
-
-  bool BasicStartupComplete(int* exit_code) override;
-  void PreSandboxStartup() override;
-  content::ContentBrowserClient* CreateContentBrowserClient() override;
-
- private:
-  std::unique_ptr<ContentBrowserClient> browser_client_;
-  content::ShellContentClient content_client_;
-
-  DISALLOW_COPY_AND_ASSIGN(MainDelegate);
-};
-
-}  // namespace navigation
-
-#endif  // SERVICES_NAVIGATION_CONTENT_CLIENT_MAIN_DELEGATE_H_
diff --git a/services/navigation/main.cc b/services/navigation/main.cc
deleted file mode 100644
index cdb3402..0000000
--- a/services/navigation/main.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/at_exit.h"
-#include "base/command_line.h"
-#include "base/debug/stack_trace.h"
-#include "base/files/file_path.h"
-#include "base/path_service.h"
-#include "content/public/app/content_main.h"
-#include "services/navigation/content_client/main_delegate.h"
-#include "services/service_manager/runner/init.h"
-
-#if defined(OS_WIN)
-#include "content/public/app/sandbox_helper_win.h"
-#include "sandbox/win/src/sandbox_types.h"
-#endif
-
-#if defined(OS_WIN)
-int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t*, int) {
-  int argc = 0;
-  char** argv = nullptr;
-#else
-int main(int argc, const char** argv) {
-#endif
-  base::CommandLine::Init(argc, argv);
-  service_manager::WaitForDebuggerIfNecessary();
-
-#if !defined(OFFICIAL_BUILD)
-#if defined(OS_WIN)
-  base::RouteStdioToConsole(false);
-#endif
-#endif
-
-  base::FilePath log_filename;
-  PathService::Get(base::DIR_EXE, &log_filename);
-  log_filename = log_filename.AppendASCII("navigation.mojo.log");
-  logging::LoggingSettings settings;
-  settings.logging_dest = logging::LOG_TO_ALL;
-  settings.log_file = log_filename.value().c_str();
-  settings.delete_old = logging::DELETE_OLD_LOG_FILE;
-  logging::InitLogging(settings);
-
-  navigation::MainDelegate delegate;
-  content::ContentMainParams params(&delegate);
-#if defined(OS_WIN)
-  sandbox::SandboxInterfaceInfo sandbox_info = { 0 };
-  content::InitializeSandboxInfo(&sandbox_info);
-  params.instance = GetModuleHandle(NULL);
-  params.sandbox_info = &sandbox_info;
-#endif
-  return content::ContentMain(params);
-}
diff --git a/services/navigation/manifest.json b/services/navigation/manifest.json
index 0341124..8037a06 100644
--- a/services/navigation/manifest.json
+++ b/services/navigation/manifest.json
@@ -1,5 +1,5 @@
 {
-  "name": "exe:navigation",
+  "name": "service:navigation",
   "display_name": "Navigation",
   "interface_provider_specs": {
     "service_manager:connector": {
diff --git a/services/navigation/navigation.cc b/services/navigation/navigation.cc
index 81478d9e..6fe4f70 100644
--- a/services/navigation/navigation.cc
+++ b/services/navigation/navigation.cc
@@ -11,6 +11,7 @@
 #include "services/navigation/view_impl.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/cpp/interface_registry.h"
+#include "services/service_manager/public/cpp/service_context.h"
 
 namespace navigation {
 
@@ -30,6 +31,10 @@
 
 }  // namespace
 
+std::unique_ptr<service_manager::Service> CreateNavigationService() {
+  return base::MakeUnique<Navigation>();
+}
+
 Navigation::Navigation()
     : view_task_runner_(base::ThreadTaskRunnerHandle::Get()),
       ref_factory_(base::MessageLoop::QuitWhenIdleClosure()),
@@ -39,10 +44,13 @@
 }
 Navigation::~Navigation() {}
 
-bool Navigation::OnConnect(const service_manager::Identity& remote_identity,
-                           service_manager::InterfaceRegistry* registry,
-                           service_manager::Connector* connector) {
-  std::string remote_user_id = remote_identity.user_id();
+void Navigation::OnStart(service_manager::ServiceContext* context) {
+  context_ = context;
+}
+
+bool Navigation::OnConnect(const service_manager::ServiceInfo& remote_info,
+                           service_manager::InterfaceRegistry* registry) {
+  std::string remote_user_id = remote_info.identity.user_id();
   if (!client_user_id_.empty() && client_user_id_ != remote_user_id) {
     LOG(ERROR) << "Must have a separate Navigation service instance for "
                << "different BrowserContexts.";
@@ -58,7 +66,7 @@
 void Navigation::CreateView(mojom::ViewClientPtr client,
                             mojom::ViewRequest request) {
   std::unique_ptr<service_manager::Connector> new_connector =
-      connector_->Clone();
+      context_->connector()->Clone();
   std::unique_ptr<service_manager::ServiceContextRef> context_ref =
       ref_factory_.CreateRef();
   view_task_runner_->PostTask(
diff --git a/services/navigation/navigation.h b/services/navigation/navigation.h
index 635dc3b..ba2ec84 100644
--- a/services/navigation/navigation.h
+++ b/services/navigation/navigation.h
@@ -21,16 +21,18 @@
 
 namespace navigation {
 
-class Navigation : public content::ConnectionFilter, public mojom::ViewFactory {
+std::unique_ptr<service_manager::Service> CreateNavigationService();
+
+class Navigation : public service_manager::Service, public mojom::ViewFactory {
  public:
   Navigation();
   ~Navigation() override;
 
  private:
-  // content::ConnectionFilter:
-  bool OnConnect(const service_manager::Identity& remote_identity,
-                 service_manager::InterfaceRegistry* registry,
-                 service_manager::Connector* connector) override;
+  // service_manager::Service:
+  void OnStart(service_manager::ServiceContext* context) override;
+  bool OnConnect(const service_manager::ServiceInfo& remote_info,
+                 service_manager::InterfaceRegistry* registry) override;
 
   // mojom::ViewFactory:
   void CreateView(mojom::ViewClientPtr client,
@@ -41,7 +43,8 @@
 
   scoped_refptr<base::SequencedTaskRunner> view_task_runner_;
 
-  service_manager::Connector* connector_ = nullptr;
+  service_manager::ServiceContext* context_;
+
   std::string client_user_id_;
 
   service_manager::ServiceContextRefFactory ref_factory_;
diff --git a/services/service_manager/public/cpp/lib/names.cc b/services/service_manager/public/cpp/lib/names.cc
index b088db62..b3f5149c 100644
--- a/services/service_manager/public/cpp/lib/names.cc
+++ b/services/service_manager/public/cpp/lib/names.cc
@@ -9,9 +9,6 @@
 
 namespace service_manager {
 
-const char kNameType_Service[] = "service";
-const char kNameType_Exe[] = "exe";
-
 bool IsValidName(const std::string& name) {
   std::vector<std::string> parts =
       base::SplitString(name, ":", base::KEEP_WHITESPACE,
@@ -19,7 +16,7 @@
   if (parts.size() != 2)
     return false;
 
-  if (parts.front().empty())
+  if (parts.front().empty() || parts.front() != "service")
     return false;
 
   const std::string& path = parts.back();
@@ -27,14 +24,6 @@
       !base::StartsWith(path, "//", base::CompareCase::INSENSITIVE_ASCII);
 }
 
-std::string GetNameType(const std::string& name) {
-  std::vector<std::string> parts =
-      base::SplitString(name, ":", base::KEEP_WHITESPACE,
-                        base::SPLIT_WANT_ALL);
-  DCHECK(2 == parts.size());
-  return parts.front();
-}
-
 std::string GetNamePath(const std::string& name) {
   std::vector<std::string> parts =
       base::SplitString(name, ":", base::KEEP_WHITESPACE,
diff --git a/services/service_manager/public/cpp/names.h b/services/service_manager/public/cpp/names.h
index 38fd9a0..4644eb9 100644
--- a/services/service_manager/public/cpp/names.h
+++ b/services/service_manager/public/cpp/names.h
@@ -9,43 +9,21 @@
 
 namespace service_manager {
 
-extern const char kNameType_Service[];
-extern const char kNameType_Exe[];
+// TODO(beng): Eliminate this file entirely and drop the service: prefix.
 
 // Services are identified by structured "names", of the form:
 //
-//    type:path.
+//    service:<name>
 //
-// The type field tells the service manager how to load the service. Two types
-// are
-// recognized:
-//
-//  service
-//   A synthetic service name, which may be packaged as part of another service
-//   (via ServiceFactory) or as a standalone .library. .library files live
-//   within a directory hierarchy alongside the service manager executable.
-//
-//  exe
-//   Represents a native executable on the host platform, expected to live
-//   alongside the service manager executable. Executables launched via this
-//   mechanism are passed a handle to the service manager on the command line
-//   and are expected to bind a ServiceRequest enabling further communication
-//   with the Service Manager. The path component contains the executable name,
-//   minus any platform specific extension.
-//
-// Other types may be supplied but are not recognized by any of the
-// NativeRunners, and as such custom loaders must be specified for such names.
-//
-// Any name type may serve as an alias for any other name type. Aliasing is
-// resolved implicitly by the Service Manager.
+// This name is an alias that helps the Service Manager locate an implementation
+// of the Service interface. The alias may be to some object constructed and
+// registered with the Service Manager, or be provided by a Service library file
+// extant on disk in the location EXE_DIR/Packages/<name>/<name>.library.
 
-// Returns true if the name is a valid form, i.e. type:path. path cannot start
-// with a "//" sequence. These are _not_ urls.
+// Returns true if the name is a valid form, i.e. service:<name>. path cannot
+// start with a "//" sequence. These are _not_ urls.
 bool IsValidName(const std::string& name);
 
-// Get the type component of the specified name.
-std::string GetNameType(const std::string& name);
-
 // Get the path component of the specified name.
 std::string GetNamePath(const std::string& name);
 
diff --git a/services/service_manager/standalone/context.cc b/services/service_manager/standalone/context.cc
index 181c41a4..f633a6f1 100644
--- a/services/service_manager/standalone/context.cc
+++ b/services/service_manager/standalone/context.cc
@@ -253,7 +253,7 @@
 #else
     std::string possible_app = args[i];
 #endif
-    if (GetNameType(possible_app) == kNameType_Service) {
+    if (IsValidName(possible_app)) {
       Run(possible_app);
       break;
     }
diff --git a/services/service_manager/tests/connect/BUILD.gn b/services/service_manager/tests/connect/BUILD.gn
index 5970a2b..ab9f411ff 100644
--- a/services/service_manager/tests/connect/BUILD.gn
+++ b/services/service_manager/tests/connect/BUILD.gn
@@ -19,13 +19,13 @@
     "//services/service_manager/public/cpp:service_test_support",
     "//services/service_manager/public/cpp:sources",
     "//services/service_manager/public/interfaces",
+    "//services/service_manager/tests:util",
   ]
 
   data_deps = [
     ":connect_test_app",
     ":connect_test_class_app",
     ":connect_test_singleton_app",
-    ":connect_test_driver",
     ":connect_test_exe",
     ":connect_test_package",
     ":manifest",
@@ -150,33 +150,6 @@
   source = "connect_test_singleton_app_manifest.json"
 }
 
-executable("connect_test_driver") {
-  testonly = true
-
-  sources = [
-    "connect_test_driver.cc",
-  ]
-
-  deps = [
-    ":interfaces",
-    "//base",
-    "//build/win:default_exe_manifest",
-    "//services/service_manager/public/cpp",
-    "//services/service_manager/runner/child:test_native_main",
-    "//services/service_manager/tests:util",
-  ]
-
-  data_deps = [
-    ":connect_test_driver_manifest",
-  ]
-}
-
-service_manifest("connect_test_driver_manifest") {
-  type = "exe"
-  name = "connect_test_driver"
-  source = "connect_test_driver_manifest.json"
-}
-
 executable("connect_test_exe") {
   testonly = true
 
@@ -198,7 +171,6 @@
 }
 
 service_manifest("connect_test_exe_manifest") {
-  type = "exe"
   name = "connect_test_exe"
   source = "connect_test_exe_manifest.json"
 }
diff --git a/services/service_manager/tests/connect/connect_test_driver.cc b/services/service_manager/tests/connect/connect_test_driver.cc
deleted file mode 100644
index f002586d..0000000
--- a/services/service_manager/tests/connect/connect_test_driver.cc
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <memory>
-
-#include "base/at_exit.h"
-#include "base/command_line.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/process/process.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/service_manager/public/cpp/connection.h"
-#include "services/service_manager/public/cpp/connector.h"
-#include "services/service_manager/public/cpp/interface_factory.h"
-#include "services/service_manager/public/cpp/interface_registry.h"
-#include "services/service_manager/public/cpp/service.h"
-#include "services/service_manager/public/cpp/service_context.h"
-#include "services/service_manager/runner/child/test_native_main.h"
-#include "services/service_manager/runner/init.h"
-#include "services/service_manager/tests/connect/connect_test.mojom.h"
-#include "services/service_manager/tests/util.h"
-
-using service_manager::test::mojom::ClientProcessTest;
-using service_manager::test::mojom::ClientProcessTestRequest;
-
-namespace {
-
-class Driver : public service_manager::Service,
-               public service_manager::InterfaceFactory<ClientProcessTest>,
-               public ClientProcessTest {
- public:
-  Driver() {}
-  ~Driver() override {}
-
- private:
-  // service_manager::Service:
-  void OnStart(service_manager::ServiceContext* context) override {
-    context_ = context;
-  }
-
-  bool OnConnect(const service_manager::ServiceInfo& remote_info,
-                 service_manager::InterfaceRegistry* registry) override {
-    registry->AddInterface<ClientProcessTest>(this);
-    return true;
-  }
-
-  bool OnStop() override {
-    // TODO(rockot): http://crbug.com/596621. Should be able to remove this
-    // override entirely.
-    _exit(1);
-  }
-
-  // service_manager::InterfaceFactory<ConnectTestService>:
-  void Create(const service_manager::Identity& remote_identity,
-              ClientProcessTestRequest request) override {
-    bindings_.AddBinding(this, std::move(request));
-  }
-
-  // test::mojom::ClientProcessTest:
-  void LaunchAndConnectToProcess(
-      const LaunchAndConnectToProcessCallback& callback) override {
-    base::Process process;
-    std::unique_ptr<service_manager::Connection> connection =
-        service_manager::test::LaunchAndConnectToProcess(
-#if defined(OS_WIN)
-            "connect_test_exe.exe",
-#else
-            "connect_test_exe",
-#endif
-            service_manager::Identity("exe:connect_test_exe",
-                                      service_manager::mojom::kInheritUserID),
-            context_->connector(), &process);
-    callback.Run(static_cast<int32_t>(connection->GetResult()),
-                 connection->GetRemoteIdentity());
-  }
-
-  service_manager::ServiceContext* context_ = nullptr;
-  mojo::BindingSet<ClientProcessTest> bindings_;
-
-  DISALLOW_COPY_AND_ASSIGN(Driver);
-};
-
-}  // namespace
-
-int main(int argc, char** argv) {
-  base::AtExitManager at_exit;
-  base::CommandLine::Init(argc, argv);
-
-  service_manager::InitializeLogging();
-  return service_manager::TestNativeMain(base::MakeUnique<Driver>());
-}
diff --git a/services/service_manager/tests/connect/connect_test_driver_manifest.json b/services/service_manager/tests/connect/connect_test_driver_manifest.json
deleted file mode 100644
index c87e1e6..0000000
--- a/services/service_manager/tests/connect/connect_test_driver_manifest.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "name": "exe:connect_test_driver",
-  "display_name": "Connect Test Driver",
-  "interface_provider_specs": {
-    "service_manager:connector": {
-      "provides": {
-        "connect_unittests:client_process_test": [
-          "service_manager::test::mojom::ClientProcessTest"
-        ]
-      }
-    }
-  }
-}
diff --git a/services/service_manager/tests/connect/connect_test_exe_manifest.json b/services/service_manager/tests/connect/connect_test_exe_manifest.json
index 2705187a..e5c30b64 100644
--- a/services/service_manager/tests/connect/connect_test_exe_manifest.json
+++ b/services/service_manager/tests/connect/connect_test_exe_manifest.json
@@ -1,5 +1,5 @@
 {
-  "name": "exe:connect_test_exe",
+  "name": "service:connect_test_exe",
   "display_name": "Connect Test Exe",
   "interface_provider_specs": { }
 }
diff --git a/services/service_manager/tests/connect/connect_unittest.cc b/services/service_manager/tests/connect/connect_unittest.cc
index a72f31e..7e45f6b 100644
--- a/services/service_manager/tests/connect/connect_unittest.cc
+++ b/services/service_manager/tests/connect/connect_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/bind.h"
 #include "base/guid.h"
 #include "base/macros.h"
+#include "base/process/process.h"
 #include "base/run_loop.h"
 #include "base/test/test_suite.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
@@ -20,6 +21,7 @@
 #include "services/service_manager/public/cpp/service_test.h"
 #include "services/service_manager/public/interfaces/service_manager.mojom.h"
 #include "services/service_manager/tests/connect/connect_test.mojom.h"
+#include "services/service_manager/tests/util.h"
 
 // Tests that multiple services can be packaged in a single service by
 // implementing ServiceFactory; that these services can be specified by
@@ -35,7 +37,6 @@
 const char kTestAppBName[] = "service:connect_test_b";
 const char kTestClassAppName[] = "service:connect_test_class_app";
 const char kTestSingletonAppName[] = "service:connect_test_singleton_app";
-const char kTestDriverName[] = "exe:connect_test_driver";
 
 void ReceiveOneString(std::string* out_string,
                       base::RunLoop* loop,
@@ -345,19 +346,18 @@
 // client
 // process specifications. This is the only one for blocking.
 TEST_F(ConnectTest, ConnectToClientProcess_Blocked) {
-  std::unique_ptr<Connection> connection =
-      connector()->Connect(kTestDriverName);
-  test::mojom::ClientProcessTestPtr client_process_test;
-  connection->GetInterface(&client_process_test);
-  mojom::ConnectResult result;
-  Identity result_identity;
-  {
-    base::RunLoop loop;
-    client_process_test->LaunchAndConnectToProcess(
-        base::Bind(&ReceiveConnectionResult, &result, &result_identity, &loop));
-    loop.Run();
-  }
-  EXPECT_EQ(mojom::ConnectResult::ACCESS_DENIED, result);
+  base::Process process;
+  std::unique_ptr<service_manager::Connection> connection =
+      service_manager::test::LaunchAndConnectToProcess(
+#if defined(OS_WIN)
+          "connect_test_exe.exe",
+#else
+          "connect_test_exe",
+#endif
+          service_manager::Identity("service:connect_test_exe",
+                                    service_manager::mojom::kInheritUserID),
+          connector(), &process);
+  EXPECT_EQ(connection->GetResult(), mojom::ConnectResult::ACCESS_DENIED);
 }
 
 // Verifies that a client with the "all_users" capability class can receive
diff --git a/services/service_manager/tests/connect/connect_unittests_manifest.json b/services/service_manager/tests/connect/connect_unittests_manifest.json
index 3d8c307..38edd32 100644
--- a/services/service_manager/tests/connect/connect_unittests_manifest.json
+++ b/services/service_manager/tests/connect/connect_unittests_manifest.json
@@ -17,7 +17,7 @@
           "connect_unittests:standalone_app",
           "connect_unittests:user_id_test"
         ],
-        "exe:connect_test_driver": [
+        "service:connect_test_driver": [
           "connect_unittests:client_process_test"
         ],
         "service:connect_test_a": [
diff --git a/services/service_manager/tests/lifecycle/BUILD.gn b/services/service_manager/tests/lifecycle/BUILD.gn
index 38575e2..0e65b67e 100644
--- a/services/service_manager/tests/lifecycle/BUILD.gn
+++ b/services/service_manager/tests/lifecycle/BUILD.gn
@@ -158,7 +158,6 @@
 }
 
 service_manifest("lifecycle_unittest_exe_manifest") {
-  type = "exe"
   name = "lifecycle_unittest_exe"
   source = "exe_manifest.json"
 }
diff --git a/services/service_manager/tests/lifecycle/exe_manifest.json b/services/service_manager/tests/lifecycle/exe_manifest.json
index 85adc01..6ff0faf 100644
--- a/services/service_manager/tests/lifecycle/exe_manifest.json
+++ b/services/service_manager/tests/lifecycle/exe_manifest.json
@@ -1,5 +1,5 @@
 {
-  "name": "exe:lifecycle_unittest_exe",
+  "name": "service:lifecycle_unittest_exe",
   "display_name": "Lifecycle Unittest Exe",
   "interface_provider_specs": {
     "service_manager:connector": {
diff --git a/services/service_manager/tests/lifecycle/lifecycle_unittest.cc b/services/service_manager/tests/lifecycle/lifecycle_unittest.cc
index e6280b5..567b236b 100644
--- a/services/service_manager/tests/lifecycle/lifecycle_unittest.cc
+++ b/services/service_manager/tests/lifecycle/lifecycle_unittest.cc
@@ -23,7 +23,7 @@
 
 const char kTestAppName[] = "service:lifecycle_unittest_app";
 const char kTestParentName[] = "service:lifecycle_unittest_parent";
-const char kTestExeName[] = "exe:lifecycle_unittest_exe";
+const char kTestExeName[] = "service:lifecycle_unittest_exe";
 const char kTestPackageName[] = "service:lifecycle_unittest_package";
 const char kTestPackageAppNameA[] = "service:lifecycle_unittest_package_app_a";
 const char kTestPackageAppNameB[] = "service:lifecycle_unittest_package_app_b";
diff --git a/services/service_manager/tests/service_manager/BUILD.gn b/services/service_manager/tests/service_manager/BUILD.gn
index a369537..e8960a5 100644
--- a/services/service_manager/tests/service_manager/BUILD.gn
+++ b/services/service_manager/tests/service_manager/BUILD.gn
@@ -18,15 +18,16 @@
     "//base",
     "//base/test:test_config",
     "//mojo/common:common_base",
+    "//mojo/edk/system",
     "//services/service_manager/public/cpp:service_test_support",
     "//services/service_manager/public/cpp:sources",
     "//services/service_manager/public/interfaces",
+    "//services/service_manager/runner/common",
   ]
 
   data_deps = [
     ":manifest",
     ":service_manager_unittest_embedder",
-    ":service_manager_unittest_driver",
     ":service_manager_unittest_target",
   ]
 }
@@ -46,36 +47,6 @@
   source = "service_manager_unittest_manifest.json"
 }
 
-executable("service_manager_unittest_driver") {
-  testonly = true
-
-  sources = [
-    "driver.cc",
-  ]
-
-  deps = [
-    ":interfaces",
-    "//base",
-    "//build/win:default_exe_manifest",
-    "//mojo/edk/system",
-    "//services/service_manager/public/cpp",
-    "//services/service_manager/public/interfaces",
-    "//services/service_manager/runner:init",
-    "//services/service_manager/runner/child:test_native_main",
-    "//services/service_manager/runner/common",
-  ]
-
-  data_deps = [
-    ":driver_manifest",
-  ]
-}
-
-service_manifest("driver_manifest") {
-  type = "exe"
-  name = "service_manager_unittest_driver"
-  source = "driver_manifest.json"
-}
-
 executable("service_manager_unittest_target") {
   testonly = true
 
@@ -97,13 +68,11 @@
 }
 
 service_manifest("target_manifest") {
-  type = "exe"
   name = "service_manager_unittest_target"
   source = "target_manifest.json"
 }
 
 service_manifest("embedder_manifest") {
-  type = "exe"
   name = "service_manager_unittest_embedder"
   source = "embedder_manifest.json"
   deps = [
@@ -117,7 +86,7 @@
   source = "singleton_manifest.json"
 }
 
-executable("service_manager_unittest_embedder") {
+service("service_manager_unittest_embedder") {
   testonly = true
 
   sources = [
diff --git a/services/service_manager/tests/service_manager/driver.cc b/services/service_manager/tests/service_manager/driver.cc
deleted file mode 100644
index e5b0d59d..0000000
--- a/services/service_manager/tests/service_manager/driver.cc
+++ /dev/null
@@ -1,143 +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 <stdint.h>
-
-#include <memory>
-#include <utility>
-
-#include "base/at_exit.h"
-#include "base/base_paths.h"
-#include "base/base_switches.h"
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/memory/weak_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "base/path_service.h"
-#include "base/process/process.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/platform_channel_pair.h"
-#include "mojo/edk/embedder/scoped_platform_handle.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/service_manager/public/cpp/connection.h"
-#include "services/service_manager/public/cpp/connector.h"
-#include "services/service_manager/public/cpp/interface_factory.h"
-#include "services/service_manager/public/cpp/interface_registry.h"
-#include "services/service_manager/public/cpp/service.h"
-#include "services/service_manager/public/cpp/service_context.h"
-#include "services/service_manager/public/interfaces/connector.mojom.h"
-#include "services/service_manager/public/interfaces/service_manager.mojom.h"
-#include "services/service_manager/runner/child/test_native_main.h"
-#include "services/service_manager/runner/common/client_util.h"
-#include "services/service_manager/runner/common/switches.h"
-#include "services/service_manager/runner/init.h"
-#include "services/service_manager/tests/service_manager/service_manager_unittest.mojom.h"
-
-namespace {
-
-class Driver : public service_manager::Service,
-               public service_manager::InterfaceFactory<
-                   service_manager::test::mojom::Driver>,
-               public service_manager::test::mojom::Driver {
- public:
-  Driver() : weak_factory_(this) {}
-  ~Driver() override {}
-
- private:
-  // service_manager::Service:
-  void OnStart(service_manager::ServiceContext* context) override {
-    base::FilePath target_path;
-    CHECK(base::PathService::Get(base::DIR_EXE, &target_path));
-#if defined(OS_WIN)
-    target_path = target_path.Append(
-        FILE_PATH_LITERAL("service_manager_unittest_target.exe"));
-#else
-    target_path = target_path.Append(
-        FILE_PATH_LITERAL("service_manager_unittest_target"));
-#endif
-
-    base::CommandLine child_command_line(target_path);
-    // Forward the wait-for-debugger flag but nothing else - we don't want to
-    // stamp on the platform-channel flag.
-    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-            switches::kWaitForDebugger)) {
-      child_command_line.AppendSwitch(switches::kWaitForDebugger);
-    }
-
-    // Create the channel to be shared with the target process. Pass one end
-    // on the command line.
-    mojo::edk::PlatformChannelPair platform_channel_pair;
-    mojo::edk::HandlePassingInformation handle_passing_info;
-    platform_channel_pair.PrepareToPassClientHandleToChildProcess(
-        &child_command_line, &handle_passing_info);
-
-    std::string child_token = mojo::edk::GenerateRandomToken();
-    service_manager::mojom::ServicePtr client =
-        service_manager::PassServiceRequestOnCommandLine(&child_command_line,
-                                                         child_token);
-    service_manager::mojom::PIDReceiverPtr receiver;
-
-    service_manager::Identity target("exe:service_manager_unittest_target",
-                                     service_manager::mojom::kInheritUserID);
-    service_manager::Connector::ConnectParams params(target);
-    params.set_client_process_connection(std::move(client),
-                                         GetProxy(&receiver));
-    std::unique_ptr<service_manager::Connection> connection =
-        context->connector()->Connect(&params);
-    connection->AddConnectionCompletedClosure(
-        base::Bind(&Driver::OnConnectionCompleted, base::Unretained(this)));
-
-    base::LaunchOptions options;
-#if defined(OS_WIN)
-    options.handles_to_inherit = &handle_passing_info;
-#elif defined(OS_POSIX)
-    options.fds_to_remap = &handle_passing_info;
-#endif
-    target_ = base::LaunchProcess(child_command_line, options);
-    DCHECK(target_.IsValid());
-    receiver->SetPID(target_.Pid());
-    mojo::edk::ChildProcessLaunched(target_.Handle(),
-                                    platform_channel_pair.PassServerHandle(),
-                                    child_token);
-  }
-
-  bool OnConnect(const service_manager::ServiceInfo& remote_info,
-                 service_manager::InterfaceRegistry* registry) override {
-    registry->AddInterface<service_manager::test::mojom::Driver>(this);
-    return true;
-  }
-
-  // service_manager::InterfaceFactory<Driver>:
-  void Create(const service_manager::Identity& remote_identity,
-              service_manager::test::mojom::DriverRequest request) override {
-    bindings_.AddBinding(this, std::move(request));
-  }
-
-  // Driver:
-  void QuitDriver() override {
-    target_.Terminate(0, false);
-    base::MessageLoop::current()->QuitWhenIdle();
-  }
-
-  void OnConnectionCompleted() {}
-
-  base::Process target_;
-  mojo::BindingSet<service_manager::test::mojom::Driver> bindings_;
-  base::WeakPtrFactory<Driver> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(Driver);
-};
-
-}  // namespace
-
-int main(int argc, char** argv) {
-  base::AtExitManager at_exit;
-  base::CommandLine::Init(argc, argv);
-
-  service_manager::InitializeLogging();
-  return service_manager::TestNativeMain(base::MakeUnique<Driver>());
-}
diff --git a/services/service_manager/tests/service_manager/driver_manifest.json b/services/service_manager/tests/service_manager/driver_manifest.json
deleted file mode 100644
index b6a0875..0000000
--- a/services/service_manager/tests/service_manager/driver_manifest.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-  "name": "exe:service_manager_unittest_driver",
-  "display_name": "Service Manager Unittest: Driver",
-  "interface_provider_specs": {
-    "service_manager:connector": {
-      "provides": {
-        "service_manager_unittest:driver": [
-          "service_manager::test::mojom::Driver"
-        ]
-      },
-      "requires": {
-        "exe:service_manager_unittest_target": [ ],
-        "service:service_manager": [ "service_manager:client_process" ]
-      }
-    }
-  }
-}
diff --git a/services/service_manager/tests/service_manager/embedder.cc b/services/service_manager/tests/service_manager/embedder.cc
index 5c85f81c..5e22b94d 100644
--- a/services/service_manager/tests/service_manager/embedder.cc
+++ b/services/service_manager/tests/service_manager/embedder.cc
@@ -10,10 +10,12 @@
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
+#include "services/service_manager/public/c/main.h"
 #include "services/service_manager/public/cpp/interface_factory.h"
 #include "services/service_manager/public/cpp/interface_registry.h"
 #include "services/service_manager/public/cpp/service.h"
 #include "services/service_manager/public/cpp/service_context.h"
+#include "services/service_manager/public/cpp/service_runner.h"
 #include "services/service_manager/public/interfaces/service_factory.mojom.h"
 #include "services/service_manager/public/interfaces/service_manager.mojom.h"
 #include "services/service_manager/runner/child/test_native_main.h"
@@ -81,10 +83,7 @@
 
 }  // namespace
 
-int main(int argc, char** argv) {
-  base::AtExitManager at_exit;
-  base::CommandLine::Init(argc, argv);
-
-  service_manager::InitializeLogging();
-  return service_manager::TestNativeMain(base::MakeUnique<Embedder>());
+MojoResult ServiceMain(MojoHandle service_request_handle) {
+  service_manager::ServiceRunner runner(new Embedder);
+  return runner.Run(service_request_handle);
 }
diff --git a/services/service_manager/tests/service_manager/embedder_manifest.json b/services/service_manager/tests/service_manager/embedder_manifest.json
index 6012832..7d39705 100644
--- a/services/service_manager/tests/service_manager/embedder_manifest.json
+++ b/services/service_manager/tests/service_manager/embedder_manifest.json
@@ -1,5 +1,5 @@
 {
-  "name": "exe:service_manager_unittest_embedder",
+  "name": "service:service_manager_unittest_embedder",
   "display_name": "Service Manager Unittest: Embedder",
   "interface_provider_specs": {
     "service_manager:connector": {
@@ -10,7 +10,7 @@
         ]
       },
       "requires": {
-        "exe:service_manager_unittest_target": [ ]
+        "service:service_manager_unittest_target": [ ]
       }
     }
   }
diff --git a/services/service_manager/tests/service_manager/service_manager_unittest.cc b/services/service_manager/tests/service_manager/service_manager_unittest.cc
index 1f93f535..432e879 100644
--- a/services/service_manager/tests/service_manager/service_manager_unittest.cc
+++ b/services/service_manager/tests/service_manager/service_manager_unittest.cc
@@ -8,19 +8,27 @@
 #include <memory>
 #include <utility>
 
+#include "base/base_switches.h"
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/command_line.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
+#include "base/path_service.h"
+#include "base/process/process.h"
 #include "base/process/process_handle.h"
 #include "base/run_loop.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "services/service_manager/public/cpp/interface_factory.h"
 #include "services/service_manager/public/cpp/interface_registry.h"
 #include "services/service_manager/public/cpp/service.h"
 #include "services/service_manager/public/cpp/service_test.h"
 #include "services/service_manager/public/interfaces/service_manager.mojom.h"
+#include "services/service_manager/runner/common/client_util.h"
 #include "services/service_manager/tests/service_manager/service_manager_unittest.mojom.h"
 
 namespace service_manager {
@@ -141,6 +149,67 @@
     service_failed_to_start_callback_ = callback;
   }
 
+  void StartTarget() {
+    base::FilePath target_path;
+    CHECK(base::PathService::Get(base::DIR_EXE, &target_path));
+#if defined(OS_WIN)
+    target_path = target_path.Append(
+        FILE_PATH_LITERAL("service_manager_unittest_target.exe"));
+#else
+    target_path = target_path.Append(
+        FILE_PATH_LITERAL("service_manager_unittest_target"));
+#endif
+
+    base::CommandLine child_command_line(target_path);
+    // Forward the wait-for-debugger flag but nothing else - we don't want to
+    // stamp on the platform-channel flag.
+    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+            switches::kWaitForDebugger)) {
+      child_command_line.AppendSwitch(switches::kWaitForDebugger);
+    }
+
+    // Create the channel to be shared with the target process. Pass one end
+    // on the command line.
+    mojo::edk::PlatformChannelPair platform_channel_pair;
+    mojo::edk::HandlePassingInformation handle_passing_info;
+    platform_channel_pair.PrepareToPassClientHandleToChildProcess(
+        &child_command_line, &handle_passing_info);
+
+    std::string child_token = mojo::edk::GenerateRandomToken();
+    service_manager::mojom::ServicePtr client =
+        service_manager::PassServiceRequestOnCommandLine(&child_command_line,
+                                                         child_token);
+    service_manager::mojom::PIDReceiverPtr receiver;
+
+    service_manager::Identity target("service:service_manager_unittest_target",
+                                     service_manager::mojom::kInheritUserID);
+    service_manager::Connector::ConnectParams params(target);
+    params.set_client_process_connection(std::move(client),
+                                         GetProxy(&receiver));
+    std::unique_ptr<service_manager::Connection> connection =
+        connector()->Connect(&params);
+    connection->AddConnectionCompletedClosure(
+        base::Bind(&ServiceManagerTest::OnConnectionCompleted,
+                   base::Unretained(this)));
+
+    base::LaunchOptions options;
+#if defined(OS_WIN)
+    options.handles_to_inherit = &handle_passing_info;
+#elif defined(OS_POSIX)
+    options.fds_to_remap = &handle_passing_info;
+#endif
+    target_ = base::LaunchProcess(child_command_line, options);
+    DCHECK(target_.IsValid());
+    receiver->SetPID(target_.Pid());
+    mojo::edk::ChildProcessLaunched(target_.Handle(),
+                                    platform_channel_pair.PassServerHandle(),
+                                    child_token);
+  }
+
+  void KillTarget() {
+    target_.Terminate(0, false);
+  }
+
  private:
   // test::ServiceTest:
   std::unique_ptr<Service> CreateService() override {
@@ -185,6 +254,8 @@
     }
   }
 
+  void OnConnectionCompleted() {}
+
   ServiceManagerTestClient* service_;
   mojo::Binding<mojom::ServiceManagerListener> binding_;
   std::vector<InstanceInfo> instances_;
@@ -192,6 +263,7 @@
   std::unique_ptr<base::RunLoop> wait_for_instances_loop_;
   ServiceStartedCallback service_started_callback_;
   ServiceFailedToStartCallback service_failed_to_start_callback_;
+  base::Process target_;
 
   DISALLOW_COPY_AND_ASSIGN(ServiceManagerTest);
 };
@@ -199,20 +271,13 @@
 TEST_F(ServiceManagerTest, CreateInstance) {
   AddListenerAndWaitForApplications();
 
-  // 1. Launch a process. (Actually, have the runner launch a process that
-  //    launches a process.)
-  test::mojom::DriverPtr driver;
-  std::unique_ptr<Connection> connection =
-      connector()->Connect("exe:service_manager_unittest_driver");
-  connection->GetInterface(&driver);
+  // 1. Launch a process.
+  StartTarget();
 
   // 2. Wait for the target to connect to us. (via
   //    service:service_manager_unittest)
   WaitForTargetIdentityCall();
 
-  EXPECT_FALSE(connection->IsPending());
-  Identity remote_identity = connection->GetRemoteIdentity();
-
   // 3. Validate that this test suite's name was received from the application
   //    manager.
   EXPECT_TRUE(ContainsInstanceWithName("service:service_manager_unittest"));
@@ -220,28 +285,17 @@
   // 4. Validate that the right applications/processes were created.
   //    Note that the target process will be created even if the tests are
   //    run with --single-process.
-  EXPECT_EQ(2u, instances().size());
-  {
-    auto& instance = instances().front();
-    EXPECT_EQ(remote_identity, instance.identity);
-    EXPECT_EQ("exe:service_manager_unittest_driver", instance.identity.name());
-    EXPECT_NE(base::kNullProcessId, instance.pid);
-  }
+  EXPECT_EQ(1u, instances().size());
   {
     auto& instance = instances().back();
     // We learn about the target process id via a ping from it.
     EXPECT_EQ(target_identity(), instance.identity);
-    EXPECT_EQ("exe:service_manager_unittest_target", instance.identity.name());
+    EXPECT_EQ("service:service_manager_unittest_target",
+              instance.identity.name());
     EXPECT_NE(base::kNullProcessId, instance.pid);
   }
 
-  {
-    base::RunLoop loop;
-    driver.set_connection_error_handler(
-        base::Bind(&base::RunLoop::Quit, base::Unretained(&loop)));
-    driver->QuitDriver();
-    loop.Run();
-  }
+  KillTarget();
 }
 
 void OnServiceStartedCallback(int* start_count,
@@ -279,12 +333,12 @@
         &failed_to_start, loop.QuitClosure()));
 
     std::unique_ptr<Connection> embedder_connection =
-        connector()->Connect("exe:service_manager_unittest_embedder");
+        connector()->Connect("service:service_manager_unittest_embedder");
     loop.Run();
     EXPECT_FALSE(failed_to_start);
     EXPECT_FALSE(embedder_connection->IsPending());
     EXPECT_EQ(1, start_count);
-    EXPECT_EQ("exe:service_manager_unittest_embedder", service_name);
+    EXPECT_EQ("service:service_manager_unittest_embedder", service_name);
   }
 
   {
diff --git a/services/service_manager/tests/service_manager/service_manager_unittest_manifest.json b/services/service_manager/tests/service_manager/service_manager_unittest_manifest.json
index d10a428..3772444 100644
--- a/services/service_manager/tests/service_manager/service_manager_unittest_manifest.json
+++ b/services/service_manager/tests/service_manager/service_manager_unittest_manifest.json
@@ -9,16 +9,17 @@
         ]
       },
       "requires": {
-        "service:service_manager": [ "service_manager:service_manager" ],
-        "exe:service_manager_unittest_driver": [
-          "service_manager_unittest:driver"
+        "service:service_manager": [
+          "service_manager:service_manager",
+          "service_manager:client_process"
         ],
-        "exe:service_manager_unittest_embedder": [
+        "service:service_manager_unittest_embedder": [
           "service_manager_unittest:embedder"
         ],
         "service:service_manager_unittest_singleton": [
           "service_manager_unittest:singleton"
-        ]
+        ],
+        "service:service_manager_unittest_target": [ ]
       }
     }
   }
diff --git a/services/service_manager/tests/service_manager/singleton_manifest.json b/services/service_manager/tests/service_manager/singleton_manifest.json
index 65e9fc5..9230ee7 100644
--- a/services/service_manager/tests/service_manager/singleton_manifest.json
+++ b/services/service_manager/tests/service_manager/singleton_manifest.json
@@ -7,7 +7,7 @@
         "service_manager_unittest:singleton": [ ]
       },
       "requires": {
-        "exe:service_manager_unittest_target": [ ],
+        "service:service_manager_unittest_target": [ ],
         "service:service_manager": [ "service_manager:all_users" ]
       }
     }
diff --git a/services/service_manager/tests/service_manager/target_manifest.json b/services/service_manager/tests/service_manager/target_manifest.json
index 2cf40b50..b1d66e9 100644
--- a/services/service_manager/tests/service_manager/target_manifest.json
+++ b/services/service_manager/tests/service_manager/target_manifest.json
@@ -1,5 +1,5 @@
 {
-  "name": "exe:service_manager_unittest_target",
+  "name": "service:service_manager_unittest_target",
   "display_name": "Service Manager Unittest: Target",
   "interface_provider_specs": {
     "service_manager:connector": {
diff --git a/services/ui/clipboard/BUILD.gn b/services/ui/clipboard/BUILD.gn
index ff7bc2a..801bb44 100644
--- a/services/ui/clipboard/BUILD.gn
+++ b/services/ui/clipboard/BUILD.gn
@@ -51,7 +51,6 @@
 }
 
 service_manifest("test_manifest") {
-  type = "exe"
   name = "mus_clipboard_unittests"
   source = "test_manifest.json"
 }
diff --git a/services/ui/clipboard/clipboard_unittest.cc b/services/ui/clipboard/clipboard_unittest.cc
index 5566fe8..17759c4 100644
--- a/services/ui/clipboard/clipboard_unittest.cc
+++ b/services/ui/clipboard/clipboard_unittest.cc
@@ -30,7 +30,7 @@
 
 class ClipboardAppTest : public service_manager::test::ServiceTest {
  public:
-  ClipboardAppTest() : ServiceTest("exe:mus_clipboard_unittests") {}
+  ClipboardAppTest() : ServiceTest("service:mus_clipboard_unittests") {}
   ~ClipboardAppTest() override {}
 
   // Overridden from service_manager::test::ServiceTest:
diff --git a/services/ui/clipboard/test_manifest.json b/services/ui/clipboard/test_manifest.json
index 372138e1..a4276dac 100644
--- a/services/ui/clipboard/test_manifest.json
+++ b/services/ui/clipboard/test_manifest.json
@@ -1,5 +1,5 @@
 {
-  "name": "exe:mus_clipboard_unittests",
+  "name": "service:mus_clipboard_unittests",
   "display_name": "Clipboard Service Unittests",
   "interface_provider_specs": {
     "service_manager:connector": {
diff --git a/services/ui/demo/BUILD.gn b/services/ui/demo/BUILD.gn
index f06f81b..5a4e2bb 100644
--- a/services/ui/demo/BUILD.gn
+++ b/services/ui/demo/BUILD.gn
@@ -51,7 +51,6 @@
 }
 
 service_manifest("test_manifest") {
-  type = "exe"
   name = "mus_demo_unittests"
   source = "test_manifest.json"
 }
diff --git a/services/ui/demo/mus_demo_unittests.cc b/services/ui/demo/mus_demo_unittests.cc
index 7385175..750cac1 100644
--- a/services/ui/demo/mus_demo_unittests.cc
+++ b/services/ui/demo/mus_demo_unittests.cc
@@ -15,7 +15,7 @@
 
 namespace {
 
-const char kTestAppName[] = "exe:mus_demo_unittests";
+const char kTestAppName[] = "service:mus_demo_unittests";
 
 void RunCallback(bool* success, const base::Closure& callback, bool result) {
   *success = result;
diff --git a/services/ui/demo/test_manifest.json b/services/ui/demo/test_manifest.json
index f112309..c3be55d 100644
--- a/services/ui/demo/test_manifest.json
+++ b/services/ui/demo/test_manifest.json
@@ -1,5 +1,5 @@
 {
-  "name": "exe:mus_demo_unittests",
+  "name": "service:mus_demo_unittests",
   "display_name": "MUS Demo Unittests",
   "interface_provider_specs": {
     "service_manager:connector": {
diff --git a/services/ui/ime/BUILD.gn b/services/ui/ime/BUILD.gn
index 18d8d7b2..9c0d649d 100644
--- a/services/ui/ime/BUILD.gn
+++ b/services/ui/ime/BUILD.gn
@@ -55,7 +55,6 @@
 }
 
 service_manifest("test_manifest") {
-  type = "exe"
   name = "mus_ime_unittests"
   source = "test_manifest.json"
 }
diff --git a/services/ui/ime/ime_unittest.cc b/services/ui/ime/ime_unittest.cc
index da199c0..776111cf 100644
--- a/services/ui/ime/ime_unittest.cc
+++ b/services/ui/ime/ime_unittest.cc
@@ -44,7 +44,7 @@
 
 class IMEAppTest : public service_manager::test::ServiceTest {
  public:
-  IMEAppTest() : ServiceTest("exe:mus_ime_unittests") {}
+  IMEAppTest() : ServiceTest("service:mus_ime_unittests") {}
   ~IMEAppTest() override {}
 
   // service_manager::test::ServiceTest:
diff --git a/services/ui/ime/test_manifest.json b/services/ui/ime/test_manifest.json
index 277b2f5..b1d12ba 100644
--- a/services/ui/ime/test_manifest.json
+++ b/services/ui/ime/test_manifest.json
@@ -1,5 +1,5 @@
 {
-  "name": "exe:mus_ime_unittests",
+  "name": "service:mus_ime_unittests",
   "display_name": "IME Service Unittests",
   "interface_provider_specs": {
     "service_manager:connector": {
diff --git a/services/ui/service.cc b/services/ui/service.cc
index a710139..e67356c8 100644
--- a/services/ui/service.cc
+++ b/services/ui/service.cc
@@ -193,6 +193,8 @@
     touch_controller_.reset(
         new ws::TouchController(window_server_->display_manager()));
 
+  platform_screen_->Init(window_server_->display_manager());
+
   ime_server_.Init(context->connector());
 }
 
@@ -254,12 +256,6 @@
     touch_controller_->UpdateTouchTransforms();
 }
 
-void Service::CreateDefaultDisplays() {
-  // The display manager will create Displays once hardware or virtual displays
-  // are ready.
-  platform_screen_->Init(window_server_->display_manager());
-}
-
 void Service::Create(const service_manager::Identity& remote_identity,
                      mojom::AccessibilityManagerRequest request) {
   UserState* user_state = GetUserState(remote_identity);
diff --git a/services/ui/service.h b/services/ui/service.h
index 3775efc..ec8d447d 100644
--- a/services/ui/service.h
+++ b/services/ui/service.h
@@ -108,7 +108,6 @@
   void OnFirstDisplayReady() override;
   void OnNoMoreDisplays() override;
   bool IsTestConfig() const override;
-  void CreateDefaultDisplays() override;
   void UpdateTouchTransforms() override;
 
   // service_manager::InterfaceFactory<mojom::AccessibilityManager>
diff --git a/services/ui/ws/cursor_unittest.cc b/services/ui/ws/cursor_unittest.cc
index 19257e5..a3f5944 100644
--- a/services/ui/ws/cursor_unittest.cc
+++ b/services/ui/ws/cursor_unittest.cc
@@ -49,12 +49,10 @@
  protected:
   // testing::Test:
   void SetUp() override {
-    window_server_delegate()->set_num_displays_to_create(1);
+    window_server_delegate()->CreateDisplays(1);
 
     // As a side effect, this allocates Displays.
-    WindowManagerWindowTreeFactorySetTestApi(
-        window_server()->window_manager_window_tree_factory_set())
-        .Add(kTestId1);
+    AddWindowManager(window_server(), kTestId1);
     window_server()->user_id_tracker()->AddUserId(kTestId1);
     window_server()->user_id_tracker()->SetActiveUserId(kTestId1);
   }
diff --git a/services/ui/ws/display_unittest.cc b/services/ui/ws/display_unittest.cc
index 3432489..7876deb 100644
--- a/services/ui/ws/display_unittest.cc
+++ b/services/ui/ws/display_unittest.cc
@@ -77,12 +77,10 @@
 
 TEST_F(DisplayTest, CallsCreateDefaultDisplays) {
   const int kNumHostsToCreate = 2;
-  window_server_delegate()->set_num_displays_to_create(kNumHostsToCreate);
+  window_server_delegate()->CreateDisplays(kNumHostsToCreate);
 
   DisplayManager* display_manager = window_server()->display_manager();
-  WindowManagerWindowTreeFactorySetTestApi(
-      window_server()->window_manager_window_tree_factory_set())
-      .Add(kTestId1);
+  AddWindowManager(window_server(), kTestId1);
   // The first register should trigger creation of the default
   // Displays. There should be kNumHostsToCreate Displays.
   EXPECT_EQ(static_cast<size_t>(kNumHostsToCreate),
@@ -96,9 +94,7 @@
   }
 
   // Add another registry, should trigger creation of another wm.
-  WindowManagerWindowTreeFactorySetTestApi(
-      window_server()->window_manager_window_tree_factory_set())
-      .Add(kTestId2);
+  AddWindowManager(window_server(), kTestId2);
   for (Display* display : display_manager->displays()) {
     ASSERT_EQ(2u, display->num_window_manger_states());
     WindowManagerDisplayRoot* root1 =
@@ -114,17 +110,13 @@
 }
 
 TEST_F(DisplayTest, Destruction) {
-  window_server_delegate()->set_num_displays_to_create(1);
+  window_server_delegate()->CreateDisplays(1);
 
-  WindowManagerWindowTreeFactorySetTestApi(
-      window_server()->window_manager_window_tree_factory_set())
-      .Add(kTestId1);
+  AddWindowManager(window_server(), kTestId1);
 
   // Add another registry, should trigger creation of another wm.
   DisplayManager* display_manager = window_server()->display_manager();
-  WindowManagerWindowTreeFactorySetTestApi(
-      window_server()->window_manager_window_tree_factory_set())
-      .Add(kTestId2);
+  AddWindowManager(window_server(), kTestId2);
   ASSERT_EQ(1u, display_manager->displays().size());
   Display* display = *display_manager->displays().begin();
   ASSERT_EQ(2u, display->num_window_manger_states());
@@ -150,14 +142,10 @@
 }
 
 TEST_F(DisplayTest, EventStateResetOnUserSwitch) {
-  window_server_delegate()->set_num_displays_to_create(1);
+  window_server_delegate()->CreateDisplays(1);
 
-  WindowManagerWindowTreeFactorySetTestApi(
-      window_server()->window_manager_window_tree_factory_set())
-      .Add(kTestId1);
-  WindowManagerWindowTreeFactorySetTestApi(
-      window_server()->window_manager_window_tree_factory_set())
-      .Add(kTestId2);
+  AddWindowManager(window_server(), kTestId1);
+  AddWindowManager(window_server(), kTestId2);
 
   window_server()->user_id_tracker()->SetActiveUserId(kTestId1);
 
@@ -198,13 +186,9 @@
 
 // Verifies capture fails when wm is inactive and succeeds when wm is active.
 TEST_F(DisplayTest, SetCaptureFromWindowManager) {
-  window_server_delegate()->set_num_displays_to_create(1);
-  WindowManagerWindowTreeFactorySetTestApi(
-      window_server()->window_manager_window_tree_factory_set())
-      .Add(kTestId1);
-  WindowManagerWindowTreeFactorySetTestApi(
-      window_server()->window_manager_window_tree_factory_set())
-      .Add(kTestId2);
+  window_server_delegate()->CreateDisplays(1);
+  AddWindowManager(window_server(), kTestId1);
+  AddWindowManager(window_server(), kTestId2);
   window_server()->user_id_tracker()->SetActiveUserId(kTestId1);
   DisplayManager* display_manager = window_server()->display_manager();
   ASSERT_EQ(1u, display_manager->displays().size());
@@ -231,16 +215,12 @@
 }
 
 TEST_F(DisplayTest, FocusFailsForInactiveUser) {
-  window_server_delegate()->set_num_displays_to_create(1);
-  WindowManagerWindowTreeFactorySetTestApi(
-      window_server()->window_manager_window_tree_factory_set())
-      .Add(kTestId1);
+  window_server_delegate()->CreateDisplays(1);
+  AddWindowManager(window_server(), kTestId1);
   TestWindowTreeClient* window_tree_client1 =
       window_server_delegate()->last_client();
   ASSERT_TRUE(window_tree_client1);
-  WindowManagerWindowTreeFactorySetTestApi(
-      window_server()->window_manager_window_tree_factory_set())
-      .Add(kTestId2);
+  AddWindowManager(window_server(), kTestId2);
   window_server()->user_id_tracker()->SetActiveUserId(kTestId1);
   DisplayManager* display_manager = window_server()->display_manager();
   ASSERT_EQ(1u, display_manager->displays().size());
@@ -271,10 +251,8 @@
 
 // Verifies a single tree is used for multiple displays.
 TEST_F(DisplayTest, MultipleDisplays) {
-  window_server_delegate()->set_num_displays_to_create(2);
-  WindowManagerWindowTreeFactorySetTestApi(
-      window_server()->window_manager_window_tree_factory_set())
-      .Add(kTestId1);
+  window_server_delegate()->CreateDisplays(2);
+  AddWindowManager(window_server(), kTestId1);
   window_server()->user_id_tracker()->SetActiveUserId(kTestId1);
   ASSERT_EQ(1u, window_server_delegate()->bindings()->size());
   TestWindowTreeBinding* window_tree_binding =
@@ -349,10 +327,8 @@
 
 // Assertions around destroying a secondary display.
 TEST_F(DisplayTest, DestroyingDisplayDoesntDelete) {
-  window_server_delegate()->set_num_displays_to_create(2);
-  WindowManagerWindowTreeFactorySetTestApi(
-      window_server()->window_manager_window_tree_factory_set())
-      .Add(kTestId1);
+  window_server_delegate()->CreateDisplays(2);
+  AddWindowManager(window_server(), kTestId1);
   window_server()->user_id_tracker()->SetActiveUserId(kTestId1);
   ASSERT_EQ(1u, window_server_delegate()->bindings()->size());
   WindowTree* tree = (*window_server_delegate()->bindings())[0]->tree();
diff --git a/services/ui/ws/test_utils.cc b/services/ui/ws/test_utils.cc
index b707038b..ecf714e 100644
--- a/services/ui/ws/test_utils.cc
+++ b/services/ui/ws/test_utils.cc
@@ -88,24 +88,6 @@
 
 }  // namespace
 
-// WindowManagerWindowTreeFactorySetTestApi ------------------------------------
-
-WindowManagerWindowTreeFactorySetTestApi::
-    WindowManagerWindowTreeFactorySetTestApi(
-        WindowManagerWindowTreeFactorySet*
-            window_manager_window_tree_factory_set)
-    : window_manager_window_tree_factory_set_(
-          window_manager_window_tree_factory_set) {}
-
-WindowManagerWindowTreeFactorySetTestApi::
-    ~WindowManagerWindowTreeFactorySetTestApi() {}
-
-void WindowManagerWindowTreeFactorySetTestApi::Add(const UserId& user_id) {
-  WindowManagerWindowTreeFactory* factory =
-      window_manager_window_tree_factory_set_->Add(user_id, nullptr);
-  factory->CreateWindowTree(nullptr, nullptr);
-}
-
 // TestPlatformDisplayFactory  -------------------------------------------------
 
 const int64_t TestPlatformDisplayFactory::kFirstDisplayId = 1;
@@ -420,6 +402,14 @@
 TestWindowServerDelegate::TestWindowServerDelegate() {}
 TestWindowServerDelegate::~TestWindowServerDelegate() {}
 
+void TestWindowServerDelegate::CreateDisplays(int num_displays) {
+  DCHECK_GT(num_displays, 0);
+  DCHECK(window_server_);
+
+  for (int i = 0; i < num_displays; ++i)
+    AddDisplay();
+}
+
 Display* TestWindowServerDelegate::AddDisplay() {
   // Display manages its own lifetime.
   Display* display = new Display(window_server_, PlatformDisplayInitParams());
@@ -444,14 +434,6 @@
   return std::move(binding);
 }
 
-void TestWindowServerDelegate::CreateDefaultDisplays() {
-  DCHECK(num_displays_to_create_);
-  DCHECK(window_server_);
-
-  for (int i = 0; i < num_displays_to_create_; ++i)
-    AddDisplay();
-}
-
 bool TestWindowServerDelegate::IsTestConfig() const {
   return true;
 }
@@ -549,6 +531,12 @@
 
 // ----------------------------------------------------------------------------
 
+void AddWindowManager(WindowServer* window_server, const UserId& user_id) {
+  window_server->window_manager_window_tree_factory_set()
+      ->Add(user_id, nullptr)
+      ->CreateWindowTree(nullptr, nullptr);
+}
+
 ServerWindow* FirstRoot(WindowTree* tree) {
   return tree->roots().size() == 1u
              ? tree->GetWindow((*tree->roots().begin())->id())
diff --git a/services/ui/ws/test_utils.h b/services/ui/ws/test_utils.h
index 8588d0d..43c4842 100644
--- a/services/ui/ws/test_utils.h
+++ b/services/ui/ws/test_utils.h
@@ -36,23 +36,6 @@
 
 // Collection of utilities useful in creating mus tests.
 
-class WindowManagerWindowTreeFactorySetTestApi {
- public:
-  explicit WindowManagerWindowTreeFactorySetTestApi(
-      WindowManagerWindowTreeFactorySet*
-          window_manager_window_tree_factory_set);
-  ~WindowManagerWindowTreeFactorySetTestApi();
-
-  void Add(const UserId& user_id);
-
- private:
-  WindowManagerWindowTreeFactorySet* window_manager_window_tree_factory_set_;
-
-  DISALLOW_COPY_AND_ASSIGN(WindowManagerWindowTreeFactorySetTestApi);
-};
-
-// -----------------------------------------------------------------------------
-
 class UserDisplayManagerTestApi {
  public:
   explicit UserDisplayManagerTestApi(UserDisplayManager* udm) : udm_(udm) {}
@@ -522,10 +505,6 @@
     window_server_ = window_server;
   }
 
-  void set_num_displays_to_create(int count) {
-    num_displays_to_create_ = count;
-  }
-
   TestWindowTreeClient* last_client() {
     return last_binding() ? last_binding()->client() : nullptr;
   }
@@ -537,6 +516,9 @@
 
   bool got_on_no_more_displays() const { return got_on_no_more_displays_; }
 
+  // Creates |num_displays| displays.
+  void CreateDisplays(int num_displays);
+
   Display* AddDisplay();
 
   // WindowServerDelegate:
@@ -547,14 +529,10 @@
       ws::WindowTree* tree,
       mojom::WindowTreeRequest* tree_request,
       mojom::WindowTreeClientPtr* client) override;
-  void CreateDefaultDisplays() override;
   bool IsTestConfig() const override;
   void UpdateTouchTransforms() override {}
 
  private:
-  // If CreateDefaultDisplays() this is the number of Displays that are
-  // created. The default is 0, which results in a DCHECK.
-  int num_displays_to_create_ = 0;
   WindowServer* window_server_ = nullptr;
   bool got_on_no_more_displays_ = false;
   // All TestWindowTreeBinding objects created via CreateWindowTreeBinding.
@@ -643,6 +621,10 @@
 
 // -----------------------------------------------------------------------------
 
+// Adds a new WM to |window_server| for |user_id|. Creates
+// WindowManagerWindowTreeFactory and associated WindowTree for the WM.
+void AddWindowManager(WindowServer* window_server, const UserId& user_id);
+
 // Returns the first and only root of |tree|. If |tree| has zero or more than
 // one root returns null.
 ServerWindow* FirstRoot(WindowTree* tree);
diff --git a/services/ui/ws/user_display_manager_unittest.cc b/services/ui/ws/user_display_manager_unittest.cc
index cbff458..ee827f0 100644
--- a/services/ui/ws/user_display_manager_unittest.cc
+++ b/services/ui/ws/user_display_manager_unittest.cc
@@ -128,14 +128,12 @@
 };
 
 TEST_F(UserDisplayManagerTest, OnlyNotifyWhenFrameDecorationsSet) {
-  window_server_delegate()->set_num_displays_to_create(1);
+  window_server_delegate()->CreateDisplays(1);
 
   const UserId kUserId1 = "2";
   TestDisplayManagerObserver display_manager_observer1;
   DisplayManager* display_manager = window_server()->display_manager();
-  WindowManagerWindowTreeFactorySetTestApi(
-      window_server()->window_manager_window_tree_factory_set())
-      .Add(kUserId1);
+  AddWindowManager(window_server(), kUserId1);
   UserDisplayManager* user_display_manager1 =
       display_manager->GetUserDisplayManager(kUserId1);
   ASSERT_TRUE(user_display_manager1);
@@ -158,14 +156,12 @@
 }
 
 TEST_F(UserDisplayManagerTest, AddObserverAfterFrameDecorationsSet) {
-  window_server_delegate()->set_num_displays_to_create(1);
+  window_server_delegate()->CreateDisplays(1);
 
   const UserId kUserId1 = "2";
   TestDisplayManagerObserver display_manager_observer1;
   DisplayManager* display_manager = window_server()->display_manager();
-  WindowManagerWindowTreeFactorySetTestApi(
-      window_server()->window_manager_window_tree_factory_set())
-      .Add(kUserId1);
+  AddWindowManager(window_server(), kUserId1);
   UserDisplayManager* user_display_manager1 =
       display_manager->GetUserDisplayManager(kUserId1);
   ASSERT_TRUE(user_display_manager1);
@@ -184,14 +180,12 @@
 }
 
 TEST_F(UserDisplayManagerTest, AddRemoveDisplay) {
-  window_server_delegate()->set_num_displays_to_create(1);
+  window_server_delegate()->CreateDisplays(1);
 
   const UserId kUserId1 = "2";
   TestDisplayManagerObserver display_manager_observer1;
   DisplayManager* display_manager = window_server()->display_manager();
-  WindowManagerWindowTreeFactorySetTestApi(
-      window_server()->window_manager_window_tree_factory_set())
-      .Add(kUserId1);
+  AddWindowManager(window_server(), kUserId1);
   UserDisplayManager* user_display_manager1 =
       display_manager->GetUserDisplayManager(kUserId1);
   ASSERT_TRUE(user_display_manager1);
@@ -223,14 +217,12 @@
 }
 
 TEST_F(UserDisplayManagerTest, NegativeCoordinates) {
-  window_server_delegate()->set_num_displays_to_create(1);
+  window_server_delegate()->CreateDisplays(1);
 
   const UserId kUserId1 = "2";
   TestDisplayManagerObserver display_manager_observer1;
   DisplayManager* display_manager = window_server()->display_manager();
-  WindowManagerWindowTreeFactorySetTestApi(
-      window_server()->window_manager_window_tree_factory_set())
-      .Add(kUserId1);
+  AddWindowManager(window_server(), kUserId1);
   UserDisplayManager* user_display_manager1 =
       display_manager->GetUserDisplayManager(kUserId1);
   ASSERT_TRUE(user_display_manager1);
diff --git a/services/ui/ws/window_manager_state_unittest.cc b/services/ui/ws/window_manager_state_unittest.cc
index 76b1a25..fd3c5a286 100644
--- a/services/ui/ws/window_manager_state_unittest.cc
+++ b/services/ui/ws/window_manager_state_unittest.cc
@@ -547,12 +547,10 @@
 // with no roots.
 TEST(WindowManagerStateShutdownTest, DestroyTreeBeforeDisplay) {
   WindowServerTestHelper ws_test_helper;
-  ws_test_helper.window_server_delegate()->set_num_displays_to_create(1);
+  ws_test_helper.window_server_delegate()->CreateDisplays(1);
   WindowServer* window_server = ws_test_helper.window_server();
   const UserId kUserId1 = "2";
-  WindowManagerWindowTreeFactorySetTestApi(
-      window_server->window_manager_window_tree_factory_set())
-      .Add(kUserId1);
+  AddWindowManager(window_server, kUserId1);
   ASSERT_EQ(1u, window_server->display_manager()->displays().size());
   Display* display = *(window_server->display_manager()->displays().begin());
   WindowManagerDisplayRoot* window_manager_display_root =
diff --git a/services/ui/ws/window_manager_window_tree_factory.h b/services/ui/ws/window_manager_window_tree_factory.h
index 4d5760d..252fdf5 100644
--- a/services/ui/ws/window_manager_window_tree_factory.h
+++ b/services/ui/ws/window_manager_window_tree_factory.h
@@ -19,10 +19,6 @@
 class WindowServer;
 class WindowTree;
 
-namespace test {
-class WindowManagerWindowTreeFactorySetTestApi;
-}
-
 // Implementation of mojom::WindowManagerWindowTreeFactory.
 class WindowManagerWindowTreeFactory
     : public mojom::WindowManagerWindowTreeFactory {
@@ -42,8 +38,6 @@
                         mojom::WindowTreeClientPtr window_tree_client) override;
 
  private:
-  friend class test::WindowManagerWindowTreeFactorySetTestApi;
-
   // Used by tests.
   WindowManagerWindowTreeFactory(WindowManagerWindowTreeFactorySet* registry,
                                  const UserId& user_id);
diff --git a/services/ui/ws/window_manager_window_tree_factory_set.cc b/services/ui/ws/window_manager_window_tree_factory_set.cc
index 50e78227..4d1981c5 100644
--- a/services/ui/ws/window_manager_window_tree_factory_set.cc
+++ b/services/ui/ws/window_manager_window_tree_factory_set.cc
@@ -80,15 +80,8 @@
 
 void WindowManagerWindowTreeFactorySet::OnWindowManagerWindowTreeFactoryReady(
     WindowManagerWindowTreeFactory* factory) {
-  const bool is_first_valid_factory = !got_valid_factory_;
-  got_valid_factory_ = true;
   for (auto& observer : observers_)
     observer.OnWindowManagerWindowTreeFactoryReady(factory);
-
-  // Notify after other observers as WindowServer triggers other
-  // observers being added, which will have already processed the add.
-  if (is_first_valid_factory)
-    window_server_->OnFirstWindowManagerWindowTreeFactoryReady();
 }
 
 void WindowManagerWindowTreeFactorySet::OnUserIdRemoved(const UserId& id) {
diff --git a/services/ui/ws/window_manager_window_tree_factory_set.h b/services/ui/ws/window_manager_window_tree_factory_set.h
index a430d85a..b20c3bc 100644
--- a/services/ui/ws/window_manager_window_tree_factory_set.h
+++ b/services/ui/ws/window_manager_window_tree_factory_set.h
@@ -25,10 +25,6 @@
 class WindowServer;
 class WindowTree;
 
-namespace test {
-class WindowManagerWindowTreeFactorySetTestApi;
-}
-
 // WindowManagerWindowTreeFactorySet tracks the set of registered
 // WindowManagerWindowTreeHostFactories.
 class WindowManagerWindowTreeFactorySet : public UserIdTrackerObserver {
@@ -65,7 +61,6 @@
 
  private:
   friend class WindowManagerWindowTreeFactory;
-  friend class test::WindowManagerWindowTreeFactorySetTestApi;
 
   // Called by WindowManagerWindowTreeFactory when CreateWindowTree() has
   // been called.
@@ -75,8 +70,6 @@
   // UserIdTrackerObserver:
   void OnUserIdRemoved(const UserId& id) override;
 
-  // Set to true the first time a valid factory has been found.
-  bool got_valid_factory_ = false;
   UserIdTracker* id_tracker_;
   WindowServer* window_server_;
 
diff --git a/services/ui/ws/window_server.cc b/services/ui/ws/window_server.cc
index e3da19d..6fc2028 100644
--- a/services/ui/ws/window_server.cc
+++ b/services/ui/ws/window_server.cc
@@ -234,16 +234,6 @@
   return nullptr;
 }
 
-void WindowServer::OnFirstWindowManagerWindowTreeFactoryReady() {
-  if (display_manager_->has_active_or_pending_displays())
-    return;
-
-  // We've been supplied a WindowManagerFactory and no displays have been
-  // created yet. Treat this as a signal to create a Display.
-  // TODO(sky): we need a better way to determine this, most likely a switch.
-  delegate_->CreateDefaultDisplays();
-}
-
 UserActivityMonitor* WindowServer::GetUserActivityMonitorForUser(
     const UserId& user_id) {
   DCHECK_GT(activity_monitor_map_.count(user_id), 0u);
diff --git a/services/ui/ws/window_server.h b/services/ui/ws/window_server.h
index 01e3e5f3..62540dc 100644
--- a/services/ui/ws/window_server.h
+++ b/services/ui/ws/window_server.h
@@ -132,8 +132,6 @@
   }
   const WindowTree* GetTreeWithRoot(const ServerWindow* window) const;
 
-  void OnFirstWindowManagerWindowTreeFactoryReady();
-
   UserActivityMonitor* GetUserActivityMonitorForUser(const UserId& user_id);
 
   WindowManagerWindowTreeFactorySet* window_manager_window_tree_factory_set() {
diff --git a/services/ui/ws/window_server_delegate.h b/services/ui/ws/window_server_delegate.h
index 990f926..50ae3aa 100644
--- a/services/ui/ws/window_server_delegate.h
+++ b/services/ui/ws/window_server_delegate.h
@@ -36,10 +36,6 @@
     WINDOW_MANAGER,
   };
 
-  // Called if no Displays have been created, but a WindowManagerFactory has
-  // been set.
-  virtual void CreateDefaultDisplays() = 0;
-
   // Called once when the AcceleratedWidget of a Display is available.
   virtual void OnFirstDisplayReady();
 
diff --git a/services/ui/ws/window_tree_unittest.cc b/services/ui/ws/window_tree_unittest.cc
index 9d5eaca..6e0d53ae 100644
--- a/services/ui/ws/window_tree_unittest.cc
+++ b/services/ui/ws/window_tree_unittest.cc
@@ -1357,12 +1357,9 @@
     WindowServer* window_server = ws_test_helper.window_server();
     window_server->user_id_tracker()->AddUserId(kTestUserId1);
     const int kNumHostsToCreate = 1;
-    ws_test_helper.window_server_delegate()->set_num_displays_to_create(
-        kNumHostsToCreate);
+    ws_test_helper.window_server_delegate()->CreateDisplays(kNumHostsToCreate);
 
-    WindowManagerWindowTreeFactorySetTestApi(
-        window_server->window_manager_window_tree_factory_set())
-        .Add(kTestUserId1);
+    AddWindowManager(window_server, kTestUserId1);
     window_server->user_id_tracker()->SetActiveUserId(kTestUserId1);
     TestWindowTreeBinding* test_binding =
         ws_test_helper.window_server_delegate()->last_binding();
diff --git a/services/video_capture/mock_device_test.cc b/services/video_capture/mock_device_test.cc
index a6789af8..419d9cf 100644
--- a/services/video_capture/mock_device_test.cc
+++ b/services/video_capture/mock_device_test.cc
@@ -7,7 +7,7 @@
 namespace video_capture {
 
 MockDeviceTest::MockDeviceTest()
-    : service_manager::test::ServiceTest("exe:video_capture_unittests") {}
+    : service_manager::test::ServiceTest("service:video_capture_unittests") {}
 
 MockDeviceTest::~MockDeviceTest() =
     default;
diff --git a/services/video_capture/receiver_mojo_to_media_adapter.cc b/services/video_capture/receiver_mojo_to_media_adapter.cc
index cc06ab5..9200df0 100644
--- a/services/video_capture/receiver_mojo_to_media_adapter.cc
+++ b/services/video_capture/receiver_mojo_to_media_adapter.cc
@@ -17,10 +17,10 @@
 
 void ReceiverMojoToMediaAdapter::OnIncomingCapturedVideoFrame(
     std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer,
-    const scoped_refptr<media::VideoFrame>& frame) {
+    scoped_refptr<media::VideoFrame> frame) {
   // O: |frame| should already be backed by a MojoSharedBufferVideoFrame
   //    assuming we have used the correct buffer factory with the pool.
-  auto video_frame_ptr = media::mojom::VideoFrame::From(frame);
+  auto video_frame_ptr = media::mojom::VideoFrame::From(std::move(frame));
   receiver_->OnIncomingCapturedVideoFrame(std::move(video_frame_ptr));
 }
 
diff --git a/services/video_capture/receiver_mojo_to_media_adapter.h b/services/video_capture/receiver_mojo_to_media_adapter.h
index 810c760..1640613 100644
--- a/services/video_capture/receiver_mojo_to_media_adapter.h
+++ b/services/video_capture/receiver_mojo_to_media_adapter.h
@@ -24,7 +24,7 @@
   // media::VideoFrameReceiver:
   void OnIncomingCapturedVideoFrame(
       std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer,
-      const scoped_refptr<media::VideoFrame>& frame) override;
+      scoped_refptr<media::VideoFrame> frame) override;
   void OnError() override;
   void OnLog(const std::string& message) override;
   void OnBufferDestroyed(int buffer_id_to_drop) override;
diff --git a/services/video_capture/service_unittest_manifest.json b/services/video_capture/service_unittest_manifest.json
index 5ac9419..cf767ca 100644
--- a/services/video_capture/service_unittest_manifest.json
+++ b/services/video_capture/service_unittest_manifest.json
@@ -1,5 +1,5 @@
 {
-  "name": "exe:video_capture_unittests",
+  "name": "service:video_capture_unittests",
   "display_name": "Video Capture Unittests",
   "interface_provider_specs": {
     "service_manager:connector": {
diff --git a/services/video_capture/video_capture_service_test.cc b/services/video_capture/video_capture_service_test.cc
index 4fead98..97860b9 100644
--- a/services/video_capture/video_capture_service_test.cc
+++ b/services/video_capture/video_capture_service_test.cc
@@ -7,7 +7,7 @@
 namespace video_capture {
 
 VideoCaptureServiceTest::VideoCaptureServiceTest()
-    : service_manager::test::ServiceTest("exe:video_capture_unittests") {}
+    : service_manager::test::ServiceTest("service:video_capture_unittests") {}
 
 VideoCaptureServiceTest::~VideoCaptureServiceTest() = default;
 
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 256a9ce..e7466261 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -301,7 +301,7 @@
     "//third_party/skia/src/utils/SkInterpolator.cpp",
     "//third_party/skia/src/utils/SkLayer.cpp",
     "//third_party/skia/src/utils/SkMeshUtils.cpp",
-    "//third_party/skia/src/utils/SkOSFile.cpp",
+    "//third_party/skia/src/utils/SkOSPath.cpp",
     "//third_party/skia/src/utils/SkParsePath.cpp",
 
     # Testing.
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 37b6e15..fa25d91 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -382,6 +382,15 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
+        "test": "views_aura_mus_unittests"
+      },
+      {
+        "args": [
+          "--override-use-gl-with-osmesa-for-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": false
+        },
         "test": "views_mus_interactive_ui_tests"
       },
       {
diff --git a/third_party/WebKit/LayoutTests/OWNERS b/third_party/WebKit/LayoutTests/OWNERS
index 60d20784..0dd80a8 100644
--- a/third_party/WebKit/LayoutTests/OWNERS
+++ b/third_party/WebKit/LayoutTests/OWNERS
@@ -3,5 +3,4 @@
 # Changes to the SmokeTests file must be actually approved so it doesn't grow willy-nilly.
 per-file SmokeTests=dpranke@chromium.org
 per-file SmokeTests=peter@chromium.org
-per-file SmokeTests=abarth@chromium.org
 per-file SmokeTests=ojan@chromium.org
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 1cb340883..f2cf5e1e 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -99,6 +99,8 @@
 
 crbug.com/639147 svg/wicd/test-rightsizing-b.xhtml [ Failure Pass ]
 
+crbug.com/664206 [ Linux Win ] svg/custom/masking-clipping-hidpi.svg [ Failure ]
+
 crbug.com/644433 virtual/gpu/fast/canvas/OffscreenCanvas-2d-pattern-in-worker.html [ Failure ]
 crbug.com/645389 [ Win ] virtual/gpu/fast/canvas/canvas-hit-regions-fallback-element-test.html [ Timeout ]
 crbug.com/645389 [ Win ] virtual/gpu/fast/canvas/canvas-hit-regions-event-test.html [ Timeout ]
@@ -892,8 +894,6 @@
 crbug.com/417782 virtual/rootlayerscrolls/fast/scrolling/scrollable-area-frame-visibility-hidden-child.html [ Failure ]
 crbug.com/417782 virtual/rootlayerscrolls/fast/scrolling/scrollable-area-frame-zero-size-and-border.html [ Failure ]
 crbug.com/417782 virtual/rootlayerscrolls/fast/scrolling/scrollable-area-frame.html [ Failure ]
-crbug.com/417782 virtual/rootlayerscrolls/fast/scrolling/scrollbar-tickmarks-styled.html [ Failure ]
-crbug.com/417782 virtual/rootlayerscrolls/fast/scrolling/scrollbar-tickmarks-styled-after-onload.html [ Failure ]
 
 crbug.com/651343 fast/text/international/inline-plaintext-relayout-with-leading-neutrals.html [ Pass Failure ]
 
diff --git a/third_party/WebKit/LayoutTests/animations/css-animation-overrides-svg-presentation-attribute-animation-expected.txt b/third_party/WebKit/LayoutTests/animations/css-animation-overrides-svg-presentation-attribute-animation-expected.txt
deleted file mode 100644
index 4409e5c..0000000
--- a/third_party/WebKit/LayoutTests/animations/css-animation-overrides-svg-presentation-attribute-animation-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL CSS animations always override SVG presentation attribute animations assert_equals: expected "rgb(0, 128, 0)" but got "rgb(255, 0, 0)"
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/scale-and-scroll-iframe-window.html b/third_party/WebKit/LayoutTests/fast/events/scale-and-scroll-iframe-window.html
index 3d10cc2f..06c80a4d 100644
--- a/third_party/WebKit/LayoutTests/fast/events/scale-and-scroll-iframe-window.html
+++ b/third_party/WebKit/LayoutTests/fast/events/scale-and-scroll-iframe-window.html
@@ -1,5 +1,11 @@
 <html>
 <head>
+    <style>
+        ::-webkit-scrollbar {
+            width: 0px;
+            height: 0px;
+        }
+    </style>
     <script>
         window.enablePixelTesting = true;
 
diff --git a/third_party/WebKit/LayoutTests/fast/js/custom-constructors-expected.txt b/third_party/WebKit/LayoutTests/fast/js/custom-constructors-expected.txt
index bca019d..b97d4c6 100644
--- a/third_party/WebKit/LayoutTests/fast/js/custom-constructors-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/js/custom-constructors-expected.txt
@@ -11,8 +11,8 @@
 PASS new Image(0, 0).height is 0
 PASS new Image(100).width is 100
 PASS new Image(100, 200).height is 200
-PASS new Image(-100).width is -100
-PASS new Image(-100, -200).height is -200
+PASS new Image(-100).width is 0
+PASS new Image(-100, -200).height is 0
 PASS new Image().hasAttribute('height') is false
 PASS new Image().hasAttribute('width') is false
 PASS new Image(0).hasAttribute('height') is false
diff --git a/third_party/WebKit/LayoutTests/fast/js/script-tests/custom-constructors.js b/third_party/WebKit/LayoutTests/fast/js/script-tests/custom-constructors.js
index e7c749ef..e27deb2c 100644
--- a/third_party/WebKit/LayoutTests/fast/js/script-tests/custom-constructors.js
+++ b/third_party/WebKit/LayoutTests/fast/js/script-tests/custom-constructors.js
@@ -12,8 +12,8 @@
 shouldBe("new Image(0, 0).height", "0");
 shouldBe("new Image(100).width", "100");
 shouldBe("new Image(100, 200).height", "200");
-shouldBe("new Image(-100).width", "-100");
-shouldBe("new Image(-100, -200).height", "-200");
+shouldBe("new Image(-100).width", "0");
+shouldBe("new Image(-100, -200).height", "0");
 
 shouldBe("new Image().hasAttribute('height')", "false");
 shouldBe("new Image().hasAttribute('width')", "false");
diff --git a/third_party/WebKit/LayoutTests/fast/speech/scripted/speechrecognition-audiotrack.html b/third_party/WebKit/LayoutTests/fast/speech/scripted/speechrecognition-audiotrack.html
new file mode 100644
index 0000000..cc8ac807
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/speech/scripted/speechrecognition-audiotrack.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<title>SpeechRecognition audioTrack attribute</title>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script>
+test(() => {
+  const sr = new webkitSpeechRecognition();
+  assert_equals(sr.audioTrack, null);
+}, 'initial state');
+
+test(() => {
+  const sr = new webkitSpeechRecognition();
+  sr.audioTrack = null;
+  assert_equals(sr.audioTrack, null);
+  sr.audioTrack = undefined;
+  assert_equals(sr.audioTrack, null);
+}, 'setting to null/undefined');
+
+test(() => {
+  const sr = new webkitSpeechRecognition();
+  for (const value of [{}, "", window]) {
+    assert_throws(new TypeError, () => { sr.audioTrack = value; } );
+    assert_equals(sr.audioTrack, null);
+  }
+}, 'setting to non-MediaStreamTrack values');
+
+promise_test(() => {
+  return navigator.mediaDevices.getUserMedia({ audio: true })
+      .then(stream => {
+        const audioTrack = stream.getAudioTracks()[0];
+        assert_equals(audioTrack.kind, "audio");
+        const sr = new webkitSpeechRecognition();
+        sr.audioTrack = audioTrack;
+        assert_equals(sr.audioTrack, audioTrack);
+      });
+}, 'setting to MediaStreamTrack with kind "audio"');
+
+// TODO(burnik): Should assigning a video track throw instead?
+promise_test(() => {
+  return navigator.mediaDevices.getUserMedia({ video: true })
+      .then(stream => {
+        const videoTrack = stream.getVideoTracks()[0];
+        assert_equals(videoTrack.kind, "video");
+        const sr = new webkitSpeechRecognition();
+        sr.videoTrack = videoTrack;
+        assert_equals(sr.videoTrack, videoTrack);
+      });
+}, 'setting to MediaStreamTrack with kind "video"');
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/speech/scripted/speechrecognition-basics-expected.txt b/third_party/WebKit/LayoutTests/fast/speech/scripted/speechrecognition-basics-expected.txt
index a531e4f..66d0363 100644
--- a/third_party/WebKit/LayoutTests/fast/speech/scripted/speechrecognition-basics-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/speech/scripted/speechrecognition-basics-expected.txt
@@ -55,9 +55,6 @@
 PASS count is 7
 onend
 PASS count is 8
-
-handleEmptyGrammarTest():
-onend
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/fast/speech/scripted/speechrecognition-basics.html b/third_party/WebKit/LayoutTests/fast/speech/scripted/speechrecognition-basics.html
index 7730319..57d0d3e 100644
--- a/third_party/WebKit/LayoutTests/fast/speech/scripted/speechrecognition-basics.html
+++ b/third_party/WebKit/LayoutTests/fast/speech/scripted/speechrecognition-basics.html
@@ -81,20 +81,6 @@
     r.onend = function() {
         debug('onend');
         shouldBe('count', '8');
-        ++count;
-        handleEmptyGrammarTest();
-    }
-
-    r.start();
-}
-
-// See crbug.com/545855 for details.
-function handleEmptyGrammarTest() {
-    debug('\nhandleEmptyGrammarTest():');
-    var r = new webkitSpeechRecognition("");
-    r.grammars = "";
-    r.onend = function() {
-        debug('onend');
         finishJSTest();
     }
 
diff --git a/third_party/WebKit/LayoutTests/fast/speech/scripted/speechrecognition-grammars.html b/third_party/WebKit/LayoutTests/fast/speech/scripted/speechrecognition-grammars.html
new file mode 100644
index 0000000..19835a4f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/speech/scripted/speechrecognition-grammars.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>SpeechRecognition grammars attribute</title>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script>
+test(() => {
+  const sr = new webkitSpeechRecognition();
+  assert_true(sr.grammars instanceof webkitSpeechGrammarList);
+}, 'initial state');
+
+test(() => {
+  for (const value of [null, undefined, {}, "", window]) {
+    const sr = new webkitSpeechRecognition();
+    const grammars = sr.grammars;
+    assert_throws(new TypeError, () => { sr.grammars = value; } );
+    assert_equals(sr.grammars, grammars);
+  }
+}, 'setting to non-SpeechGrammarList values');
+
+test(() => {
+  const sr = new webkitSpeechRecognition();
+  const grammars = new webkitSpeechGrammarList();
+  sr.grammars = grammars;
+  assert_equals(sr.grammars, grammars);
+}, 'setting to SpeechGrammarList');
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/transforms/selection-bounds-in-transformed-view.html b/third_party/WebKit/LayoutTests/fast/transforms/selection-bounds-in-transformed-view.html
index f16af247..04ca106f 100644
--- a/third_party/WebKit/LayoutTests/fast/transforms/selection-bounds-in-transformed-view.html
+++ b/third_party/WebKit/LayoutTests/fast/transforms/selection-bounds-in-transformed-view.html
@@ -13,11 +13,11 @@
 
         window.internals.settings.setPreferCompositingToLCDTextEnabled(true);
         document.execCommand("FindString", false, "target");
-        document.getElementById("result").innerText = internals.visualViewportScrollY() === 860 ? "PASS" : "FAIL (scrollTop:" + document.scrollingElement.scrollTop + ")";
+        document.getElementById("result").innerText = internals.visualViewportScrollY() === 863 ? "PASS" : "FAIL (scrollTop:" + document.scrollingElement.scrollTop + ")";
 
         window.internals.settings.setPreferCompositingToLCDTextEnabled(false);
         document.execCommand("FindString", false, "target");
-        document.getElementById("result").innerText += internals.visualViewportScrollY() === 860 ? " PASS" : " FAIL (scrollTop:" + document.scrollingElement.scrollTop + ")";
+        document.getElementById("result").innerText += internals.visualViewportScrollY() === 863 ? " PASS" : " FAIL (scrollTop:" + document.scrollingElement.scrollTop + ")";
     };
     </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/elements/event-listeners-framework-with-service-worker-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/elements/event-listeners-framework-with-service-worker-expected.txt
index a1d8b7c..cb9f3837 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/elements/event-listeners-framework-with-service-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/elements/event-listeners-framework-with-service-worker-expected.txt
@@ -13,6 +13,7 @@
 [expanded] WindowRemoveevent-listeners-framework-with-service-worker.html:59
     useCapture: false
     passive: false
+    once: false
     handler: function onload(event) {
   runTest()
 }
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/FileAPI/fileReader-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/FileAPI/fileReader-expected.txt
deleted file mode 100644
index 1cc9d165..0000000
--- a/third_party/WebKit/LayoutTests/imported/wpt/FileAPI/fileReader-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-PASS FileReader interface object 
-PASS no-argument FileReader constructor 
-FAIL FileReader States -- abort assert_unreached: abort event should fire sync Reached unreachable code
-PASS FileReader States -- events 
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/dom/reflection-embedded-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/html/dom/reflection-embedded-expected.txt
index 162c683..421d48c 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/dom/reflection-embedded-expected.txt
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/dom/reflection-embedded-expected.txt
@@ -20,12 +20,8 @@
 PASS img.crossOrigin: 52 tests
 PASS img.useMap: 32 tests
 PASS img.isMap: 33 tests
-PASS img.width: 7 tests
-FAIL img.width: IDL set to 2147483648 assert_equals: getAttribute() expected "0" but got "-2147483648"
-FAIL img.width: IDL set to 4294967295 assert_equals: getAttribute() expected "0" but got "-1"
-PASS img.height: 7 tests
-FAIL img.height: IDL set to 2147483648 assert_equals: getAttribute() expected "0" but got "-2147483648"
-FAIL img.height: IDL set to 4294967295 assert_equals: getAttribute() expected "0" but got "-1"
+PASS img.width: 9 tests
+PASS img.height: 9 tests
 PASS img.name: 32 tests
 PASS img.lowsrc: 38 tests
 PASS img.align: 32 tests
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/page-frames-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/page-frames-expected.txt
index 02869bf..77259bd 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/page-frames-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/page-frames-expected.txt
@@ -1,8 +1,10 @@
+
 Recording started
 Tracing complete
 Frames in TracingStartedInPage
-url: inspector-protocol/timeline/page-frames.html name: 
+url: inspector-protocol/timeline/page-frames.html name:  parent: undefined nodeId: undefined
+url: data:text/html,<script>window.foo = 42</script> name: frame0 parent: string nodeId: number
 Frames in CommitLoad events
-url: about:blank name: Frame No. 1
-url: inspector-protocol/resources/blank.html name: 
+url: about:blank name: Frame No. 1 parent: string nodeId: number
+url: inspector-protocol/resources/blank.html name:  parent: string nodeId: number
 
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/page-frames.html b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/page-frames.html
index f774670..f6621af 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/page-frames.html
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/page-frames.html
@@ -38,11 +38,12 @@
     function dumpFrame(frame)
     {
         var url = frame.url.replace(/.*\/(([^/]*\/){2}[^/]*$)/, "$1");
-        InspectorTest.log(`url: ${url} name: ${frame.name}`);
+        InspectorTest.log(`url: ${url} name: ${frame.name} parent: ${typeof frame.parent} nodeId: ${typeof frame.nodeId}`);
     }
 }
 </script>
 </head>
 <body onLoad="runTest();">
+<iframe src="data:text/html,<script>window.foo = 42</script>" name="frame0"></iframe>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/inspector/console/command-line-api-getEventListeners-expected.txt b/third_party/WebKit/LayoutTests/inspector/console/command-line-api-getEventListeners-expected.txt
index aa2ac9f1..c2f5743 100644
--- a/third_party/WebKit/LayoutTests/inspector/console/command-line-api-getEventListeners-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/console/command-line-api-getEventListeners-expected.txt
@@ -6,6 +6,7 @@
 [page] keydown: {
 [page]     0: {
 [page]         listener: function listener1() { }
+[page]         once: false
 [page]         passive: false
 [page]         remove: function remove() { [Command Line API] }
 [page]         type: "keydown"
@@ -13,6 +14,7 @@
 [page]     }
 [page]     1: {
 [page]         listener: function listener2() { }
+[page]         once: false
 [page]         passive: false
 [page]         remove: function remove() { [Command Line API] }
 [page]         type: "keydown"
@@ -22,6 +24,7 @@
 [page] wheel: {
 [page]     0: {
 [page]         listener: function listener2() { }
+[page]         once: false
 [page]         passive: true
 [page]         remove: function remove() { [Command Line API] }
 [page]         type: "wheel"
@@ -32,6 +35,7 @@
 [page] keydown: {
 [page]     0: {
 [page]         listener: function listener2() { }
+[page]         once: false
 [page]         passive: false
 [page]         remove: function remove() { [Command Line API] }
 [page]         type: "keydown"
@@ -42,15 +46,27 @@
 [page] keydown: {
 [page]     0: {
 [page]         listener: function listener2() { }
+[page]         once: false
 [page]         passive: false
 [page]         remove: function remove() { [Command Line API] }
 [page]         type: "keydown"
 [page]         useCapture: true
 [page]     }
 [page] }
+[page] keyup: {
+[page]     0: {
+[page]         listener: function listener2() { }
+[page]         once: true
+[page]         passive: false
+[page]         remove: function remove() { [Command Line API] }
+[page]         type: "keyup"
+[page]         useCapture: false
+[page]     }
+[page] }
 [page] mousedown: {
 [page]     0: {
 [page]         listener: function listener2() { }
+[page]         once: false
 [page]         passive: false
 [page]         remove: function remove() { [Command Line API] }
 [page]         type: "mousedown"
@@ -60,6 +76,7 @@
 [page] mousemove: {
 [page]     0: {
 [page]         listener: function listener1() { }
+[page]         once: false
 [page]         passive: false
 [page]         remove: function remove() { [Command Line API] }
 [page]         type: "mousemove"
@@ -70,6 +87,7 @@
 [page] click: {
 [page]     0: {
 [page]         listener: function onclick(event) { alert(1) }
+[page]         once: false
 [page]         passive: false
 [page]         remove: function remove() { [Command Line API] }
 [page]         type: "click"
@@ -79,6 +97,7 @@
 [page] mouseover: {
 [page]     0: {
 [page]         listener: function onmouseover(event) { listener2() }
+[page]         once: false
 [page]         passive: false
 [page]         remove: function remove() { [Command Line API] }
 [page]         type: "mouseover"
@@ -89,6 +108,7 @@
 [page] load: {
 [page]     0: {
 [page]         listener: function onload(event) { runTest() }
+[page]         once: false
 [page]         passive: false
 [page]         remove: function remove() { [Command Line API] }
 [page]         type: "load"
@@ -98,6 +118,7 @@
 [page] popstate: {
 [page]     0: {
 [page]         listener: function listener1() { }
+[page]         once: false
 [page]         passive: false
 [page]         remove: function remove() { [Command Line API] }
 [page]         type: "popstate"
diff --git a/third_party/WebKit/LayoutTests/inspector/console/command-line-api-getEventListeners.html b/third_party/WebKit/LayoutTests/inspector/console/command-line-api-getEventListeners.html
index cbd6791..96a21f8 100644
--- a/third_party/WebKit/LayoutTests/inspector/console/command-line-api-getEventListeners.html
+++ b/third_party/WebKit/LayoutTests/inspector/console/command-line-api-getEventListeners.html
@@ -38,6 +38,7 @@
 document.getElementById("outer").addEventListener("mousemove", listener1, false);
 document.getElementById("outer").addEventListener("mousedown", listener2, true);
 document.getElementById("outer").addEventListener("keydown", listener2, true);
+document.getElementById("outer").addEventListener("keyup", listener2, {once: true});
 window.addEventListener("popstate", listener1, false);
 
 function dumpObject(object, prefix)
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar-custom-framework-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar-custom-framework-expected.txt
index 2802416..4b85e26f 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar-custom-framework-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar-custom-framework-expected.txt
@@ -8,6 +8,7 @@
 [expanded] button#inspectedNodeRemoveevent-listener-sidebar-custom-framework.html:23
     useCapture: false
     passive: false
+    once: false
     handler: function internalHandler(e)
     {
         console.log("I'm internal event handler");
@@ -22,6 +23,7 @@
 [expanded] button#inspectedNodeevent-listener-sidebar-custom-framework.html:13
     useCapture: true
     passive: false
+    once: false
     handler: function customFirstEventListener(e)
     {
         console.log("I'm first custom event listener");
@@ -32,6 +34,7 @@
 [expanded] button#inspectedNodeevent-listener-sidebar-custom-framework.html:18
     useCapture: false
     passive: false
+    once: false
     handler: function customSecondEventListener(e)
     {
         console.log("I'm second custom event listener");
@@ -43,6 +46,7 @@
 [expanded] button#inspectedNodeRemoveevent-listener-sidebar-custom-framework.html:23
     useCapture: false
     passive: false
+    once: false
     handler: function internalHandler(e)
     {
         console.log("I'm internal event handler");
@@ -58,7 +62,7 @@
 	internal handler isn't a function or empty
 	internal handler isn't a function or empty
 	fetcher call produced error: Error in getter
-	event listener's type isn't string or empty, event listener's useCapture isn't boolean or undefined, event listener's passive isn't boolean or undefined, event listener's handler isn't a function or empty
+	event listener's type isn't string or empty, event listener's useCapture isn't boolean or undefined, event listener's passive isn't boolean or undefined, event listener's once isn't boolean or undefined, event listener's handler isn't a function or empty
 	Error in getter
 	Error in getter
 	internal handler isn't a function or empty
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar-custom-framework.html b/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar-custom-framework.html
index d0992d0..831d78c 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar-custom-framework.html
+++ b/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar-custom-framework.html
@@ -36,8 +36,8 @@
     function frameworkEventListeners(node)
     {
         if (node === inspectedNode) {
-            return {eventListeners: [{type: "customFirst", useCapture: true, passive: false, handler: customFirstEventListener},
-                                     {type: "customSecond", useCapture: false, passive: false, handler: customSecondEventListener}],
+            return {eventListeners: [{type: "customFirst", useCapture: true, passive: false, once: false, handler: customFirstEventListener},
+                                     {type: "customSecond", useCapture: false, passive: false, once: false, handler: customSecondEventListener}],
                     internalHandlers: [internalHandler]};
         }
         return {eventListeners: []};
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar-expected.txt
index 984aaad..4dcedae1 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar-expected.txt
@@ -7,41 +7,49 @@
 [expanded] documentRemoveevent-listener-sidebar.html:6
     useCapture: false
     passive: false
+    once: false
     handler: function documentClickHandler(event) { console.log("click - document - attribute"); }
 == normal
 [expanded] documentRemoveevent-listener-sidebar.html:31
     useCapture: true
     passive: false
+    once: false
     handler: function () { console.log("click - document - handleEvent"); }
 == normal
 [expanded] documentRemoveevent-listener-sidebar.html:25
     useCapture: true
     passive: false
+    once: false
     handler: function ObjectHandler() { document.addEventListener("click", this, true); }
 == normal
 [expanded] documentRemoveevent-listener-sidebar.html:19
     useCapture: true
     passive: false
+    once: false
     handler: function (event) { console.log("click - document - capturing"); }
 == normal
 [expanded] button#nodeRemoveevent-listener-sidebar.html:17
     useCapture: false
     passive: false
+    once: false
     handler: function (event) { console.log("click - button - bubbling (registered after attribute)"); }
 == normal
 [expanded] button#nodeRemoveevent-listener-sidebar.html:16
     useCapture: false
     passive: false
+    once: false
     handler: function (event) { console.log("click - button - attribute"); }
 == normal
 [expanded] button#nodeRemoveevent-listener-sidebar.html:12
     useCapture: false
     passive: false
+    once: false
     handler: function clickHandler(event) { console.log("click - button - bubbling (registered before attribute)"); }
 == normal
 [expanded] button#nodeRemoveevent-listener-sidebar.html:15
     useCapture: true
     passive: false
+    once: false
     handler: function (event) { console.log("click - button - capturing"); }
 
 ======== custom event ========
@@ -49,6 +57,7 @@
 [expanded] bodyRemoveevent-listener-sidebar.html:10
     useCapture: true
     passive: false
+    once: true
     handler: function f() {}
 
 ======== hover ========
@@ -56,6 +65,7 @@
 [expanded] button#nodeRemoveevent-listener-sidebar.html:14
     useCapture: false
     passive: false
+    once: false
     handler: function hoverHandler(event) { console.log("hover - button - bubbling"); }
 
 ======== load ========
@@ -63,6 +73,7 @@
 [expanded] WindowRemoveevent-listener-sidebar.html:76
     useCapture: false
     passive: false
+    once: false
     handler: function onload(event) {
   onloadHandler()
 }
@@ -72,6 +83,7 @@
 [expanded] bodyRemoveToggle Passiveevent-listener-sidebar.html:10
     useCapture: false
     passive: true
+    once: false
     handler: function f() {}
 Listeners for selected node only(should be no listeners):
 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar-jquery1-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar-jquery1-expected.txt
index d529aba..8232364 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar-jquery1-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar-jquery1-expected.txt
@@ -7,21 +7,25 @@
 [expanded] button#nodeRemoveevent-listener-sidebar-jquery1.html:11
     useCapture: true
     passive: false
+    once: false
     handler: function (){ console.log("second jquery"); }
 == frameworkUser
 [expanded] button#nodeRemoveevent-listener-sidebar-jquery1.html:10
     useCapture: true
     passive: false
+    once: false
     handler: function (){ console.log("first jquery"); }
 == normal
 [expanded] button#nodeRemoveevent-listener-sidebar-jquery1.html:12
     useCapture: false
     passive: false
+    once: false
     handler: function () { console.log("addEventListener"); }
 == frameworkInternal
 [expanded] button#nodeRemovejquery-1.11.3.min.js:4
     useCapture: false
     passive: false
+    once: false
     handler: function (a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)}
 
 ======== load ========
@@ -29,6 +33,7 @@
 [expanded] WindowRemoveevent-listener-sidebar-jquery1.html:36
     useCapture: false
     passive: false
+    once: false
     handler: function onload(event) {
   onloadHandler()
 }
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar-jquery2-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar-jquery2-expected.txt
index 21f62d3..e7ba8c4d 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar-jquery2-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar-jquery2-expected.txt
@@ -7,26 +7,31 @@
 [expanded] button#nodeRemoveevent-listener-sidebar-jquery2.html:11
     useCapture: true
     passive: false
+    once: false
     handler: function (){ console.log("second jquery"); }
 == frameworkUser
 [expanded] button#nodeRemoveevent-listener-sidebar-jquery2.html:10
     useCapture: true
     passive: false
+    once: false
     handler: function (){ console.log("first jquery"); }
 == normal
 [expanded] button#nodeRemoveevent-listener-sidebar-jquery2.html:13
     useCapture: false
     passive: false
+    once: false
     handler: function () { console.log("onclick"); }
 == normal
 [expanded] button#nodeRemoveevent-listener-sidebar-jquery2.html:12
     useCapture: false
     passive: false
+    once: false
     handler: function () { console.log("addEventListener"); }
 == frameworkInternal
 [expanded] button#nodeRemovejquery-2.1.4.min.js:3
     useCapture: false
     passive: false
+    once: false
     handler: function (b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}
 
 ======== load ========
@@ -34,6 +39,7 @@
 [expanded] WindowRemoveevent-listener-sidebar-jquery2.html:52
     useCapture: false
     passive: false
+    once: false
     handler: function onload(event) {
   onloadHandler()
 }
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar.html b/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar.html
index 1fa1cc8..4b90125 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar.html
+++ b/third_party/WebKit/LayoutTests/inspector/elements/event-listener-sidebar.html
@@ -20,7 +20,7 @@
     document.addEventListener("mousedown", f, false);
     document.removeEventListener("mousedown", f, false);
 
-    document.body.addEventListener("custom event", f, true);
+    document.body.addEventListener("custom event", f, {capture: true, once: true});
 
     function ObjectHandler() { document.addEventListener("click", this, true); }
     ObjectHandler.prototype.toString = function() { return "ObjectHandler"; }
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/event-listeners-about-blank-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/event-listeners-about-blank-expected.txt
index 9d653831..1063ac2 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/event-listeners-about-blank-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/event-listeners-about-blank-expected.txt
@@ -7,6 +7,7 @@
 [expanded] bodyRemoveevent-listeners-about-blank.html:9
     useCapture: true
     passive: false
+    once: false
     handler: function f() {}
 
 ======== hover ========
@@ -14,6 +15,7 @@
 [expanded] div#div-in-iframeRemoveevent-listeners-about-blank.html:9
     useCapture: true
     passive: false
+    once: true
     handler: function f() {}
 
 ======== wheel ========
@@ -21,5 +23,6 @@
 [expanded] bodyRemoveToggle Passiveevent-listeners-about-blank.html:9
     useCapture: false
     passive: true
+    once: false
     handler: function f() {}
 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/event-listeners-about-blank.html b/third_party/WebKit/LayoutTests/inspector/elements/event-listeners-about-blank.html
index 97d9212..9ab97f0d 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/event-listeners-about-blank.html
+++ b/third_party/WebKit/LayoutTests/inspector/elements/event-listeners-about-blank.html
@@ -12,7 +12,7 @@
     body.addEventListener("click", f, true);
     var div = frame.contentDocument.createElement("div");
     div.id = "div-in-iframe";
-    div.addEventListener("hover", f, true);
+    div.addEventListener("hover", f, {capture: true, once: true});
     body.appendChild(div);
     body.addEventListener("wheel", f, {"passive": true});
 }
diff --git a/third_party/WebKit/LayoutTests/inspector/input-event-warning.html b/third_party/WebKit/LayoutTests/inspector/input-event-warning.html
index e393690..2b1c62b 100644
--- a/third_party/WebKit/LayoutTests/inspector/input-event-warning.html
+++ b/third_party/WebKit/LayoutTests/inspector/input-event-warning.html
@@ -42,7 +42,7 @@
        target.addEventListener(type, eventListener1);
        target.addEventListener(type, eventListener2);
     }
-    var deadline = performance.now() + 110;
+    var deadline = performance.now() + 100;
     while (performance.now() < deadline) {};
     if (window.testRunner)
         window.testRunner.setDumpConsoleMessages(false);
@@ -67,13 +67,15 @@
 
     function step1()
     {
-        InspectorTest.mainTarget.logAgent().setReportViolationsEnabled(false);
+        InspectorTest.mainTarget.logAgent().startViolationsReport([
+            {name: 'blockedEvent', threshold: 30000}]);
         InspectorTest.evaluateInPage("dispatchEvents()", step2);
     }
 
     function step2()
     {
-        InspectorTest.mainTarget.logAgent().setReportViolationsEnabled(true);
+        InspectorTest.mainTarget.logAgent().startViolationsReport([
+            {name: 'blockedEvent', threshold: 0.001}]);
         InspectorTest.addResult("There should be no warnings above this line");
         InspectorTest.evaluateInPage("dispatchEvents()", () => InspectorTest.completeTest());
     }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table-cell-collapsed-border-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table-cell-collapsed-border-expected.txt
index cd078c63..c8b10b4 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table-cell-collapsed-border-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table-cell-collapsed-border-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable TABLE",
+          "rect": [3, 60, 441, 405],
+          "reason": "invalidate paint rectangle"
+        },
+        {
           "object": "LayoutTableCell TD id='t3'",
           "rect": [220, 260, 122, 110],
           "reason": "style change"
@@ -26,48 +31,8 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD id='t1'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD id='t2'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD id='t3'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTableCell TD id='t1'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table-outer-border-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table-outer-border-expected.txt
index b06d1cb..dd21182 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table-outer-border-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table-outer-border-expected.txt
@@ -95,14 +95,6 @@
       "reason": "layoutObject removal"
     },
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
       "object": "LayoutTable TABLE id='table' class='green'",
       "reason": "layoutObject insertion"
     },
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/border-collapse-change-separate-to-collapse-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/border-collapse-change-separate-to-collapse-expected.txt
index 18cc801..ba69534 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/border-collapse-change-separate-to-collapse-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/border-collapse-change-separate-to-collapse-expected.txt
@@ -51,14 +51,6 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
       "object": "LayoutTable TABLE id='table'",
       "reason": "style change"
     },
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-cell-append-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-cell-append-expected.txt
index 59ef756..3a611cf 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-cell-append-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-cell-append-expected.txt
@@ -8,6 +8,11 @@
       "paintInvalidations": [
         {
           "object": "LayoutTable TABLE",
+          "rect": [8, 8, 120, 56],
+          "reason": "invalidate paint rectangle"
+        },
+        {
+          "object": "LayoutTable TABLE",
           "rect": [8, 59, 120, 5],
           "reason": "incremental"
         },
@@ -31,12 +36,8 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTable TABLE",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-cell-border-color-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-cell-border-color-expected.txt
index 76964c5..89451a0 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-cell-border-color-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-cell-border-color-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable TABLE",
+          "rect": [8, 8, 114, 54],
+          "reason": "invalidate paint rectangle"
+        },
+        {
           "object": "LayoutTableCell TD id='foo'",
           "rect": [8, 8, 60, 54],
           "reason": "style change"
@@ -16,12 +21,8 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD id='foo'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTableCell TD id='foo'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-cell-border-width-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-cell-border-width-expected.txt
index 6a5f6dc0..930bb9f5 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-cell-border-width-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-cell-border-width-expected.txt
@@ -8,6 +8,11 @@
       "paintInvalidations": [
         {
           "object": "LayoutTable TABLE",
+          "rect": [8, 8, 114, 54],
+          "reason": "invalidate paint rectangle"
+        },
+        {
+          "object": "LayoutTable TABLE",
           "rect": [8, 58, 114, 4],
           "reason": "incremental"
         },
@@ -36,12 +41,8 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD id='foo'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTable TABLE",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-col-border-color-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-col-border-color-expected.txt
index 27c93b4..09774d8 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-col-border-color-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-col-border-color-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable TABLE",
+          "rect": [8, 8, 113, 104],
+          "reason": "invalidate paint rectangle"
+        },
+        {
           "object": "LayoutTableCol COL id='col'",
           "rect": [8, 8, 113, 104],
           "reason": "style change"
@@ -16,20 +21,8 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTableCol COL id='col'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-col-border-width-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-col-border-width-expected.txt
index 5ba2070..39e6514d 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-col-border-width-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-col-border-width-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable TABLE",
+          "rect": [8, 8, 113, 104],
+          "reason": "invalidate paint rectangle"
+        },
+        {
           "object": "LayoutTableCol COL id='col'",
           "rect": [8, 8, 113, 104],
           "reason": "style change"
@@ -61,20 +66,8 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTable TABLE",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-colgroup-border-color-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-colgroup-border-color-expected.txt
index 71449ce..5629d33 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-colgroup-border-color-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-colgroup-border-color-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable TABLE",
+          "rect": [7, 7, 167, 104],
+          "reason": "invalidate paint rectangle"
+        },
+        {
           "object": "LayoutTableCol COLGROUP id='colgroup'",
           "rect": [8, 8, 166, 102],
           "reason": "style change"
@@ -16,28 +21,8 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTableCol COLGROUP id='colgroup'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-colgroup-border-width-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-colgroup-border-width-expected.txt
index 7f1d98f..d83e8b9 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-colgroup-border-width-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-colgroup-border-width-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable TABLE",
+          "rect": [7, 7, 167, 104],
+          "reason": "invalidate paint rectangle"
+        },
+        {
           "object": "LayoutTableCol COLGROUP id='colgroup'",
           "rect": [8, 8, 166, 102],
           "reason": "style change"
@@ -71,28 +76,8 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTable TABLE",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-row-border-width-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-row-border-width-expected.txt
index 4013197..3bc0e1e 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-row-border-width-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-row-border-width-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable TABLE",
+          "rect": [8, 8, 60, 103],
+          "reason": "invalidate paint rectangle"
+        },
+        {
           "object": "LayoutTableCell TD",
           "rect": [8, 8, 60, 54],
           "reason": "forced by layout"
@@ -41,12 +46,8 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTable TABLE",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-table-border-color-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-table-border-color-expected.txt
index 5a9447d..e9ea09b 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-table-border-color-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-table-border-color-expected.txt
@@ -16,10 +16,6 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
       "object": "LayoutTable TABLE id='tbl'",
       "reason": "style change"
     }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-table-border-width-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-table-border-width-expected.txt
index 7ebee422..c2f06f30 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-table-border-width-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-table-border-width-expected.txt
@@ -21,10 +21,6 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
       "object": "LayoutTable TABLE id='tbl'",
       "reason": "style change"
     },
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-tbody-border-width-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-tbody-border-width-expected.txt
index 2b0dc2a..27ee5850 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-tbody-border-width-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-tbody-border-width-expected.txt
@@ -8,6 +8,11 @@
       "paintInvalidations": [
         {
           "object": "LayoutTable TABLE",
+          "rect": [8, 8, 114, 153],
+          "reason": "invalidate paint rectangle"
+        },
+        {
+          "object": "LayoutTable TABLE",
           "rect": [8, 159, 114, 2],
           "reason": "incremental"
         },
@@ -81,28 +86,8 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTable TABLE",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-change-collapsed-border-color-expected.html b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-change-collapsed-border-color-expected.html
new file mode 100644
index 0000000..cf6608bf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-change-collapsed-border-color-expected.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<p style="height: 50px">Passes if green borders are visible surrounding A's cell and no red.
+<table style="border-collapse: collapse;">
+  <tr>
+    <td id="a" style="border: 5px solid green; width: 100px; height: 100px">A</td>
+  </tr>
+</table>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-change-collapsed-border-color-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-change-collapsed-border-color-expected.txt
new file mode 100644
index 0000000..992af1b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-change-collapsed-border-color-expected.txt
@@ -0,0 +1,45 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTable TABLE",
+          "rect": [8, 82, 112, 112],
+          "reason": "invalidate paint rectangle"
+        }
+      ]
+    },
+    {
+      "name": "LayoutTableCell TD id='a'",
+      "position": [10, 84],
+      "bounds": [107, 107],
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableCell TD id='a'",
+          "rect": [-2, -2, 112, 112],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
+    },
+    {
+      "object": "LayoutTableCell TD id='a'",
+      "reason": "style change"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-change-collapsed-border-color.html b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-change-collapsed-border-color.html
new file mode 100644
index 0000000..96981e21
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-change-collapsed-border-color.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<p style="height: 50px">Passes if green borders are visible surrounding A's cell and no red.
+<table style="border-collapse: collapse;">
+  <tr>
+    <td id="a" style="border: 5px solid red; width: 100px; height: 100px; will-change: transform">A</td>
+  </tr>
+</table>
+<script src="../resources/text-based-repaint.js"></script>
+<script>
+function repaintTest() {
+  a.style.borderColor = 'green';
+}
+onload = runRepaintAndPixelTest;
+</script>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-row-change-collapsed-border-color-expected.html b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-row-change-collapsed-border-color-expected.html
new file mode 100644
index 0000000..dc1f099
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-row-change-collapsed-border-color-expected.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<p style="height: 50px">Passes if green borders are visible surrounding A's cell and no red.
+<table style="border-collapse: collapse;">
+  <tr id="a" style="border: 5px solid green">
+    <td style="width: 100px; height: 100px">A</td>
+  </tr>
+</table>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-row-change-collapsed-border-color-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-row-change-collapsed-border-color-expected.txt
new file mode 100644
index 0000000..c26169aa2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-row-change-collapsed-border-color-expected.txt
@@ -0,0 +1,41 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTable TABLE",
+          "rect": [8, 82, 112, 112],
+          "reason": "invalidate paint rectangle"
+        }
+      ]
+    },
+    {
+      "name": "LayoutTableRow TR id='a'",
+      "position": [10, 84],
+      "bounds": [107, 107],
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableRow TR id='a'",
+          "rect": [0, 0, 107, 107],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
+    },
+    {
+      "object": "LayoutTableRow TR id='a'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-row-change-collapsed-border-color.html b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-row-change-collapsed-border-color.html
new file mode 100644
index 0000000..b3f51b3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-row-change-collapsed-border-color.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<p style="height: 50px">Passes if green borders are visible surrounding A's cell and no red.
+<table style="border-collapse: collapse;">
+  <tr id="a" style="border: 5px solid red; will-change: transform">
+    <td style="width: 100px; height: 100px">A</td>
+  </tr>
+</table>
+<script src="../resources/text-based-repaint.js"></script>
+<script>
+function repaintTest() {
+  a.style.borderColor = 'green';
+}
+onload = runRepaintAndPixelTest;
+</script>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-section-change-collapsed-border-color-expected.html b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-section-change-collapsed-border-color-expected.html
new file mode 100644
index 0000000..b76f9bf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-section-change-collapsed-border-color-expected.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<p style="height: 50px">Passes if green borders are visible surrounding A's cell and no red.
+<table style="border-collapse: collapse;">
+  <thead id="a" style="border: 5px solid green">
+    <tr>
+      <td style="width: 100px; height: 100px">A</td>
+    </tr>
+  </head>
+</table>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-section-change-collapsed-border-color-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-section-change-collapsed-border-color-expected.txt
new file mode 100644
index 0000000..8505bd02
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-section-change-collapsed-border-color-expected.txt
@@ -0,0 +1,41 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTable TABLE",
+          "rect": [8, 82, 112, 112],
+          "reason": "invalidate paint rectangle"
+        }
+      ]
+    },
+    {
+      "name": "LayoutTableSection THEAD id='a'",
+      "position": [10, 84],
+      "bounds": [107, 107],
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableSection THEAD id='a'",
+          "rect": [0, 0, 107, 107],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
+    },
+    {
+      "object": "LayoutTableSection THEAD id='a'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-section-change-collapsed-border-color.html b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-section-change-collapsed-border-color.html
new file mode 100644
index 0000000..c7b9932
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-section-change-collapsed-border-color.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<p style="height: 50px">Passes if green borders are visible surrounding A's cell and no red.
+<table style="border-collapse: collapse;">
+  <thead id="a" style="border: 5px solid red; will-change: transform">
+    <tr>
+      <td style="width: 100px; height: 100px">A</td>
+    </tr>
+  </head>
+</table>
+<script src="../resources/text-based-repaint.js"></script>
+<script>
+function repaintTest() {
+  a.style.borderColor = 'green';
+}
+onload = runRepaintAndPixelTest;
+</script>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-centered-inline-under-fixed-pos-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-centered-inline-under-fixed-pos-expected.txt
index 73762ea..6087f005 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-centered-inline-under-fixed-pos-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-centered-inline-under-fixed-pos-expected.txt
@@ -122,7 +122,7 @@
         {
           "object": "LayoutBlockFlow (positioned) DIV class='container'",
           "rect": [0, 0, 400, 600],
-          "reason": "full"
+          "reason": "forced by layout"
         },
         {
           "object": "LayoutView #document",
@@ -154,7 +154,7 @@
     },
     {
       "object": "LayoutBlockFlow (positioned) DIV class='container'",
-      "reason": "full"
+      "reason": "forced by layout"
     },
     {
       "object": "LayoutBlockFlow DIV class='parent'",
@@ -181,7 +181,7 @@
         {
           "object": "LayoutBlockFlow (positioned) DIV class='container'",
           "rect": [0, 0, 800, 600],
-          "reason": "full"
+          "reason": "forced by layout"
         },
         {
           "object": "LayoutView #document",
@@ -208,7 +208,7 @@
     },
     {
       "object": "LayoutBlockFlow (positioned) DIV class='container'",
-      "reason": "full"
+      "reason": "forced by layout"
     },
     {
       "object": "LayoutBlockFlow DIV class='parent'",
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table-cell-collapsed-border-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table-cell-collapsed-border-expected.txt
index e452e1f..b1b1bfb 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table-cell-collapsed-border-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table-cell-collapsed-border-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable TABLE",
+          "rect": [3, 64, 441, 405],
+          "reason": "invalidate paint rectangle"
+        },
+        {
           "object": "LayoutTableCell TD id='t3'",
           "rect": [220, 264, 122, 110],
           "reason": "style change"
@@ -26,48 +31,8 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD id='t1'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD id='t2'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD id='t3'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTableCell TD id='t1'",
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table-collapsed-border-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table-collapsed-border-expected.txt
index 71fa8da..15129f2 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table-collapsed-border-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table-collapsed-border-expected.txt
@@ -147,30 +147,6 @@
       "reason": "layoutObject removal"
     },
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
       "object": "LayoutTable TABLE id='t'",
       "reason": "style change"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt
index 6bb9255..2882c0c 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable (relative positioned) TABLE",
+          "rect": [8, 8, 114, 54],
+          "reason": "invalidate paint rectangle"
+        },
+        {
           "object": "LayoutTableCell TD id='foo'",
           "rect": [8, 8, 60, 54],
           "reason": "style change"
@@ -30,12 +35,8 @@
       "reason": "layoutObject removal"
     },
     {
-      "object": "LayoutTableCell TD id='foo'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable (relative positioned) TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTableCell TD id='foo'",
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/window-resize-vertical-writing-mode-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
index 609d5f1..d38357c 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
@@ -365,22 +365,22 @@
         {
           "object": "LayoutBlockFlow BODY",
           "rect": [437, 8, 355, 584],
-          "reason": "forced by layout"
+          "reason": "bounds change"
         },
         {
           "object": "LayoutBlockFlow BODY",
           "rect": [37, 8, 355, 584],
-          "reason": "forced by layout"
+          "reason": "bounds change"
         },
         {
           "object": "LayoutText #text",
           "rect": [439, 7, 352, 554],
-          "reason": "forced by layout"
+          "reason": "bounds change"
         },
         {
           "object": "LayoutText #text",
           "rect": [39, 7, 352, 554],
-          "reason": "forced by layout"
+          "reason": "bounds change"
         }
       ]
     }
@@ -392,35 +392,35 @@
     },
     {
       "object": "LayoutBlockFlow BODY",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "RootInlineBox",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "LayoutText #text",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "InlineTextBox 'AAAA BBBB CCCC'",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "InlineTextBox 'DDDD EEEE FFFF'",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "InlineTextBox 'GGGG HHHH IIII JJJJ'",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "InlineTextBox 'KKKK LLLL MMMM'",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "InlineTextBox 'NNNN'",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/table-collapsed-border-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/table-collapsed-border-expected.txt
index d64d6549..38b14e4 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/table-collapsed-border-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/table-collapsed-border-expected.txt
@@ -147,30 +147,6 @@
       "reason": "layoutObject removal"
     },
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
       "object": "LayoutTable TABLE id='t'",
       "reason": "style change"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt
index 1804202..d682150 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable (relative positioned) TABLE",
+          "rect": [8, 8, 114, 54],
+          "reason": "invalidate paint rectangle"
+        },
+        {
           "object": "LayoutTableCell TD id='foo'",
           "rect": [8, 8, 60, 54],
           "reason": "style change"
@@ -30,12 +35,8 @@
       "reason": "layoutObject removal"
     },
     {
-      "object": "LayoutTableCell TD id='foo'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable (relative positioned) TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTableCell TD id='foo'",
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/window-resize-vertical-writing-mode-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
index 244c8ab..ab5f6d51 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
@@ -365,22 +365,22 @@
         {
           "object": "LayoutBlockFlow BODY",
           "rect": [447, 8, 345, 584],
-          "reason": "forced by layout"
+          "reason": "bounds change"
         },
         {
           "object": "LayoutBlockFlow BODY",
           "rect": [47, 8, 345, 584],
-          "reason": "forced by layout"
+          "reason": "bounds change"
         },
         {
           "object": "LayoutText #text",
           "rect": [447, 8, 345, 565],
-          "reason": "forced by layout"
+          "reason": "bounds change"
         },
         {
           "object": "LayoutText #text",
           "rect": [47, 8, 345, 565],
-          "reason": "forced by layout"
+          "reason": "bounds change"
         }
       ]
     }
@@ -392,35 +392,35 @@
     },
     {
       "object": "LayoutBlockFlow BODY",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "RootInlineBox",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "LayoutText #text",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "InlineTextBox 'AAAA BBBB CCCC'",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "InlineTextBox 'DDDD EEEE FFFF'",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "InlineTextBox 'GGGG HHHH IIII JJJJ'",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "InlineTextBox 'KKKK LLLL MMMM'",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "InlineTextBox 'NNNN'",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/table-collapsed-border-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/table-collapsed-border-expected.txt
index 5eba99d..9eac676d 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/table-collapsed-border-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/table-collapsed-border-expected.txt
@@ -147,30 +147,6 @@
       "reason": "layoutObject removal"
     },
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
       "object": "LayoutTable TABLE id='t'",
       "reason": "style change"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt
index 560cab9..1904492 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable (relative positioned) TABLE",
+          "rect": [8, 8, 114, 54],
+          "reason": "invalidate paint rectangle"
+        },
+        {
           "object": "LayoutTableCell TD id='foo'",
           "rect": [8, 8, 60, 54],
           "reason": "style change"
@@ -30,12 +35,8 @@
       "reason": "layoutObject removal"
     },
     {
-      "object": "LayoutTableCell TD id='foo'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable (relative positioned) TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTableCell TD id='foo'",
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/window-resize-vertical-writing-mode-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
index 3be2d60..89c04bcb 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
@@ -365,22 +365,22 @@
         {
           "object": "LayoutBlockFlow BODY",
           "rect": [447, 8, 345, 584],
-          "reason": "forced by layout"
+          "reason": "bounds change"
         },
         {
           "object": "LayoutBlockFlow BODY",
           "rect": [47, 8, 345, 584],
-          "reason": "forced by layout"
+          "reason": "bounds change"
         },
         {
           "object": "LayoutText #text",
           "rect": [449, 7, 342, 566],
-          "reason": "forced by layout"
+          "reason": "bounds change"
         },
         {
           "object": "LayoutText #text",
           "rect": [49, 7, 342, 566],
-          "reason": "forced by layout"
+          "reason": "bounds change"
         }
       ]
     }
@@ -392,35 +392,35 @@
     },
     {
       "object": "LayoutBlockFlow BODY",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "RootInlineBox",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "LayoutText #text",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "InlineTextBox 'AAAA BBBB CCCC'",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "InlineTextBox 'DDDD EEEE FFFF'",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "InlineTextBox 'GGGG HHHH IIII JJJJ'",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "InlineTextBox 'KKKK LLLL MMMM'",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     },
     {
       "object": "InlineTextBox 'NNNN'",
-      "reason": "forced by layout"
+      "reason": "bounds change"
     }
   ]
 }
diff --git a/third_party/WebKit/Source/bindings/IDLExtendedAttributes.txt b/third_party/WebKit/Source/bindings/IDLExtendedAttributes.txt
index 94b66bf..41fce66 100644
--- a/third_party/WebKit/Source/bindings/IDLExtendedAttributes.txt
+++ b/third_party/WebKit/Source/bindings/IDLExtendedAttributes.txt
@@ -103,3 +103,4 @@
 URL
 Unforgeable
 Unscopable
+WebModuleAPI
\ No newline at end of file
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8EventListenerInfo.h b/third_party/WebKit/Source/bindings/core/v8/V8EventListenerInfo.h
index ea53dc1..c39b7c6 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8EventListenerInfo.h
+++ b/third_party/WebKit/Source/bindings/core/v8/V8EventListenerInfo.h
@@ -17,17 +17,20 @@
   V8EventListenerInfo(AtomicString eventType,
                       bool useCapture,
                       bool passive,
+                      bool once,
                       v8::Local<v8::Object> handler,
                       v8::MaybeLocal<v8::Function> removeFunction)
       : eventType(eventType),
         useCapture(useCapture),
         passive(passive),
+        once(once),
         handler(handler),
         removeFunction(removeFunction) {}
 
   AtomicString eventType;
   bool useCapture;
   bool passive;
+  bool once;
   v8::Local<v8::Object> handler;
   v8::MaybeLocal<v8::Function> removeFunction;
 };
diff --git a/third_party/WebKit/Source/bindings/scripts/code_generator_web_module.py b/third_party/WebKit/Source/bindings/scripts/code_generator_web_module.py
new file mode 100644
index 0000000..92ad2b6
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/scripts/code_generator_web_module.py
@@ -0,0 +1,83 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# pylint: disable=import-error,print-statement,relative-import
+
+"""Generates Blink Web Module bindings.
+
+The Blink Web Module bindings provide a stable, IDL-generated interface for the
+Web Modules.
+
+The Web Modules are the high-level services like Autofill,
+Autocomplete, Translate, Distiller, Phishing Detector, and others. Web Modules
+typically want to introspec the document and rendering infromation to implement
+browser features.
+
+The bindings are meant to be as simple and as ephemeral as possible, mostly just
+wrapping existing DOM classes. Their primary goal is to avoid leaking the actual
+DOM classes to the Web Modules layer.
+"""
+
+import os
+import posixpath
+
+from code_generator import CodeGeneratorBase
+# TODO(dglazkov): Move TypedefResolver to code_generator.py
+from code_generator_v8 import TypedefResolver
+from v8_interface import interface_context
+
+MODULE_PYNAME = os.path.splitext(os.path.basename(__file__))[0] + '.py'
+
+WEB_MODULE_IDL_ATTRIBUTE = 'WebModuleAPI'
+
+
+class CodeGeneratorWebModule(CodeGeneratorBase):
+    def __init__(self, info_provider, cache_dir, output_dir):
+        CodeGeneratorBase.__init__(self, MODULE_PYNAME, info_provider,
+                                   cache_dir, output_dir)
+        self.typedef_resolver = TypedefResolver(info_provider)
+
+    def get_template(self):
+        template_filename = 'web_module_interface.cpp.tmpl'
+        return self.jinja_env.get_template(template_filename)
+
+    # TODO(dglazkov): Move to CodeGeneratorBase.
+    def output_paths(self, definition_name):
+        header_path = posixpath.join(self.output_dir,
+                                     'Web%s.h' % definition_name)
+        cpp_path = posixpath.join(self.output_dir,
+                                  'Web%s.cpp' % definition_name)
+        return header_path, cpp_path
+
+    def generate_code(self, definitions, definition_name):
+        self.typedef_resolver.resolve(definitions, definition_name)
+        header_path, cpp_path = self.output_paths(definition_name)
+
+        template_context = {}
+        # TODO(dglazkov): Implement dictionaries
+        if definition_name not in definitions.interfaces:
+            return None
+
+        interface = definitions.interfaces[definition_name]
+        if WEB_MODULE_IDL_ATTRIBUTE not in interface.extended_attributes:
+            return None
+
+        # TODO(dglazkov): Implement callback interfaces.
+        # TODO(dglazkov): Implement partial interfaces.
+        if interface.is_callback and interface.is_partial:
+            return None
+
+        template_context = interface_context(interface,
+                                             definitions.interfaces)
+        template_context['code_generator'] = MODULE_PYNAME
+        template_context['class_name'] = definition_name
+
+        cpp_template = self.get_template()
+        cpp_text = cpp_template.render(template_context)
+
+        header_text = 'header'
+        return (
+            (header_path, header_text),
+            (cpp_path, cpp_text)
+        )
diff --git a/third_party/WebKit/Source/bindings/scripts/idl_compiler.py b/third_party/WebKit/Source/bindings/scripts/idl_compiler.py
index c8e232e..444415c 100755
--- a/third_party/WebKit/Source/bindings/scripts/idl_compiler.py
+++ b/third_party/WebKit/Source/bindings/scripts/idl_compiler.py
@@ -115,6 +115,11 @@
         target_definitions = definitions[self.target_component]
         output_code_list = self.code_generator.generate_code(
             target_definitions, interface_name)
+
+        # Generator may choose to omit the file.
+        if output_code_list is None:
+            return
+
         for output_path, output_code in output_code_list:
             write_file(output_code, output_path)
 
diff --git a/third_party/WebKit/Source/bindings/scripts/scripts.gni b/third_party/WebKit/Source/bindings/scripts/scripts.gni
index d744ab7..bdb3205f 100644
--- a/third_party/WebKit/Source/bindings/scripts/scripts.gni
+++ b/third_party/WebKit/Source/bindings/scripts/scripts.gni
@@ -41,6 +41,7 @@
                                      # V8 code generator
                                      "code_generator.py",
                                      "code_generator_v8.py",
+                                     "code_generator_web_module.py",
                                      "v8_attributes.py",
                                      "v8_callback_function.py",
                                      "v8_callback_interface.py",
diff --git a/third_party/WebKit/Source/bindings/scripts/v8_dictionary.py b/third_party/WebKit/Source/bindings/scripts/v8_dictionary.py
index 4bdb8fd2..e15f60fd 100644
--- a/third_party/WebKit/Source/bindings/scripts/v8_dictionary.py
+++ b/third_party/WebKit/Source/bindings/scripts/v8_dictionary.py
@@ -188,13 +188,14 @@
     idl_type = unwrap_nullable_if_needed(member.idl_type)
     cpp_name = v8_utilities.cpp_name(member)
 
-    def getter_expression():
-        if idl_type.impl_should_use_nullable_container:
-            return 'm_%s.get()' % cpp_name
-        return 'm_%s' % cpp_name
+    nullable_indicator_name = None
+    if not idl_type.cpp_type_has_null_value:
+        nullable_indicator_name = 'm_has' + cpp_name[0].upper() + cpp_name[1:]
 
     def has_method_expression():
-        if idl_type.impl_should_use_nullable_container or idl_type.is_enum or idl_type.is_string_type or idl_type.is_union_type:
+        if nullable_indicator_name:
+            return nullable_indicator_name
+        elif idl_type.is_enum or idl_type.is_string_type or idl_type.is_union_type:
             return '!m_%s.isNull()' % cpp_name
         elif idl_type.name in ['Any', 'Object']:
             return '!(m_{0}.isEmpty() || m_{0}.isNull() || m_{0}.isUndefined())'.format(cpp_name)
@@ -203,12 +204,6 @@
         else:
             return 'm_%s' % cpp_name
 
-    def member_cpp_type():
-        member_cpp_type = idl_type.cpp_type_args(used_in_cpp_sequence=True)
-        if idl_type.impl_should_use_nullable_container:
-            return v8_types.cpp_template_type('Nullable', member_cpp_type)
-        return member_cpp_type
-
     cpp_default_value = None
     if member.default_value and not member.default_value.is_null:
         cpp_default_value = idl_type.literal_cpp_value(member.default_value)
@@ -217,13 +212,14 @@
     return {
         'cpp_default_value': cpp_default_value,
         'cpp_name': cpp_name,
-        'getter_expression': getter_expression(),
+        'getter_expression': 'm_' + cpp_name,
         'has_method_expression': has_method_expression(),
         'has_method_name': has_method_name_for_dictionary_member(member),
         'is_nullable': idl_type.is_nullable,
         'is_traceable': idl_type.is_traceable,
-        'member_cpp_type': member_cpp_type(),
+        'member_cpp_type': idl_type.cpp_type_args(used_in_cpp_sequence=True),
         'null_setter_name': null_setter_name_for_dictionary_member(member),
+        'nullable_indicator_name': nullable_indicator_name,
         'rvalue_cpp_type': idl_type.cpp_type_args(used_as_rvalue_type=True),
         'setter_name': setter_name_for_dictionary_member(member),
     }
diff --git a/third_party/WebKit/Source/bindings/scripts/v8_types.py b/third_party/WebKit/Source/bindings/scripts/v8_types.py
index 6fcbe63..242020bf 100644
--- a/third_party/WebKit/Source/bindings/scripts/v8_types.py
+++ b/third_party/WebKit/Source/bindings/scripts/v8_types.py
@@ -422,17 +422,8 @@
     includes.update(includes_for_interface(interface_name))
 
 
-def impl_should_use_nullable_container(idl_type):
-    return not(idl_type.cpp_type_has_null_value)
-
-IdlTypeBase.impl_should_use_nullable_container = property(
-    impl_should_use_nullable_container)
-
-
 def impl_includes_for_type(idl_type, interfaces_info):
     includes_for_type = set()
-    if idl_type.impl_should_use_nullable_container:
-        includes_for_type.add('bindings/core/v8/Nullable.h')
 
     idl_type = idl_type.preprocessed_type
     native_array_element_type = idl_type.native_array_element_type
diff --git a/third_party/WebKit/Source/bindings/templates/dictionary_impl.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/dictionary_impl.cpp.tmpl
index 3e612a1..b1c55e0 100644
--- a/third_party/WebKit/Source/bindings/templates/dictionary_impl.cpp.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/dictionary_impl.cpp.tmpl
@@ -18,6 +18,33 @@
 
 {{cpp_class}}::~{{cpp_class}}() {}
 
+{% for member in members %}
+bool {{cpp_class}}::{{member.has_method_name}}() const {
+  return {{member.has_method_expression}};
+}
+{{member.rvalue_cpp_type}} {{cpp_class}}::{{member.cpp_name}}() const {
+  {% if member.nullable_indicator_name %}
+  DCHECK({{member.nullable_indicator_name}});
+  {% endif %}
+  return {{member.getter_expression}};
+}
+void {{cpp_class}}::{{member.setter_name}}({{member.rvalue_cpp_type}} value) {
+  m_{{member.cpp_name}} = value;
+  {% if member.nullable_indicator_name %}
+  {{member.nullable_indicator_name}} = true;
+  {% endif %}
+}
+{% if member.null_setter_name %}
+void {{cpp_class}}::{{member.null_setter_name}}() {
+  {% if member.nullable_indicator_name %}
+  {{member.nullable_indicator_name}} = false;
+  {% else %}
+  m_{{member.cpp_name}} = {{member.member_cpp_type}}();
+  {% endif %}
+}
+{% endif %}
+{% endfor %}
+
 DEFINE_TRACE({{cpp_class}}) {
   {% for member in members if member.is_traceable %}
   visitor->trace(m_{{member.cpp_name}});
diff --git a/third_party/WebKit/Source/bindings/templates/dictionary_impl.h.tmpl b/third_party/WebKit/Source/bindings/templates/dictionary_impl.h.tmpl
index c7c15cf..f69a7bd 100644
--- a/third_party/WebKit/Source/bindings/templates/dictionary_impl.h.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/dictionary_impl.h.tmpl
@@ -18,11 +18,11 @@
   virtual ~{{cpp_class}}();
 
   {% for member in members %}
-  bool {{member.has_method_name}}() const { return {{member.has_method_expression}}; }
-  {{member.rvalue_cpp_type}} {{member.cpp_name}}() const { return {{member.getter_expression}}; }
-  void {{member.setter_name}}({{member.rvalue_cpp_type}} value) { m_{{member.cpp_name}} = value; }
+  bool {{member.has_method_name}}() const;
+  {{member.rvalue_cpp_type}} {{member.cpp_name}}() const;
+  void {{member.setter_name}}({{member.rvalue_cpp_type}});
   {% if member.null_setter_name %}
-  void {{member.null_setter_name}}() { m_{{member.cpp_name}} = {{member.member_cpp_type}}(); }
+  void {{member.null_setter_name}}();
   {% endif %}
 
   {% endfor %}
@@ -31,6 +31,9 @@
 
  private:
   {% for member in members %}
+  {% if member.nullable_indicator_name %}
+  bool {{member.nullable_indicator_name}} = false;
+  {% endif %}
   {{member.member_cpp_type}} m_{{member.cpp_name}};
   {% endfor %}
 
diff --git a/third_party/WebKit/Source/bindings/templates/templates.gni b/third_party/WebKit/Source/bindings/templates/templates.gni
index 35f972f2..bc9e938 100644
--- a/third_party/WebKit/Source/bindings/templates/templates.gni
+++ b/third_party/WebKit/Source/bindings/templates/templates.gni
@@ -24,5 +24,6 @@
                                                 "partial_interface.h.tmpl",
                                                 "union_container.cpp.tmpl",
                                                 "union_container.h.tmpl",
+                                                "web_module_interface.cpp.tmpl",
                                               ],
                                               "abspath")
diff --git a/third_party/WebKit/Source/bindings/templates/web_module_interface.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/web_module_interface.cpp.tmpl
new file mode 100644
index 0000000..af5afefa
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/templates/web_module_interface.cpp.tmpl
@@ -0,0 +1,36 @@
+{% filter format_blink_cpp_source_code %}
+
+{% include 'copyright_block.txt' %}
+// clang-format on
+
+#include "Web{{class_name}}.h"
+
+// TODO(dglazkov): Implement generating includes.
+{% for filename in cpp_includes %}
+#include "{{filename}}"
+{% endfor %}
+
+namespace blink {
+namespace api {
+
+// TODO(dglazkov): Implement constant generation
+{% for constant in constants %}
+// {{ constant.name }}
+{% endfor %}
+
+// TODO(dglazkov): Implement constructor generation
+
+// TODO(dglazkov): Implement attribute getter/setter generation
+{% for attribute in attributes %}
+// {{attribute.name}}
+{% endfor %}
+
+// TODO(dglazkov): Implement method generation
+{% for method in methods %}
+// {{method.cpp_type}} Cpp{{class_name}}::{{method.name}}
+{% endfor %}
+
+}  // namespace api
+}  // namespace blink
+
+{% endfilter %}
\ No newline at end of file
diff --git a/third_party/WebKit/Source/bindings/tests/idls/core/TestInterface3.idl b/third_party/WebKit/Source/bindings/tests/idls/core/TestInterface3.idl
index de08b550..e9a8981 100644
--- a/third_party/WebKit/Source/bindings/tests/idls/core/TestInterface3.idl
+++ b/third_party/WebKit/Source/bindings/tests/idls/core/TestInterface3.idl
@@ -36,6 +36,7 @@
 [
     Custom=VisitDOMWrapper, // Conflict with [SetWrapperReferenceTo] and [SetWrapperReferenceFrom]
     NoImplHeader,
+    WebModuleAPI,
 ] interface TestInterface3 {
     [Custom] getter boolean (unsigned long index);
     [Custom] setter boolean (unsigned long index, Node value);
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.cpp b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.cpp
index cfe7c01..4fb234b 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.cpp
@@ -28,6 +28,332 @@
 
 TestDictionary::~TestDictionary() {}
 
+bool TestDictionary::hasAnyMember() const {
+  return !(m_anyMember.isEmpty() || m_anyMember.isNull() || m_anyMember.isUndefined());
+}
+ScriptValue TestDictionary::anyMember() const {
+  return m_anyMember;
+}
+void TestDictionary::setAnyMember(ScriptValue value) {
+  m_anyMember = value;
+}
+bool TestDictionary::hasBooleanMember() const {
+  return m_hasBooleanMember;
+}
+bool TestDictionary::booleanMember() const {
+  DCHECK(m_hasBooleanMember);
+  return m_booleanMember;
+}
+void TestDictionary::setBooleanMember(bool value) {
+  m_booleanMember = value;
+  m_hasBooleanMember = true;
+}
+bool TestDictionary::hasCreateMember() const {
+  return m_hasCreateMember;
+}
+bool TestDictionary::createMember() const {
+  DCHECK(m_hasCreateMember);
+  return m_createMember;
+}
+void TestDictionary::setCreateMember(bool value) {
+  m_createMember = value;
+  m_hasCreateMember = true;
+}
+bool TestDictionary::hasDictionaryMember() const {
+  return !m_dictionaryMember.isUndefinedOrNull();
+}
+Dictionary TestDictionary::dictionaryMember() const {
+  return m_dictionaryMember;
+}
+void TestDictionary::setDictionaryMember(Dictionary value) {
+  m_dictionaryMember = value;
+}
+bool TestDictionary::hasDoubleOrNullMember() const {
+  return m_hasDoubleOrNullMember;
+}
+double TestDictionary::doubleOrNullMember() const {
+  DCHECK(m_hasDoubleOrNullMember);
+  return m_doubleOrNullMember;
+}
+void TestDictionary::setDoubleOrNullMember(double value) {
+  m_doubleOrNullMember = value;
+  m_hasDoubleOrNullMember = true;
+}
+void TestDictionary::setDoubleOrNullMemberToNull() {
+  m_hasDoubleOrNullMember = false;
+}
+bool TestDictionary::hasDoubleOrStringMember() const {
+  return !m_doubleOrStringMember.isNull();
+}
+const DoubleOrString& TestDictionary::doubleOrStringMember() const {
+  return m_doubleOrStringMember;
+}
+void TestDictionary::setDoubleOrStringMember(const DoubleOrString& value) {
+  m_doubleOrStringMember = value;
+}
+bool TestDictionary::hasDoubleOrStringSequenceMember() const {
+  return m_hasDoubleOrStringSequenceMember;
+}
+const HeapVector<DoubleOrString>& TestDictionary::doubleOrStringSequenceMember() const {
+  DCHECK(m_hasDoubleOrStringSequenceMember);
+  return m_doubleOrStringSequenceMember;
+}
+void TestDictionary::setDoubleOrStringSequenceMember(const HeapVector<DoubleOrString>& value) {
+  m_doubleOrStringSequenceMember = value;
+  m_hasDoubleOrStringSequenceMember = true;
+}
+bool TestDictionary::hasElementOrNullMember() const {
+  return m_elementOrNullMember;
+}
+Element* TestDictionary::elementOrNullMember() const {
+  return m_elementOrNullMember;
+}
+void TestDictionary::setElementOrNullMember(Element* value) {
+  m_elementOrNullMember = value;
+}
+void TestDictionary::setElementOrNullMemberToNull() {
+  m_elementOrNullMember = Member<Element>();
+}
+bool TestDictionary::hasEnumMember() const {
+  return !m_enumMember.isNull();
+}
+String TestDictionary::enumMember() const {
+  return m_enumMember;
+}
+void TestDictionary::setEnumMember(String value) {
+  m_enumMember = value;
+}
+bool TestDictionary::hasEnumSequenceMember() const {
+  return m_hasEnumSequenceMember;
+}
+const Vector<String>& TestDictionary::enumSequenceMember() const {
+  DCHECK(m_hasEnumSequenceMember);
+  return m_enumSequenceMember;
+}
+void TestDictionary::setEnumSequenceMember(const Vector<String>& value) {
+  m_enumSequenceMember = value;
+  m_hasEnumSequenceMember = true;
+}
+bool TestDictionary::hasEventTargetMember() const {
+  return m_eventTargetMember;
+}
+EventTarget* TestDictionary::eventTargetMember() const {
+  return m_eventTargetMember;
+}
+void TestDictionary::setEventTargetMember(EventTarget* value) {
+  m_eventTargetMember = value;
+}
+bool TestDictionary::hasInternalDictionarySequenceMember() const {
+  return m_hasInternalDictionarySequenceMember;
+}
+const HeapVector<InternalDictionary>& TestDictionary::internalDictionarySequenceMember() const {
+  DCHECK(m_hasInternalDictionarySequenceMember);
+  return m_internalDictionarySequenceMember;
+}
+void TestDictionary::setInternalDictionarySequenceMember(const HeapVector<InternalDictionary>& value) {
+  m_internalDictionarySequenceMember = value;
+  m_hasInternalDictionarySequenceMember = true;
+}
+bool TestDictionary::hasLongMember() const {
+  return m_hasLongMember;
+}
+int TestDictionary::longMember() const {
+  DCHECK(m_hasLongMember);
+  return m_longMember;
+}
+void TestDictionary::setLongMember(int value) {
+  m_longMember = value;
+  m_hasLongMember = true;
+}
+bool TestDictionary::hasObjectMember() const {
+  return !(m_objectMember.isEmpty() || m_objectMember.isNull() || m_objectMember.isUndefined());
+}
+ScriptValue TestDictionary::objectMember() const {
+  return m_objectMember;
+}
+void TestDictionary::setObjectMember(ScriptValue value) {
+  m_objectMember = value;
+}
+bool TestDictionary::hasObjectOrNullMember() const {
+  return !(m_objectOrNullMember.isEmpty() || m_objectOrNullMember.isNull() || m_objectOrNullMember.isUndefined());
+}
+ScriptValue TestDictionary::objectOrNullMember() const {
+  return m_objectOrNullMember;
+}
+void TestDictionary::setObjectOrNullMember(ScriptValue value) {
+  m_objectOrNullMember = value;
+}
+void TestDictionary::setObjectOrNullMemberToNull() {
+  m_objectOrNullMember = ScriptValue();
+}
+bool TestDictionary::hasOtherDoubleOrStringMember() const {
+  return !m_otherDoubleOrStringMember.isNull();
+}
+const DoubleOrString& TestDictionary::otherDoubleOrStringMember() const {
+  return m_otherDoubleOrStringMember;
+}
+void TestDictionary::setOtherDoubleOrStringMember(const DoubleOrString& value) {
+  m_otherDoubleOrStringMember = value;
+}
+bool TestDictionary::hasRestrictedDoubleMember() const {
+  return m_hasRestrictedDoubleMember;
+}
+double TestDictionary::restrictedDoubleMember() const {
+  DCHECK(m_hasRestrictedDoubleMember);
+  return m_restrictedDoubleMember;
+}
+void TestDictionary::setRestrictedDoubleMember(double value) {
+  m_restrictedDoubleMember = value;
+  m_hasRestrictedDoubleMember = true;
+}
+bool TestDictionary::hasRuntimeMember() const {
+  return m_hasRuntimeMember;
+}
+bool TestDictionary::runtimeMember() const {
+  DCHECK(m_hasRuntimeMember);
+  return m_runtimeMember;
+}
+void TestDictionary::setRuntimeMember(bool value) {
+  m_runtimeMember = value;
+  m_hasRuntimeMember = true;
+}
+bool TestDictionary::hasStringArrayMember() const {
+  return m_hasStringArrayMember;
+}
+const Vector<String>& TestDictionary::stringArrayMember() const {
+  DCHECK(m_hasStringArrayMember);
+  return m_stringArrayMember;
+}
+void TestDictionary::setStringArrayMember(const Vector<String>& value) {
+  m_stringArrayMember = value;
+  m_hasStringArrayMember = true;
+}
+bool TestDictionary::hasStringMember() const {
+  return !m_stringMember.isNull();
+}
+String TestDictionary::stringMember() const {
+  return m_stringMember;
+}
+void TestDictionary::setStringMember(String value) {
+  m_stringMember = value;
+}
+bool TestDictionary::hasStringOrNullMember() const {
+  return !m_stringOrNullMember.isNull();
+}
+String TestDictionary::stringOrNullMember() const {
+  return m_stringOrNullMember;
+}
+void TestDictionary::setStringOrNullMember(String value) {
+  m_stringOrNullMember = value;
+}
+void TestDictionary::setStringOrNullMemberToNull() {
+  m_stringOrNullMember = String();
+}
+bool TestDictionary::hasStringSequenceMember() const {
+  return m_hasStringSequenceMember;
+}
+const Vector<String>& TestDictionary::stringSequenceMember() const {
+  DCHECK(m_hasStringSequenceMember);
+  return m_stringSequenceMember;
+}
+void TestDictionary::setStringSequenceMember(const Vector<String>& value) {
+  m_stringSequenceMember = value;
+  m_hasStringSequenceMember = true;
+}
+bool TestDictionary::hasTestInterface2OrUint8ArrayMember() const {
+  return !m_testInterface2OrUint8ArrayMember.isNull();
+}
+const TestInterface2OrUint8Array& TestDictionary::testInterface2OrUint8ArrayMember() const {
+  return m_testInterface2OrUint8ArrayMember;
+}
+void TestDictionary::setTestInterface2OrUint8ArrayMember(const TestInterface2OrUint8Array& value) {
+  m_testInterface2OrUint8ArrayMember = value;
+}
+bool TestDictionary::hasTestInterfaceGarbageCollectedMember() const {
+  return m_testInterfaceGarbageCollectedMember;
+}
+TestInterfaceGarbageCollected* TestDictionary::testInterfaceGarbageCollectedMember() const {
+  return m_testInterfaceGarbageCollectedMember;
+}
+void TestDictionary::setTestInterfaceGarbageCollectedMember(TestInterfaceGarbageCollected* value) {
+  m_testInterfaceGarbageCollectedMember = value;
+}
+bool TestDictionary::hasTestInterfaceGarbageCollectedOrNullMember() const {
+  return m_testInterfaceGarbageCollectedOrNullMember;
+}
+TestInterfaceGarbageCollected* TestDictionary::testInterfaceGarbageCollectedOrNullMember() const {
+  return m_testInterfaceGarbageCollectedOrNullMember;
+}
+void TestDictionary::setTestInterfaceGarbageCollectedOrNullMember(TestInterfaceGarbageCollected* value) {
+  m_testInterfaceGarbageCollectedOrNullMember = value;
+}
+void TestDictionary::setTestInterfaceGarbageCollectedOrNullMemberToNull() {
+  m_testInterfaceGarbageCollectedOrNullMember = Member<TestInterfaceGarbageCollected>();
+}
+bool TestDictionary::hasTestInterfaceGarbageCollectedSequenceMember() const {
+  return m_hasTestInterfaceGarbageCollectedSequenceMember;
+}
+const HeapVector<Member<TestInterfaceGarbageCollected>>& TestDictionary::testInterfaceGarbageCollectedSequenceMember() const {
+  DCHECK(m_hasTestInterfaceGarbageCollectedSequenceMember);
+  return m_testInterfaceGarbageCollectedSequenceMember;
+}
+void TestDictionary::setTestInterfaceGarbageCollectedSequenceMember(const HeapVector<Member<TestInterfaceGarbageCollected>>& value) {
+  m_testInterfaceGarbageCollectedSequenceMember = value;
+  m_hasTestInterfaceGarbageCollectedSequenceMember = true;
+}
+bool TestDictionary::hasTestInterfaceMember() const {
+  return m_testInterfaceMember;
+}
+TestInterfaceImplementation* TestDictionary::testInterfaceMember() const {
+  return m_testInterfaceMember;
+}
+void TestDictionary::setTestInterfaceMember(TestInterfaceImplementation* value) {
+  m_testInterfaceMember = value;
+}
+bool TestDictionary::hasTestInterfaceOrNullMember() const {
+  return m_testInterfaceOrNullMember;
+}
+TestInterfaceImplementation* TestDictionary::testInterfaceOrNullMember() const {
+  return m_testInterfaceOrNullMember;
+}
+void TestDictionary::setTestInterfaceOrNullMember(TestInterfaceImplementation* value) {
+  m_testInterfaceOrNullMember = value;
+}
+void TestDictionary::setTestInterfaceOrNullMemberToNull() {
+  m_testInterfaceOrNullMember = Member<TestInterfaceImplementation>();
+}
+bool TestDictionary::hasTestInterfaceSequenceMember() const {
+  return m_hasTestInterfaceSequenceMember;
+}
+const HeapVector<Member<TestInterfaceImplementation>>& TestDictionary::testInterfaceSequenceMember() const {
+  DCHECK(m_hasTestInterfaceSequenceMember);
+  return m_testInterfaceSequenceMember;
+}
+void TestDictionary::setTestInterfaceSequenceMember(const HeapVector<Member<TestInterfaceImplementation>>& value) {
+  m_testInterfaceSequenceMember = value;
+  m_hasTestInterfaceSequenceMember = true;
+}
+bool TestDictionary::hasUint8ArrayMember() const {
+  return m_uint8ArrayMember;
+}
+DOMUint8Array* TestDictionary::uint8ArrayMember() const {
+  return m_uint8ArrayMember;
+}
+void TestDictionary::setUint8ArrayMember(DOMUint8Array* value) {
+  m_uint8ArrayMember = value;
+}
+bool TestDictionary::hasUnrestrictedDoubleMember() const {
+  return m_hasUnrestrictedDoubleMember;
+}
+double TestDictionary::unrestrictedDoubleMember() const {
+  DCHECK(m_hasUnrestrictedDoubleMember);
+  return m_unrestrictedDoubleMember;
+}
+void TestDictionary::setUnrestrictedDoubleMember(double value) {
+  m_unrestrictedDoubleMember = value;
+  m_hasUnrestrictedDoubleMember = true;
+}
+
 DEFINE_TRACE(TestDictionary) {
   visitor->trace(m_doubleOrStringMember);
   visitor->trace(m_doubleOrStringSequenceMember);
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h
index aa2ef254..a19e0b9f 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h
@@ -12,7 +12,6 @@
 #include "bindings/core/v8/Dictionary.h"
 #include "bindings/core/v8/DoubleOrString.h"
 #include "bindings/core/v8/IDLDictionaryBase.h"
-#include "bindings/core/v8/Nullable.h"
 #include "bindings/core/v8/ScriptValue.h"
 #include "bindings/core/v8/TestInterface2OrUint8Array.h"
 #include "bindings/tests/idls/core/TestInterface2.h"
@@ -35,171 +34,185 @@
   TestDictionary();
   virtual ~TestDictionary();
 
-  bool hasAnyMember() const { return !(m_anyMember.isEmpty() || m_anyMember.isNull() || m_anyMember.isUndefined()); }
-  ScriptValue anyMember() const { return m_anyMember; }
-  void setAnyMember(ScriptValue value) { m_anyMember = value; }
+  bool hasAnyMember() const;
+  ScriptValue anyMember() const;
+  void setAnyMember(ScriptValue);
 
-  bool hasBooleanMember() const { return !m_booleanMember.isNull(); }
-  bool booleanMember() const { return m_booleanMember.get(); }
-  void setBooleanMember(bool value) { m_booleanMember = value; }
+  bool hasBooleanMember() const;
+  bool booleanMember() const;
+  void setBooleanMember(bool);
 
-  bool hasCreateMember() const { return !m_createMember.isNull(); }
-  bool createMember() const { return m_createMember.get(); }
-  void setCreateMember(bool value) { m_createMember = value; }
+  bool hasCreateMember() const;
+  bool createMember() const;
+  void setCreateMember(bool);
 
-  bool hasDictionaryMember() const { return !m_dictionaryMember.isUndefinedOrNull(); }
-  Dictionary dictionaryMember() const { return m_dictionaryMember; }
-  void setDictionaryMember(Dictionary value) { m_dictionaryMember = value; }
+  bool hasDictionaryMember() const;
+  Dictionary dictionaryMember() const;
+  void setDictionaryMember(Dictionary);
 
-  bool hasDoubleOrNullMember() const { return !m_doubleOrNullMember.isNull(); }
-  double doubleOrNullMember() const { return m_doubleOrNullMember.get(); }
-  void setDoubleOrNullMember(double value) { m_doubleOrNullMember = value; }
-  void setDoubleOrNullMemberToNull() { m_doubleOrNullMember = Nullable<double>(); }
+  bool hasDoubleOrNullMember() const;
+  double doubleOrNullMember() const;
+  void setDoubleOrNullMember(double);
+  void setDoubleOrNullMemberToNull();
 
-  bool hasDoubleOrStringMember() const { return !m_doubleOrStringMember.isNull(); }
-  const DoubleOrString& doubleOrStringMember() const { return m_doubleOrStringMember; }
-  void setDoubleOrStringMember(const DoubleOrString& value) { m_doubleOrStringMember = value; }
+  bool hasDoubleOrStringMember() const;
+  const DoubleOrString& doubleOrStringMember() const;
+  void setDoubleOrStringMember(const DoubleOrString&);
 
-  bool hasDoubleOrStringSequenceMember() const { return !m_doubleOrStringSequenceMember.isNull(); }
-  const HeapVector<DoubleOrString>& doubleOrStringSequenceMember() const { return m_doubleOrStringSequenceMember.get(); }
-  void setDoubleOrStringSequenceMember(const HeapVector<DoubleOrString>& value) { m_doubleOrStringSequenceMember = value; }
+  bool hasDoubleOrStringSequenceMember() const;
+  const HeapVector<DoubleOrString>& doubleOrStringSequenceMember() const;
+  void setDoubleOrStringSequenceMember(const HeapVector<DoubleOrString>&);
 
-  bool hasElementOrNullMember() const { return m_elementOrNullMember; }
-  Element* elementOrNullMember() const { return m_elementOrNullMember; }
-  void setElementOrNullMember(Element* value) { m_elementOrNullMember = value; }
-  void setElementOrNullMemberToNull() { m_elementOrNullMember = Member<Element>(); }
+  bool hasElementOrNullMember() const;
+  Element* elementOrNullMember() const;
+  void setElementOrNullMember(Element*);
+  void setElementOrNullMemberToNull();
 
-  bool hasEnumMember() const { return !m_enumMember.isNull(); }
-  String enumMember() const { return m_enumMember; }
-  void setEnumMember(String value) { m_enumMember = value; }
+  bool hasEnumMember() const;
+  String enumMember() const;
+  void setEnumMember(String);
 
-  bool hasEnumSequenceMember() const { return !m_enumSequenceMember.isNull(); }
-  const Vector<String>& enumSequenceMember() const { return m_enumSequenceMember.get(); }
-  void setEnumSequenceMember(const Vector<String>& value) { m_enumSequenceMember = value; }
+  bool hasEnumSequenceMember() const;
+  const Vector<String>& enumSequenceMember() const;
+  void setEnumSequenceMember(const Vector<String>&);
 
-  bool hasEventTargetMember() const { return m_eventTargetMember; }
-  EventTarget* eventTargetMember() const { return m_eventTargetMember; }
-  void setEventTargetMember(EventTarget* value) { m_eventTargetMember = value; }
+  bool hasEventTargetMember() const;
+  EventTarget* eventTargetMember() const;
+  void setEventTargetMember(EventTarget*);
 
-  bool hasInternalDictionarySequenceMember() const { return !m_internalDictionarySequenceMember.isNull(); }
-  const HeapVector<InternalDictionary>& internalDictionarySequenceMember() const { return m_internalDictionarySequenceMember.get(); }
-  void setInternalDictionarySequenceMember(const HeapVector<InternalDictionary>& value) { m_internalDictionarySequenceMember = value; }
+  bool hasInternalDictionarySequenceMember() const;
+  const HeapVector<InternalDictionary>& internalDictionarySequenceMember() const;
+  void setInternalDictionarySequenceMember(const HeapVector<InternalDictionary>&);
 
-  bool hasLongMember() const { return !m_longMember.isNull(); }
-  int longMember() const { return m_longMember.get(); }
-  void setLongMember(int value) { m_longMember = value; }
+  bool hasLongMember() const;
+  int longMember() const;
+  void setLongMember(int);
 
-  bool hasObjectMember() const { return !(m_objectMember.isEmpty() || m_objectMember.isNull() || m_objectMember.isUndefined()); }
-  ScriptValue objectMember() const { return m_objectMember; }
-  void setObjectMember(ScriptValue value) { m_objectMember = value; }
+  bool hasObjectMember() const;
+  ScriptValue objectMember() const;
+  void setObjectMember(ScriptValue);
 
-  bool hasObjectOrNullMember() const { return !(m_objectOrNullMember.isEmpty() || m_objectOrNullMember.isNull() || m_objectOrNullMember.isUndefined()); }
-  ScriptValue objectOrNullMember() const { return m_objectOrNullMember; }
-  void setObjectOrNullMember(ScriptValue value) { m_objectOrNullMember = value; }
-  void setObjectOrNullMemberToNull() { m_objectOrNullMember = ScriptValue(); }
+  bool hasObjectOrNullMember() const;
+  ScriptValue objectOrNullMember() const;
+  void setObjectOrNullMember(ScriptValue);
+  void setObjectOrNullMemberToNull();
 
-  bool hasOtherDoubleOrStringMember() const { return !m_otherDoubleOrStringMember.isNull(); }
-  const DoubleOrString& otherDoubleOrStringMember() const { return m_otherDoubleOrStringMember; }
-  void setOtherDoubleOrStringMember(const DoubleOrString& value) { m_otherDoubleOrStringMember = value; }
+  bool hasOtherDoubleOrStringMember() const;
+  const DoubleOrString& otherDoubleOrStringMember() const;
+  void setOtherDoubleOrStringMember(const DoubleOrString&);
 
-  bool hasRestrictedDoubleMember() const { return !m_restrictedDoubleMember.isNull(); }
-  double restrictedDoubleMember() const { return m_restrictedDoubleMember.get(); }
-  void setRestrictedDoubleMember(double value) { m_restrictedDoubleMember = value; }
+  bool hasRestrictedDoubleMember() const;
+  double restrictedDoubleMember() const;
+  void setRestrictedDoubleMember(double);
 
-  bool hasRuntimeMember() const { return !m_runtimeMember.isNull(); }
-  bool runtimeMember() const { return m_runtimeMember.get(); }
-  void setRuntimeMember(bool value) { m_runtimeMember = value; }
+  bool hasRuntimeMember() const;
+  bool runtimeMember() const;
+  void setRuntimeMember(bool);
 
-  bool hasStringArrayMember() const { return !m_stringArrayMember.isNull(); }
-  const Vector<String>& stringArrayMember() const { return m_stringArrayMember.get(); }
-  void setStringArrayMember(const Vector<String>& value) { m_stringArrayMember = value; }
+  bool hasStringArrayMember() const;
+  const Vector<String>& stringArrayMember() const;
+  void setStringArrayMember(const Vector<String>&);
 
-  bool hasStringMember() const { return !m_stringMember.isNull(); }
-  String stringMember() const { return m_stringMember; }
-  void setStringMember(String value) { m_stringMember = value; }
+  bool hasStringMember() const;
+  String stringMember() const;
+  void setStringMember(String);
 
-  bool hasStringOrNullMember() const { return !m_stringOrNullMember.isNull(); }
-  String stringOrNullMember() const { return m_stringOrNullMember; }
-  void setStringOrNullMember(String value) { m_stringOrNullMember = value; }
-  void setStringOrNullMemberToNull() { m_stringOrNullMember = String(); }
+  bool hasStringOrNullMember() const;
+  String stringOrNullMember() const;
+  void setStringOrNullMember(String);
+  void setStringOrNullMemberToNull();
 
-  bool hasStringSequenceMember() const { return !m_stringSequenceMember.isNull(); }
-  const Vector<String>& stringSequenceMember() const { return m_stringSequenceMember.get(); }
-  void setStringSequenceMember(const Vector<String>& value) { m_stringSequenceMember = value; }
+  bool hasStringSequenceMember() const;
+  const Vector<String>& stringSequenceMember() const;
+  void setStringSequenceMember(const Vector<String>&);
 
-  bool hasTestInterface2OrUint8ArrayMember() const { return !m_testInterface2OrUint8ArrayMember.isNull(); }
-  const TestInterface2OrUint8Array& testInterface2OrUint8ArrayMember() const { return m_testInterface2OrUint8ArrayMember; }
-  void setTestInterface2OrUint8ArrayMember(const TestInterface2OrUint8Array& value) { m_testInterface2OrUint8ArrayMember = value; }
+  bool hasTestInterface2OrUint8ArrayMember() const;
+  const TestInterface2OrUint8Array& testInterface2OrUint8ArrayMember() const;
+  void setTestInterface2OrUint8ArrayMember(const TestInterface2OrUint8Array&);
 
-  bool hasTestInterfaceGarbageCollectedMember() const { return m_testInterfaceGarbageCollectedMember; }
-  TestInterfaceGarbageCollected* testInterfaceGarbageCollectedMember() const { return m_testInterfaceGarbageCollectedMember; }
-  void setTestInterfaceGarbageCollectedMember(TestInterfaceGarbageCollected* value) { m_testInterfaceGarbageCollectedMember = value; }
+  bool hasTestInterfaceGarbageCollectedMember() const;
+  TestInterfaceGarbageCollected* testInterfaceGarbageCollectedMember() const;
+  void setTestInterfaceGarbageCollectedMember(TestInterfaceGarbageCollected*);
 
-  bool hasTestInterfaceGarbageCollectedOrNullMember() const { return m_testInterfaceGarbageCollectedOrNullMember; }
-  TestInterfaceGarbageCollected* testInterfaceGarbageCollectedOrNullMember() const { return m_testInterfaceGarbageCollectedOrNullMember; }
-  void setTestInterfaceGarbageCollectedOrNullMember(TestInterfaceGarbageCollected* value) { m_testInterfaceGarbageCollectedOrNullMember = value; }
-  void setTestInterfaceGarbageCollectedOrNullMemberToNull() { m_testInterfaceGarbageCollectedOrNullMember = Member<TestInterfaceGarbageCollected>(); }
+  bool hasTestInterfaceGarbageCollectedOrNullMember() const;
+  TestInterfaceGarbageCollected* testInterfaceGarbageCollectedOrNullMember() const;
+  void setTestInterfaceGarbageCollectedOrNullMember(TestInterfaceGarbageCollected*);
+  void setTestInterfaceGarbageCollectedOrNullMemberToNull();
 
-  bool hasTestInterfaceGarbageCollectedSequenceMember() const { return !m_testInterfaceGarbageCollectedSequenceMember.isNull(); }
-  const HeapVector<Member<TestInterfaceGarbageCollected>>& testInterfaceGarbageCollectedSequenceMember() const { return m_testInterfaceGarbageCollectedSequenceMember.get(); }
-  void setTestInterfaceGarbageCollectedSequenceMember(const HeapVector<Member<TestInterfaceGarbageCollected>>& value) { m_testInterfaceGarbageCollectedSequenceMember = value; }
+  bool hasTestInterfaceGarbageCollectedSequenceMember() const;
+  const HeapVector<Member<TestInterfaceGarbageCollected>>& testInterfaceGarbageCollectedSequenceMember() const;
+  void setTestInterfaceGarbageCollectedSequenceMember(const HeapVector<Member<TestInterfaceGarbageCollected>>&);
 
-  bool hasTestInterfaceMember() const { return m_testInterfaceMember; }
-  TestInterfaceImplementation* testInterfaceMember() const { return m_testInterfaceMember; }
-  void setTestInterfaceMember(TestInterfaceImplementation* value) { m_testInterfaceMember = value; }
+  bool hasTestInterfaceMember() const;
+  TestInterfaceImplementation* testInterfaceMember() const;
+  void setTestInterfaceMember(TestInterfaceImplementation*);
 
-  bool hasTestInterfaceOrNullMember() const { return m_testInterfaceOrNullMember; }
-  TestInterfaceImplementation* testInterfaceOrNullMember() const { return m_testInterfaceOrNullMember; }
-  void setTestInterfaceOrNullMember(TestInterfaceImplementation* value) { m_testInterfaceOrNullMember = value; }
-  void setTestInterfaceOrNullMemberToNull() { m_testInterfaceOrNullMember = Member<TestInterfaceImplementation>(); }
+  bool hasTestInterfaceOrNullMember() const;
+  TestInterfaceImplementation* testInterfaceOrNullMember() const;
+  void setTestInterfaceOrNullMember(TestInterfaceImplementation*);
+  void setTestInterfaceOrNullMemberToNull();
 
-  bool hasTestInterfaceSequenceMember() const { return !m_testInterfaceSequenceMember.isNull(); }
-  const HeapVector<Member<TestInterfaceImplementation>>& testInterfaceSequenceMember() const { return m_testInterfaceSequenceMember.get(); }
-  void setTestInterfaceSequenceMember(const HeapVector<Member<TestInterfaceImplementation>>& value) { m_testInterfaceSequenceMember = value; }
+  bool hasTestInterfaceSequenceMember() const;
+  const HeapVector<Member<TestInterfaceImplementation>>& testInterfaceSequenceMember() const;
+  void setTestInterfaceSequenceMember(const HeapVector<Member<TestInterfaceImplementation>>&);
 
-  bool hasUint8ArrayMember() const { return m_uint8ArrayMember; }
-  DOMUint8Array* uint8ArrayMember() const { return m_uint8ArrayMember; }
-  void setUint8ArrayMember(DOMUint8Array* value) { m_uint8ArrayMember = value; }
+  bool hasUint8ArrayMember() const;
+  DOMUint8Array* uint8ArrayMember() const;
+  void setUint8ArrayMember(DOMUint8Array*);
 
-  bool hasUnrestrictedDoubleMember() const { return !m_unrestrictedDoubleMember.isNull(); }
-  double unrestrictedDoubleMember() const { return m_unrestrictedDoubleMember.get(); }
-  void setUnrestrictedDoubleMember(double value) { m_unrestrictedDoubleMember = value; }
+  bool hasUnrestrictedDoubleMember() const;
+  double unrestrictedDoubleMember() const;
+  void setUnrestrictedDoubleMember(double);
 
   v8::Local<v8::Value> toV8Impl(v8::Local<v8::Object>, v8::Isolate*) const override;
   DECLARE_VIRTUAL_TRACE();
 
  private:
   ScriptValue m_anyMember;
-  Nullable<bool> m_booleanMember;
-  Nullable<bool> m_createMember;
+  bool m_hasBooleanMember = false;
+  bool m_booleanMember;
+  bool m_hasCreateMember = false;
+  bool m_createMember;
   Dictionary m_dictionaryMember;
-  Nullable<double> m_doubleOrNullMember;
+  bool m_hasDoubleOrNullMember = false;
+  double m_doubleOrNullMember;
   DoubleOrString m_doubleOrStringMember;
-  Nullable<HeapVector<DoubleOrString>> m_doubleOrStringSequenceMember;
+  bool m_hasDoubleOrStringSequenceMember = false;
+  HeapVector<DoubleOrString> m_doubleOrStringSequenceMember;
   Member<Element> m_elementOrNullMember;
   String m_enumMember;
-  Nullable<Vector<String>> m_enumSequenceMember;
+  bool m_hasEnumSequenceMember = false;
+  Vector<String> m_enumSequenceMember;
   Member<EventTarget> m_eventTargetMember;
-  Nullable<HeapVector<InternalDictionary>> m_internalDictionarySequenceMember;
-  Nullable<int> m_longMember;
+  bool m_hasInternalDictionarySequenceMember = false;
+  HeapVector<InternalDictionary> m_internalDictionarySequenceMember;
+  bool m_hasLongMember = false;
+  int m_longMember;
   ScriptValue m_objectMember;
   ScriptValue m_objectOrNullMember;
   DoubleOrString m_otherDoubleOrStringMember;
-  Nullable<double> m_restrictedDoubleMember;
-  Nullable<bool> m_runtimeMember;
-  Nullable<Vector<String>> m_stringArrayMember;
+  bool m_hasRestrictedDoubleMember = false;
+  double m_restrictedDoubleMember;
+  bool m_hasRuntimeMember = false;
+  bool m_runtimeMember;
+  bool m_hasStringArrayMember = false;
+  Vector<String> m_stringArrayMember;
   String m_stringMember;
   String m_stringOrNullMember;
-  Nullable<Vector<String>> m_stringSequenceMember;
+  bool m_hasStringSequenceMember = false;
+  Vector<String> m_stringSequenceMember;
   TestInterface2OrUint8Array m_testInterface2OrUint8ArrayMember;
   Member<TestInterfaceGarbageCollected> m_testInterfaceGarbageCollectedMember;
   Member<TestInterfaceGarbageCollected> m_testInterfaceGarbageCollectedOrNullMember;
-  Nullable<HeapVector<Member<TestInterfaceGarbageCollected>>> m_testInterfaceGarbageCollectedSequenceMember;
+  bool m_hasTestInterfaceGarbageCollectedSequenceMember = false;
+  HeapVector<Member<TestInterfaceGarbageCollected>> m_testInterfaceGarbageCollectedSequenceMember;
   Member<TestInterfaceImplementation> m_testInterfaceMember;
   Member<TestInterfaceImplementation> m_testInterfaceOrNullMember;
-  Nullable<HeapVector<Member<TestInterfaceImplementation>>> m_testInterfaceSequenceMember;
+  bool m_hasTestInterfaceSequenceMember = false;
+  HeapVector<Member<TestInterfaceImplementation>> m_testInterfaceSequenceMember;
   Member<DOMUint8Array> m_uint8ArrayMember;
-  Nullable<double> m_unrestrictedDoubleMember;
+  bool m_hasUnrestrictedDoubleMember = false;
+  double m_unrestrictedDoubleMember;
 
   friend class V8TestDictionary;
 };
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionaryDerivedImplementedAs.cpp b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionaryDerivedImplementedAs.cpp
index 5e93ab9..599fdfe 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionaryDerivedImplementedAs.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionaryDerivedImplementedAs.cpp
@@ -16,6 +16,47 @@
 
 TestDictionaryDerivedImplementedAs::~TestDictionaryDerivedImplementedAs() {}
 
+bool TestDictionaryDerivedImplementedAs::hasDerivedStringMember() const {
+  return !m_derivedStringMember.isNull();
+}
+String TestDictionaryDerivedImplementedAs::derivedStringMember() const {
+  return m_derivedStringMember;
+}
+void TestDictionaryDerivedImplementedAs::setDerivedStringMember(String value) {
+  m_derivedStringMember = value;
+}
+bool TestDictionaryDerivedImplementedAs::hasDerivedStringMemberWithDefault() const {
+  return !m_derivedStringMemberWithDefault.isNull();
+}
+String TestDictionaryDerivedImplementedAs::derivedStringMemberWithDefault() const {
+  return m_derivedStringMemberWithDefault;
+}
+void TestDictionaryDerivedImplementedAs::setDerivedStringMemberWithDefault(String value) {
+  m_derivedStringMemberWithDefault = value;
+}
+bool TestDictionaryDerivedImplementedAs::hasRequiredLongMember() const {
+  return m_hasRequiredLongMember;
+}
+int TestDictionaryDerivedImplementedAs::requiredLongMember() const {
+  DCHECK(m_hasRequiredLongMember);
+  return m_requiredLongMember;
+}
+void TestDictionaryDerivedImplementedAs::setRequiredLongMember(int value) {
+  m_requiredLongMember = value;
+  m_hasRequiredLongMember = true;
+}
+bool TestDictionaryDerivedImplementedAs::hasStringOrDoubleSequenceMember() const {
+  return m_hasStringOrDoubleSequenceMember;
+}
+const HeapVector<StringOrDouble>& TestDictionaryDerivedImplementedAs::stringOrDoubleSequenceMember() const {
+  DCHECK(m_hasStringOrDoubleSequenceMember);
+  return m_stringOrDoubleSequenceMember;
+}
+void TestDictionaryDerivedImplementedAs::setStringOrDoubleSequenceMember(const HeapVector<StringOrDouble>& value) {
+  m_stringOrDoubleSequenceMember = value;
+  m_hasStringOrDoubleSequenceMember = true;
+}
+
 DEFINE_TRACE(TestDictionaryDerivedImplementedAs) {
   visitor->trace(m_stringOrDoubleSequenceMember);
   TestDictionary::trace(visitor);
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionaryDerivedImplementedAs.h b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionaryDerivedImplementedAs.h
index c3224b5..d461a05 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionaryDerivedImplementedAs.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionaryDerivedImplementedAs.h
@@ -9,7 +9,6 @@
 #ifndef TestDictionaryDerivedImplementedAs_h
 #define TestDictionaryDerivedImplementedAs_h
 
-#include "bindings/core/v8/Nullable.h"
 #include "bindings/core/v8/StringOrDouble.h"
 #include "bindings/tests/idls/core/TestDictionary.h"
 #include "core/CoreExport.h"
@@ -25,21 +24,21 @@
   TestDictionaryDerivedImplementedAs();
   virtual ~TestDictionaryDerivedImplementedAs();
 
-  bool hasDerivedStringMember() const { return !m_derivedStringMember.isNull(); }
-  String derivedStringMember() const { return m_derivedStringMember; }
-  void setDerivedStringMember(String value) { m_derivedStringMember = value; }
+  bool hasDerivedStringMember() const;
+  String derivedStringMember() const;
+  void setDerivedStringMember(String);
 
-  bool hasDerivedStringMemberWithDefault() const { return !m_derivedStringMemberWithDefault.isNull(); }
-  String derivedStringMemberWithDefault() const { return m_derivedStringMemberWithDefault; }
-  void setDerivedStringMemberWithDefault(String value) { m_derivedStringMemberWithDefault = value; }
+  bool hasDerivedStringMemberWithDefault() const;
+  String derivedStringMemberWithDefault() const;
+  void setDerivedStringMemberWithDefault(String);
 
-  bool hasRequiredLongMember() const { return !m_requiredLongMember.isNull(); }
-  int requiredLongMember() const { return m_requiredLongMember.get(); }
-  void setRequiredLongMember(int value) { m_requiredLongMember = value; }
+  bool hasRequiredLongMember() const;
+  int requiredLongMember() const;
+  void setRequiredLongMember(int);
 
-  bool hasStringOrDoubleSequenceMember() const { return !m_stringOrDoubleSequenceMember.isNull(); }
-  const HeapVector<StringOrDouble>& stringOrDoubleSequenceMember() const { return m_stringOrDoubleSequenceMember.get(); }
-  void setStringOrDoubleSequenceMember(const HeapVector<StringOrDouble>& value) { m_stringOrDoubleSequenceMember = value; }
+  bool hasStringOrDoubleSequenceMember() const;
+  const HeapVector<StringOrDouble>& stringOrDoubleSequenceMember() const;
+  void setStringOrDoubleSequenceMember(const HeapVector<StringOrDouble>&);
 
   v8::Local<v8::Value> toV8Impl(v8::Local<v8::Object>, v8::Isolate*) const override;
   DECLARE_VIRTUAL_TRACE();
@@ -47,8 +46,10 @@
  private:
   String m_derivedStringMember;
   String m_derivedStringMemberWithDefault;
-  Nullable<int> m_requiredLongMember;
-  Nullable<HeapVector<StringOrDouble>> m_stringOrDoubleSequenceMember;
+  bool m_hasRequiredLongMember = false;
+  int m_requiredLongMember;
+  bool m_hasStringOrDoubleSequenceMember = false;
+  HeapVector<StringOrDouble> m_stringOrDoubleSequenceMember;
 
   friend class V8TestDictionaryDerivedImplementedAs;
 };
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestInterfaceEventInit.cpp b/third_party/WebKit/Source/bindings/tests/results/core/TestInterfaceEventInit.cpp
index f1782af4..8a48d9f 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/TestInterfaceEventInit.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/TestInterfaceEventInit.cpp
@@ -15,6 +15,16 @@
 
 TestInterfaceEventInit::~TestInterfaceEventInit() {}
 
+bool TestInterfaceEventInit::hasStringMember() const {
+  return !m_stringMember.isNull();
+}
+String TestInterfaceEventInit::stringMember() const {
+  return m_stringMember;
+}
+void TestInterfaceEventInit::setStringMember(String value) {
+  m_stringMember = value;
+}
+
 DEFINE_TRACE(TestInterfaceEventInit) {
   EventInit::trace(visitor);
 }
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestInterfaceEventInit.h b/third_party/WebKit/Source/bindings/tests/results/core/TestInterfaceEventInit.h
index 73eb7f3..4669296 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/TestInterfaceEventInit.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/TestInterfaceEventInit.h
@@ -22,9 +22,9 @@
   TestInterfaceEventInit();
   virtual ~TestInterfaceEventInit();
 
-  bool hasStringMember() const { return !m_stringMember.isNull(); }
-  String stringMember() const { return m_stringMember; }
-  void setStringMember(String value) { m_stringMember = value; }
+  bool hasStringMember() const;
+  String stringMember() const;
+  void setStringMember(String);
 
   v8::Local<v8::Value> toV8Impl(v8::Local<v8::Object>, v8::Isolate*) const override;
   DECLARE_VIRTUAL_TRACE();
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestPermissiveDictionary.cpp b/third_party/WebKit/Source/bindings/tests/results/core/TestPermissiveDictionary.cpp
index 3801ef2..3dc392a3 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/TestPermissiveDictionary.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/TestPermissiveDictionary.cpp
@@ -15,6 +15,18 @@
 
 TestPermissiveDictionary::~TestPermissiveDictionary() {}
 
+bool TestPermissiveDictionary::hasBooleanMember() const {
+  return m_hasBooleanMember;
+}
+bool TestPermissiveDictionary::booleanMember() const {
+  DCHECK(m_hasBooleanMember);
+  return m_booleanMember;
+}
+void TestPermissiveDictionary::setBooleanMember(bool value) {
+  m_booleanMember = value;
+  m_hasBooleanMember = true;
+}
+
 DEFINE_TRACE(TestPermissiveDictionary) {
   IDLDictionaryBase::trace(visitor);
 }
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestPermissiveDictionary.h b/third_party/WebKit/Source/bindings/tests/results/core/TestPermissiveDictionary.h
index cc9d80f..75f484d2 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/TestPermissiveDictionary.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/TestPermissiveDictionary.h
@@ -10,7 +10,6 @@
 #define TestPermissiveDictionary_h
 
 #include "bindings/core/v8/IDLDictionaryBase.h"
-#include "bindings/core/v8/Nullable.h"
 #include "core/CoreExport.h"
 #include "platform/heap/Handle.h"
 
@@ -22,15 +21,16 @@
   TestPermissiveDictionary();
   virtual ~TestPermissiveDictionary();
 
-  bool hasBooleanMember() const { return !m_booleanMember.isNull(); }
-  bool booleanMember() const { return m_booleanMember.get(); }
-  void setBooleanMember(bool value) { m_booleanMember = value; }
+  bool hasBooleanMember() const;
+  bool booleanMember() const;
+  void setBooleanMember(bool);
 
   v8::Local<v8::Value> toV8Impl(v8::Local<v8::Object>, v8::Isolate*) const override;
   DECLARE_VIRTUAL_TRACE();
 
  private:
-  Nullable<bool> m_booleanMember;
+  bool m_hasBooleanMember = false;
+  bool m_booleanMember;
 
   friend class V8TestPermissiveDictionary;
 };
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/WebTestInterface3.cpp b/third_party/WebKit/Source/bindings/tests/results/core/WebTestInterface3.cpp
new file mode 100644
index 0000000..b0961f6
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/tests/results/core/WebTestInterface3.cpp
@@ -0,0 +1,34 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file has been auto-generated by code_generator_web_module.py.
+// DO NOT MODIFY!
+
+// clang-format off
+// clang-format on
+
+#include "WebTestInterface3.h"
+
+// TODO(dglazkov): Implement generating includes.
+
+namespace blink {
+namespace api {
+
+// TODO(dglazkov): Implement constant generation
+
+// TODO(dglazkov): Implement constructor generation
+
+// TODO(dglazkov): Implement attribute getter/setter generation
+// readonlyStringifierAttribute
+
+// TODO(dglazkov): Implement method generation
+// void CppTestInterface3::voidMethodDocument
+// Iterator* CppTestInterface3::keys
+// Iterator* CppTestInterface3::values
+// Iterator* CppTestInterface3::entries
+// void CppTestInterface3::forEach
+// String CppTestInterface3::toString
+
+}  // namespace api
+}  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/WebTestInterface3.h b/third_party/WebKit/Source/bindings/tests/results/core/WebTestInterface3.h
new file mode 100644
index 0000000..6a6dd10
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/tests/results/core/WebTestInterface3.h
@@ -0,0 +1 @@
+header
\ No newline at end of file
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn
index fdb8d0b..cb5304a 100644
--- a/third_party/WebKit/Source/core/BUILD.gn
+++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -1281,9 +1281,11 @@
     "paint/PaintLayerTest.cpp",
     "paint/PaintPropertyTreeBuilderTest.cpp",
     "paint/PaintPropertyTreePrinterTest.cpp",
+    "paint/PrePaintTreeWalkTest.cpp",
     "paint/SVGInlineTextBoxPainterTest.cpp",
     "paint/StubChromeClientForSPv2.h",
     "paint/TableCellPainterTest.cpp",
+    "paint/TablePainterTest.cpp",
     "paint/TextPainterTest.cpp",
     "paint/VideoPainterTest.cpp",
     "streams/ReadableStreamOperationsTest.cpp",
diff --git a/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp b/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp
index 03392eb..6728de8 100644
--- a/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp
+++ b/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp
@@ -844,6 +844,13 @@
   clearPendingUpdate();
 }
 
+// TODO(alancutter): CSS properties and presentation attributes may have
+// identical effects. By grouping them in the same set we introduce a bug where
+// arbitrary hash iteration will determine the order the apply in and thus which
+// one "wins". We should be more deliberate about the order of application in
+// the case of effect collisions.
+// Example: Both 'color' and 'svg-color' set the color on ComputedStyle but are
+// considered distinct properties in the ActiveInterpolationsMap.
 static bool isStylePropertyHandle(const PropertyHandle& propertyHandle) {
   return propertyHandle.isCSSProperty() ||
          propertyHandle.isPresentationAttribute();
diff --git a/third_party/WebKit/Source/core/css/CSSProperties.in b/third_party/WebKit/Source/core/css/CSSProperties.in
index 456e351..6effee2 100644
--- a/third_party/WebKit/Source/core/css/CSSProperties.in
+++ b/third_party/WebKit/Source/core/css/CSSProperties.in
@@ -117,6 +117,20 @@
 
 // Properties with StyleBuilder handling
 
+// Animation Priority properties
+animation-delay custom_all
+animation-direction custom_all
+animation-duration custom_all
+animation-fill-mode custom_all
+animation-iteration-count keywords=[infinite], supports_multiple, custom_all
+animation-name custom_all
+animation-play-state custom_all
+animation-timing-function custom_all
+transition-delay custom_all
+transition-duration custom_all
+transition-property custom_all
+transition-timing-function custom_all
+
 // High Priority and all other font properties.
 // Other properties can depend upon high priority properties (e.g. font-size / ems)
 color interpolable, inherited, custom_all
@@ -145,14 +159,6 @@
 align-items initial=initialDefaultAlignment, converter=convertSelfOrDefaultAlignmentData
 alignment-baseline svg
 align-self initial=initialSelfAlignment, converter=convertSelfOrDefaultAlignmentData
-animation-delay custom_all
-animation-direction custom_all
-animation-duration custom_all
-animation-fill-mode custom_all
-animation-iteration-count keywords=[infinite], supports_multiple, custom_all
-animation-name custom_all
-animation-play-state custom_all
-animation-timing-function custom_all
 backdrop-filter interpolable, converter=convertFilterOperations, runtime_flag=CSSBackdropFilter
 backface-visibility
 background-attachment custom_all
@@ -356,10 +362,6 @@
 translate runtime_flag=CSSIndependentTransformProperties, converter=convertTranslate, interpolable
 rotate runtime_flag=CSSIndependentTransformProperties, converter=convertRotate, interpolable
 scale runtime_flag=CSSIndependentTransformProperties, converter=convertScale, interpolable
-transition-delay custom_all
-transition-duration custom_all
-transition-property custom_all
-transition-timing-function custom_all
 unicode-bidi
 vector-effect svg
 vertical-align interpolable, custom_inherit, custom_value
diff --git a/third_party/WebKit/Source/core/css/resolver/CSSPropertyPriority.h b/third_party/WebKit/Source/core/css/resolver/CSSPropertyPriority.h
index ed6b7f4..fc511fd 100644
--- a/third_party/WebKit/Source/core/css/resolver/CSSPropertyPriority.h
+++ b/third_party/WebKit/Source/core/css/resolver/CSSPropertyPriority.h
@@ -16,6 +16,7 @@
 
 enum CSSPropertyPriority {
   ResolveVariables = 0,
+  AnimationPropertyPriority,
   HighPropertyPriority,
   LowPropertyPriority,
   PropertyPriorityCount,
@@ -47,8 +48,31 @@
 }
 
 template <>
+inline CSSPropertyID
+CSSPropertyPriorityData<AnimationPropertyPriority>::first() {
+  static_assert(CSSPropertyAnimationDelay == firstCSSProperty,
+                "CSSPropertyAnimationDelay should be the first animation "
+                "priority property");
+  return CSSPropertyAnimationDelay;
+}
+
+template <>
+inline CSSPropertyID
+CSSPropertyPriorityData<AnimationPropertyPriority>::last() {
+  static_assert(
+      CSSPropertyTransitionTimingFunction == CSSPropertyAnimationDelay + 11,
+      "CSSPropertyTransitionTimingFunction should be the end of the high "
+      "priority property range");
+  static_assert(
+      CSSPropertyColor == CSSPropertyTransitionTimingFunction + 1,
+      "CSSPropertyTransitionTimingFunction should be immediately before "
+      "CSSPropertyColor");
+  return CSSPropertyTransitionTimingFunction;
+}
+
+template <>
 inline CSSPropertyID CSSPropertyPriorityData<HighPropertyPriority>::first() {
-  static_assert(CSSPropertyColor == firstCSSProperty,
+  static_assert(CSSPropertyColor == CSSPropertyTransitionTimingFunction + 1,
                 "CSSPropertyColor should be the first high priority property");
   return CSSPropertyColor;
 }
@@ -86,6 +110,10 @@
           property)) {
     return HighPropertyPriority;
   }
+  if (CSSPropertyPriorityData<AnimationPropertyPriority>::propertyHasPriority(
+          property)) {
+    return AnimationPropertyPriority;
+  }
   DCHECK(
       CSSPropertyPriorityData<ResolveVariables>::propertyHasPriority(property));
   return ResolveVariables;
diff --git a/third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp b/third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp
index 32fb8e7f..24b156c8 100644
--- a/third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp
+++ b/third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp
@@ -1021,7 +1021,9 @@
 
   NeedsApplyPass needsApplyPass;
   const MatchResult& result = collector.matchedResult();
-  applyMatchedProperties<HighPropertyPriority, UpdateNeedsApplyPass>(
+  applyMatchedProperties<AnimationPropertyPriority, UpdateNeedsApplyPass>(
+      state, result.allRules(), false, inheritedOnly, needsApplyPass);
+  applyMatchedProperties<HighPropertyPriority, CheckNeedsApplyPass>(
       state, result.allRules(), false, inheritedOnly, needsApplyPass);
 
   // If our font got dirtied, go ahead and update it now.
@@ -1512,6 +1514,7 @@
 
     if (property == CSSPropertyAll && isImportant == current.isImportant()) {
       if (shouldUpdateNeedsApplyPass) {
+        needsApplyPass.set(AnimationPropertyPriority, isImportant);
         needsApplyPass.set(HighPropertyPriority, isImportant);
         needsApplyPass.set(LowPropertyPriority, isImportant);
       }
@@ -1680,12 +1683,18 @@
     }
   }
 
+  // Apply animation affecting properties.
+  applyMatchedProperties<AnimationPropertyPriority, UpdateNeedsApplyPass>(
+      state, matchResult.allRules(), false, applyInheritedOnly, needsApplyPass);
+  applyMatchedProperties<AnimationPropertyPriority, CheckNeedsApplyPass>(
+      state, matchResult.allRules(), true, applyInheritedOnly, needsApplyPass);
+
   // Now we have all of the matched rules in the appropriate order. Walk the
   // rules and apply high-priority properties first, i.e., those properties that
   // other properties depend on.  The order is (1) high-priority not important,
   // (2) high-priority important, (3) normal not important and (4) normal
   // important.
-  applyMatchedProperties<HighPropertyPriority, UpdateNeedsApplyPass>(
+  applyMatchedProperties<HighPropertyPriority, CheckNeedsApplyPass>(
       state, matchResult.allRules(), false, applyInheritedOnly, needsApplyPass);
   for (auto range : ImportantAuthorRanges(matchResult)) {
     applyMatchedProperties<HighPropertyPriority, CheckNeedsApplyPass>(
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp
index e512e9cf..116247e4 100644
--- a/third_party/WebKit/Source/core/dom/Document.cpp
+++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -3428,7 +3428,7 @@
   layoutViewItem().hitTest(result);
 
   if (!request.readOnly())
-    updateHoverActiveState(request, result.innerElement());
+    updateHoverActiveState(request, result.innerElement(), result.scrollbar());
 
   if (isHTMLCanvasElement(result.innerNode())) {
     PlatformMouseEvent eventWithRegion = event;
@@ -5935,22 +5935,23 @@
 }
 
 void Document::updateHoverActiveState(const HitTestRequest& request,
-                                      Element* innerElement) {
+                                      Element* innerElement,
+                                      bool hitScrollbar) {
   DCHECK(!request.readOnly());
 
-  if (request.active() && m_frame)
+  if (request.active() && m_frame && !hitScrollbar)
     m_frame->eventHandler().notifyElementActivated();
 
-  Element* innerElementInDocument = innerElement;
+  Element* innerElementInDocument = hitScrollbar ? nullptr : innerElement;
   while (innerElementInDocument && innerElementInDocument->document() != this) {
     innerElementInDocument->document().updateHoverActiveState(
-        request, innerElementInDocument);
+        request, innerElementInDocument, hitScrollbar);
     innerElementInDocument = innerElementInDocument->document().localOwner();
   }
 
   updateDistribution();
   Element* oldActiveElement = activeHoverElement();
-  if (oldActiveElement && !request.active()) {
+  if (oldActiveElement && (!request.active() || hitScrollbar)) {
     // The oldActiveElement layoutObject is null, dropped on :active by setting
     // display: none, for instance. We still need to clear the ActiveChain as
     // the mouse is released.
diff --git a/third_party/WebKit/Source/core/dom/Document.h b/third_party/WebKit/Source/core/dom/Document.h
index 1f6e73a..cb2c1e4 100644
--- a/third_party/WebKit/Source/core/dom/Document.h
+++ b/third_party/WebKit/Source/core/dom/Document.h
@@ -719,7 +719,9 @@
   void hoveredNodeDetached(Element&);
   void activeChainNodeDetached(Element&);
 
-  void updateHoverActiveState(const HitTestRequest&, Element*);
+  void updateHoverActiveState(const HitTestRequest&,
+                              Element*,
+                              bool hitScrollbar);
 
   // Updates for :target (CSS3 selector).
   void setCSSTarget(Element*);
diff --git a/third_party/WebKit/Source/core/editing/EditingBehavior.cpp b/third_party/WebKit/Source/core/editing/EditingBehavior.cpp
index 54890191..310ddf12 100644
--- a/third_party/WebKit/Source/core/editing/EditingBehavior.cpp
+++ b/third_party/WebKit/Source/core/editing/EditingBehavior.cpp
@@ -267,7 +267,13 @@
   // unexpected behaviour
   if (ch < ' ')
     return false;
-#if !OS(WIN)
+#if OS(LINUX)
+  // According to XKB map no keyboard combinations with ctrl key are mapped to
+  // printable characters, however we need the filter as the DomKey/text could
+  // contain printable characters.
+  if (event.ctrlKey())
+    return false;
+#elif !OS(WIN)
   // Don't insert ASCII character if ctrl w/o alt or meta is on.
   // On Mac, we should ignore events when meta is on (Command-<x>).
   if (ch < 0x80) {
diff --git a/third_party/WebKit/Source/core/events/AddEventListenerOptionsResolved.cpp b/third_party/WebKit/Source/core/events/AddEventListenerOptionsResolved.cpp
index 28676fbd..8afeeeb 100644
--- a/third_party/WebKit/Source/core/events/AddEventListenerOptionsResolved.cpp
+++ b/third_party/WebKit/Source/core/events/AddEventListenerOptionsResolved.cpp
@@ -7,12 +7,13 @@
 namespace blink {
 
 AddEventListenerOptionsResolved::AddEventListenerOptionsResolved()
-    : m_passiveForcedForDocumentTarget(false) {}
+    : m_passiveForcedForDocumentTarget(false), m_passiveSpecified(false) {}
 
 AddEventListenerOptionsResolved::AddEventListenerOptionsResolved(
     const AddEventListenerOptions& options)
     : AddEventListenerOptions(options),
-      m_passiveForcedForDocumentTarget(false) {}
+      m_passiveForcedForDocumentTarget(false),
+      m_passiveSpecified(false) {}
 
 AddEventListenerOptionsResolved::~AddEventListenerOptionsResolved() {}
 
diff --git a/third_party/WebKit/Source/core/events/AddEventListenerOptionsResolved.h b/third_party/WebKit/Source/core/events/AddEventListenerOptionsResolved.h
index 26cbef2..fde5ae7 100644
--- a/third_party/WebKit/Source/core/events/AddEventListenerOptionsResolved.h
+++ b/third_party/WebKit/Source/core/events/AddEventListenerOptionsResolved.h
@@ -29,10 +29,16 @@
     return m_passiveForcedForDocumentTarget;
   }
 
+  // Set whether passive was specified when the options were
+  // created by callee.
+  void setPassiveSpecified(bool specified) { m_passiveSpecified = specified; }
+  bool passiveSpecified() const { return m_passiveSpecified; }
+
   DECLARE_VIRTUAL_TRACE();
 
  private:
   bool m_passiveForcedForDocumentTarget;
+  bool m_passiveSpecified;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/events/Event.cpp b/third_party/WebKit/Source/core/events/Event.cpp
index 5adbdb9..68487d5 100644
--- a/third_party/WebKit/Source/core/events/Event.cpp
+++ b/third_party/WebKit/Source/core/events/Event.cpp
@@ -92,7 +92,7 @@
       m_wasInitialized(true),
       m_isTrusted(false),
       m_preventDefaultCalledOnUncancelableEvent(false),
-      m_handlingPassive(PassiveMode::NotPassive),
+      m_handlingPassive(PassiveMode::NotPassiveDefault),
       m_eventPhase(0),
       m_currentTarget(nullptr),
       m_platformTimeStamp(platformTimeStamp) {}
@@ -219,7 +219,8 @@
 }
 
 void Event::preventDefault() {
-  if (m_handlingPassive != PassiveMode::NotPassive) {
+  if (m_handlingPassive != PassiveMode::NotPassive &&
+      m_handlingPassive != PassiveMode::NotPassiveDefault) {
     m_preventDefaultCalledDuringPassive = true;
 
     const LocalDOMWindow* window =
@@ -228,9 +229,11 @@
       const char* devToolsMsg = nullptr;
       switch (m_handlingPassive) {
         case PassiveMode::NotPassive:
+        case PassiveMode::NotPassiveDefault:
           NOTREACHED();
           break;
         case PassiveMode::Passive:
+        case PassiveMode::PassiveDefault:
           devToolsMsg =
               "Unable to preventDefault inside passive event listener "
               "invocation.";
diff --git a/third_party/WebKit/Source/core/events/Event.h b/third_party/WebKit/Source/core/events/Event.h
index d1c8fdda..e2074dd 100644
--- a/third_party/WebKit/Source/core/events/Event.h
+++ b/third_party/WebKit/Source/core/events/Event.h
@@ -84,9 +84,17 @@
   };
 
   enum class PassiveMode {
+    // Not passive, default initialized.
+    NotPassiveDefault,
+    // Not passive, explicitly specified.
     NotPassive,
+    // Passive, explicitly specified.
     Passive,
+    // Passive, not explicitly specified and forced due to document level
+    // listener.
     PassiveForcedDocumentLevel,
+    // Passive, default initialized.
+    PassiveDefault,
   };
 
   static Event* create() { return new Event; }
diff --git a/third_party/WebKit/Source/core/events/EventTarget.cpp b/third_party/WebKit/Source/core/events/EventTarget.cpp
index 329f68d..dc84b281 100644
--- a/third_party/WebKit/Source/core/events/EventTarget.cpp
+++ b/third_party/WebKit/Source/core/events/EventTarget.cpp
@@ -59,8 +59,6 @@
 namespace blink {
 namespace {
 
-static const double kBlockedWarningThresholdMillis = 100.0;
-
 enum PassiveForcedListenerResultType {
   PreventDefaultNotCalled,
   DocumentLevelTouchPreventDefaultCalled,
@@ -69,11 +67,16 @@
 
 Event::PassiveMode eventPassiveMode(
     const RegisteredEventListener& eventListener) {
-  if (!eventListener.passive())
-    return Event::PassiveMode::NotPassive;
+  if (!eventListener.passive()) {
+    if (eventListener.passiveSpecified())
+      return Event::PassiveMode::NotPassive;
+    return Event::PassiveMode::NotPassiveDefault;
+  }
   if (eventListener.passiveForcedForDocumentTarget())
     return Event::PassiveMode::PassiveForcedDocumentLevel;
-  return Event::PassiveMode::Passive;
+  if (eventListener.passiveSpecified())
+    return Event::PassiveMode::Passive;
+  return Event::PassiveMode::PassiveDefault;
 }
 
 Settings* windowSettings(LocalDOMWindow* executingWindow) {
@@ -96,15 +99,14 @@
          eventType == EventTypeNames::wheel;
 }
 
-double blockedEventsWarningThreshold(ExecutionContext* context, Event* event) {
+double blockedEventsWarningThreshold(ExecutionContext* context,
+                                     const Event* event) {
   if (!event->cancelable())
     return 0.0;
   if (!isScrollBlockingEvent(event->type()))
     return 0.0;
-
-  return PerformanceMonitor::enabled(context)
-             ? kBlockedWarningThresholdMillis / 1000
-             : 0;
+  return PerformanceMonitor::threshold(context,
+                                       PerformanceMonitor::kBlockedEvent);
 }
 
 void reportBlockedEvent(ExecutionContext* context,
@@ -135,8 +137,10 @@
       eventListenerEffectiveFunction(v8Listener->isolate(), handler);
   std::unique_ptr<SourceLocation> location =
       SourceLocation::fromFunction(function);
-  PerformanceMonitor::logViolation(WarningMessageLevel, context, messageText,
-                                   std::move(location));
+
+  PerformanceMonitor::reportGenericViolation(
+      context, PerformanceMonitor::kBlockedEvent, messageText, delayedSeconds,
+      location.get());
   registeredListener->setBlockedEventWarningEmitted();
 }
 
@@ -192,6 +196,8 @@
 void EventTarget::setDefaultAddEventListenerOptions(
     const AtomicString& eventType,
     AddEventListenerOptionsResolved& options) {
+  options.setPassiveSpecified(options.hasPassive());
+
   if (!isScrollBlockingEvent(eventType)) {
     if (!options.hasPassive())
       options.setPassive(false);
diff --git a/third_party/WebKit/Source/core/events/RegisteredEventListener.h b/third_party/WebKit/Source/core/events/RegisteredEventListener.h
index 948f5850..0d73bf8 100644
--- a/third_party/WebKit/Source/core/events/RegisteredEventListener.h
+++ b/third_party/WebKit/Source/core/events/RegisteredEventListener.h
@@ -40,7 +40,8 @@
         m_passive(false),
         m_once(false),
         m_blockedEventWarningEmitted(false),
-        m_passiveForcedForDocumentTarget(false) {}
+        m_passiveForcedForDocumentTarget(false),
+        m_passiveSpecified(false) {}
 
   RegisteredEventListener(EventListener* listener,
                           const AddEventListenerOptionsResolved& options)
@@ -50,7 +51,8 @@
         m_once(options.once()),
         m_blockedEventWarningEmitted(false),
         m_passiveForcedForDocumentTarget(
-            options.passiveForcedForDocumentTarget()) {}
+            options.passiveForcedForDocumentTarget()),
+        m_passiveSpecified(options.passiveSpecified()) {}
 
   DEFINE_INLINE_TRACE() { visitor->trace(m_listener); }
 
@@ -60,6 +62,7 @@
     result.setPassive(m_passive);
     result.setPassiveForcedForDocumentTarget(m_passiveForcedForDocumentTarget);
     result.setOnce(m_once);
+    result.setPassiveSpecified(m_passiveSpecified);
     return result;
   }
 
@@ -81,6 +84,8 @@
     return m_passiveForcedForDocumentTarget;
   }
 
+  bool passiveSpecified() const { return m_passiveSpecified; }
+
   void setBlockedEventWarningEmitted() { m_blockedEventWarningEmitted = true; }
 
   bool matches(const EventListener* listener,
@@ -107,6 +112,7 @@
   unsigned m_once : 1;
   unsigned m_blockedEventWarningEmitted : 1;
   unsigned m_passiveForcedForDocumentTarget : 1;
+  unsigned m_passiveSpecified : 1;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/events/TouchEvent.cpp b/third_party/WebKit/Source/core/events/TouchEvent.cpp
index 9821312..b4df5135 100644
--- a/third_party/WebKit/Source/core/events/TouchEvent.cpp
+++ b/third_party/WebKit/Source/core/events/TouchEvent.cpp
@@ -210,7 +210,8 @@
                        bool cancelable,
                        bool causesScrollingIfUncanceled,
                        bool firstTouchMoveOrStart,
-                       double platformTimeStamp)
+                       double platformTimeStamp,
+                       TouchAction currentTouchAction)
     // Pass a sourceCapabilities including the ability to fire touchevents when
     // creating this touchevent, which is always created from input device
     // capabilities from EventHandler.
@@ -228,7 +229,8 @@
       m_changedTouches(changedTouches),
       m_causesScrollingIfUncanceled(causesScrollingIfUncanceled),
       m_firstTouchMoveOrStart(firstTouchMoveOrStart),
-      m_defaultPreventedBeforeCurrentTarget(false) {}
+      m_defaultPreventedBeforeCurrentTarget(false),
+      m_currentTouchAction(currentTouchAction) {}
 
 TouchEvent::TouchEvent(const AtomicString& type,
                        const TouchEventInit& initializer)
@@ -265,6 +267,24 @@
                                    "because scrolling is in progress and "
                                    "cannot be interrupted."));
   }
+
+  if ((type() == EventTypeNames::touchstart ||
+       type() == EventTypeNames::touchmove) &&
+      view() && view()->frame() && m_currentTouchAction == TouchActionAuto) {
+    switch (handlingPassive()) {
+      case PassiveMode::NotPassiveDefault:
+        UseCounter::count(view()->frame(),
+                          UseCounter::TouchEventPreventedNoTouchAction);
+        break;
+      case PassiveMode::PassiveForcedDocumentLevel:
+        UseCounter::count(
+            view()->frame(),
+            UseCounter::TouchEventPreventedForcedDocumentPassiveNoTouchAction);
+        break;
+      default:
+        break;
+    }
+  }
 }
 
 void TouchEvent::doneDispatchingEventAtCurrentTarget() {
diff --git a/third_party/WebKit/Source/core/events/TouchEvent.h b/third_party/WebKit/Source/core/events/TouchEvent.h
index 530ae237..307663c0 100644
--- a/third_party/WebKit/Source/core/events/TouchEvent.h
+++ b/third_party/WebKit/Source/core/events/TouchEvent.h
@@ -53,10 +53,12 @@
                             bool cancelable,
                             bool causesScrollingIfUncanceled,
                             bool firstTouchMoveOrStart,
-                            double platformTimeStamp) {
+                            double platformTimeStamp,
+                            TouchAction currentTouchAction) {
     return new TouchEvent(touches, targetTouches, changedTouches, type, view,
                           modifiers, cancelable, causesScrollingIfUncanceled,
-                          firstTouchMoveOrStart, platformTimeStamp);
+                          firstTouchMoveOrStart, platformTimeStamp,
+                          currentTouchAction);
   }
 
   static TouchEvent* create(const AtomicString& type,
@@ -103,7 +105,8 @@
              bool cancelable,
              bool causesScrollingIfUncanceled,
              bool firstTouchMoveOrStart,
-             double platformTimeStamp);
+             double platformTimeStamp,
+             TouchAction currentTouchAction);
   TouchEvent(const AtomicString&, const TouchEventInit&);
 
   Member<TouchList> m_touches;
@@ -112,6 +115,10 @@
   bool m_causesScrollingIfUncanceled;
   bool m_firstTouchMoveOrStart;
   bool m_defaultPreventedBeforeCurrentTarget;
+
+  // The current effective touch action computed before each
+  // touchstart event is generated. It is used for UMA histograms.
+  TouchAction m_currentTouchAction;
 };
 
 class TouchEventDispatchMediator final : public EventDispatchMediator {
diff --git a/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp b/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp
index 30b47b8c9..e4a90c0 100644
--- a/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp
+++ b/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp
@@ -40,6 +40,7 @@
 #include "platform/RuntimeEnabledFeatures.h"
 #include "platform/mhtml/ArchiveResource.h"
 #include "platform/mhtml/MHTMLArchive.h"
+#include "platform/network/NetworkInstrumentation.h"
 #include "platform/network/NetworkUtils.h"
 #include "platform/network/ResourceTimingInfo.h"
 #include "platform/tracing/TraceEvent.h"
@@ -484,6 +485,9 @@
     FetchRequest& request,
     const ResourceFactory& factory,
     const SubstituteData& substituteData) {
+  unsigned long identifier = createUniqueIdentifier();
+  network_instrumentation::ScopedResourceLoadTracker scopedResourceLoadTracker(
+      identifier, request.resourceRequest());
   SCOPED_BLINK_UMA_HISTOGRAM_TIMER("Blink.Fetch.RequestResourceTime");
   DCHECK(request.options().synchronousPolicy == RequestAsynchronously ||
          factory.type() == Resource::Raw ||
@@ -494,17 +498,19 @@
   context().addClientHintsIfNecessary(request);
   context().addCSPHeaderIfNecessary(factory.type(), request);
 
+  // TODO(dproy): Remove this. http://crbug.com/659666
   TRACE_EVENT1("blink", "ResourceFetcher::requestResource", "url",
                urlForTraceEvent(request.url()));
 
   if (!request.url().isValid())
     return nullptr;
 
-  unsigned long identifier = createUniqueIdentifier();
   request.mutableResourceRequest().setPriority(computeLoadPriority(
       factory.type(), request, ResourcePriority::NotVisible));
   initializeResourceRequest(request.mutableResourceRequest(), factory.type(),
                             request.defer());
+  network_instrumentation::resourcePrioritySet(
+      identifier, request.resourceRequest().priority());
 
   if (!context().canRequest(
           factory.type(), request.resourceRequest(),
@@ -631,6 +637,9 @@
 
   if (!startLoad(resource))
     return nullptr;
+
+  scopedResourceLoadTracker.resourceLoadContinuesBeyondScope();
+
   DCHECK(!resource->errorOccurred() ||
          request.options().synchronousPolicy == RequestSynchronously);
   return resource;
@@ -1041,8 +1050,6 @@
 void ResourceFetcher::preloadStarted(Resource* resource) {
   if (m_preloads && m_preloads->contains(resource))
     return;
-  TRACE_EVENT_ASYNC_STEP_INTO0("blink.net", "Resource", resource->identifier(),
-                               "Preload");
   resource->increasePreloadCount();
 
   if (!m_preloads)
@@ -1115,7 +1122,8 @@
                                        double finishTime,
                                        int64_t encodedDataLength,
                                        DidFinishLoadingReason finishReason) {
-  TRACE_EVENT_ASYNC_END0("blink.net", "Resource", resource->identifier());
+  network_instrumentation::endResourceLoad(
+      resource->identifier(), network_instrumentation::RequestOutcome::Success);
   DCHECK(resource);
 
   // When loading a multipart resource, make the loader non-block when finishing
@@ -1170,7 +1178,8 @@
 
 void ResourceFetcher::didFailLoading(Resource* resource,
                                      const ResourceError& error) {
-  TRACE_EVENT_ASYNC_END0("blink.net", "Resource", resource->identifier());
+  network_instrumentation::endResourceLoad(
+      resource->identifier(), network_instrumentation::RequestOutcome::Fail);
   removeResourceLoader(resource->loader());
   m_resourceTimingInfoMap.take(const_cast<Resource*>(resource));
   bool isInternalRequest = resource->options().initiatorInfo.name ==
@@ -1428,9 +1437,8 @@
 
     resource->didChangePriority(resourceLoadPriority,
                                 resourcePriority.intraPriorityValue);
-    TRACE_EVENT_ASYNC_STEP_INTO1("blink.net", "Resource",
-                                 resource->identifier(), "ChangePriority",
-                                 "priority", resourceLoadPriority);
+    network_instrumentation::resourcePrioritySet(resource->identifier(),
+                                                 resourceLoadPriority);
     context().dispatchDidChangeResourcePriority(
         resource->identifier(), resourceLoadPriority,
         resourcePriority.intraPriorityValue);
diff --git a/third_party/WebKit/Source/core/fileapi/FileReader.cpp b/third_party/WebKit/Source/core/fileapi/FileReader.cpp
index 036e360..1ec15f7 100644
--- a/third_party/WebKit/Source/core/fileapi/FileReader.cpp
+++ b/third_party/WebKit/Source/core/fileapi/FileReader.cpp
@@ -322,10 +322,6 @@
   m_blobDataHandle = nullptr;
 }
 
-static void delayedAbort(FileReader* reader) {
-  reader->doAbort();
-}
-
 void FileReader::abort() {
   DVLOG(1) << "aborting";
 
@@ -335,20 +331,12 @@
   }
   m_loadingState = LoadingStateAborted;
 
-  // Schedule to have the abort done later since abort() might be called from
-  // the event handler and we do not want the resource loading code to be in the
-  // stack.
-  getExecutionContext()->postTask(
-      BLINK_FROM_HERE,
-      createSameThreadTask(&delayedAbort, wrapPersistent(this)));
-}
-
-void FileReader::doAbort() {
   DCHECK_NE(kDone, m_state);
+  m_state = kDone;
+
   AutoReset<bool> firingEvents(&m_stillFiringEvents, true);
 
-  terminate();
-
+  // Setting error implicitly makes |result| return null.
   m_error = FileError::createDOMException(FileError::kAbortErr);
 
   // Unregister the reader.
@@ -361,10 +349,18 @@
 
   // All possible events have fired and we're done, no more pending activity.
   ThrottlingController::finishReader(getExecutionContext(), this, finalStep);
+
+  // ..but perform the loader cancellation asynchronously as abort() could be
+  // called from the event handler and we do not want the resource loading code
+  // to be on the stack when doing so. The persistent reference keeps the
+  // reader alive until the task has completed.
+  getExecutionContext()->postTask(
+      BLINK_FROM_HERE,
+      createSameThreadTask(&FileReader::terminate, wrapPersistent(this)));
 }
 
 void FileReader::result(StringOrArrayBuffer& resultAttribute) const {
-  if (!m_loader || m_error)
+  if (m_error || !m_loader)
     return;
 
   if (m_readType == FileReaderLoader::ReadAsArrayBuffer)
@@ -445,8 +441,7 @@
   DCHECK_NE(kDone, m_state);
   m_state = kDone;
 
-  m_error = FileError::createDOMException(
-      static_cast<FileError::ErrorCode>(errorCode));
+  m_error = FileError::createDOMException(errorCode);
 
   // Unregister the reader.
   ThrottlingController::FinishReaderType finalStep =
diff --git a/third_party/WebKit/Source/core/fileapi/FileReader.h b/third_party/WebKit/Source/core/fileapi/FileReader.h
index 72d71cb4..ce982061 100644
--- a/third_party/WebKit/Source/core/fileapi/FileReader.h
+++ b/third_party/WebKit/Source/core/fileapi/FileReader.h
@@ -70,8 +70,6 @@
   void readAsDataURL(Blob*, ExceptionState&);
   void abort();
 
-  void doAbort();
-
   ReadyState getReadyState() const { return m_state; }
   DOMException* error() { return m_error; }
   void result(StringOrArrayBuffer& resultAttribute) const;
diff --git a/third_party/WebKit/Source/core/frame/FrameView.cpp b/third_party/WebKit/Source/core/frame/FrameView.cpp
index 3c7a5ca..c9e7d733 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameView.cpp
@@ -182,6 +182,7 @@
       m_hiddenForThrottling(false),
       m_subtreeThrottled(false),
       m_lifecycleUpdatesThrottled(false),
+      m_needsPaintPropertyUpdate(true),
       m_currentUpdateLifecyclePhasesTargetState(
           DocumentLifecycle::Uninitialized),
       m_scrollAnchor(this),
@@ -439,6 +440,55 @@
   scrollbar = nullptr;
 }
 
+ScrollableArea* FrameView::ScrollbarManager::scrollableArea() const {
+  return m_scrollableArea.get();
+}
+
+void FrameView::ScrollbarManager::updateScrollbarGeometry(IntSize viewSize) {
+  if (!hasHorizontalScrollbar() && !hasVerticalScrollbar())
+    return;
+
+  bool scrollbarOnLeft = m_scrollableArea->shouldPlaceVerticalScrollbarOnLeft();
+
+  if (hasHorizontalScrollbar()) {
+    int thickness = m_hBar->scrollbarThickness();
+    IntRect oldRect(m_hBar->frameRect());
+    IntRect hBarRect(
+        (scrollbarOnLeft && hasVerticalScrollbar()) ? m_vBar->width() : 0,
+        viewSize.height() - thickness,
+        viewSize.width() - (hasVerticalScrollbar() ? m_vBar->width() : 0),
+        thickness);
+    m_hBar->setFrameRect(hBarRect);
+    if (oldRect != m_hBar->frameRect())
+      m_scrollableArea->setScrollbarNeedsPaintInvalidation(HorizontalScrollbar);
+
+    int visibleWidth = m_scrollableArea->visibleWidth();
+    int contentsWidth = m_scrollableArea->contentsSize().width();
+    m_hBar->setEnabled(contentsWidth > visibleWidth &&
+                       !m_scrollableArea->scrollbarsHidden());
+    m_hBar->setProportion(visibleWidth, contentsWidth);
+    m_hBar->offsetDidChange();
+  }
+
+  if (hasVerticalScrollbar()) {
+    int thickness = m_vBar->scrollbarThickness();
+    IntRect oldRect(m_vBar->frameRect());
+    IntRect vBarRect(
+        scrollbarOnLeft ? 0 : (viewSize.width() - thickness), 0, thickness,
+        viewSize.height() - (hasHorizontalScrollbar() ? m_hBar->height() : 0));
+    m_vBar->setFrameRect(vBarRect);
+    if (oldRect != m_vBar->frameRect())
+      m_scrollableArea->setScrollbarNeedsPaintInvalidation(VerticalScrollbar);
+
+    int clientHeight = m_scrollableArea->visibleHeight();
+    int contentsHeight = m_scrollableArea->contentsSize().height();
+    m_vBar->setEnabled(contentsHeight > clientHeight &&
+                       !m_scrollableArea->scrollbarsHidden());
+    m_vBar->setProportion(clientHeight, contentsHeight);
+    m_vBar->offsetDidChange();
+  }
+}
+
 void FrameView::recalculateCustomScrollbarStyle() {
   bool didStyleChange = false;
   if (horizontalScrollbar() && horizontalScrollbar()->isCustomScrollbar()) {
@@ -450,7 +500,7 @@
     didStyleChange = true;
   }
   if (didStyleChange) {
-    updateScrollbarGeometry();
+    m_scrollbarManager.updateScrollbarGeometry(size());
     updateScrollCorner();
     positionScrollbarLayers();
   }
@@ -646,6 +696,12 @@
 
   page->chromeClient().contentsSizeChanged(m_frame.get(), size);
   frame().loader().restoreScrollPositionAndViewState();
+
+  if (!RuntimeEnabledFeatures::rootLayerScrollingEnabled()) {
+    // The presence of overflow depends on the contents size. The scroll
+    // properties can change depending on whether overflow scrolling occurs.
+    setNeedsPaintPropertyUpdate();
+  }
 }
 
 void FrameView::adjustViewSize() {
@@ -797,7 +853,7 @@
   }
 
   adjustViewSize();
-  updateScrollbarGeometry();
+  m_scrollbarManager.updateScrollbarGeometry(size());
 
   if (scrollOriginChanged())
     setNeedsLayout();
@@ -2413,7 +2469,7 @@
 }
 
 void FrameView::invalidatePaintForTickmarks() {
-  if (Scrollbar* scrollbar = verticalScrollbar())
+  if (Scrollbar* scrollbar = getScrollableArea()->verticalScrollbar())
     scrollbar->setNeedsPaintInvalidation(
         static_cast<ScrollbarPart>(~ThumbPart));
 }
@@ -2581,6 +2637,13 @@
         RootFrameViewport::create(visualViewport, *layoutViewport);
     m_viewportScrollableArea = rootFrameViewport;
 
+    // TODO(crbug.com/661236): Currently for the main frame, the scroller that
+    // the scrollbar manager works with is RootFrameViewport which is needed so
+    // that the visual viewport is taken into account when determining scrollbar
+    // extent and existence. Eventually, each scroller should have its own
+    // scrollbar manager and we shouldn't set the scroller here.
+    m_scrollbarManager.setScroller(rootFrameViewport);
+
     frameHost->globalRootScrollerController().initializeViewportScrollCallback(
         *rootFrameViewport);
   }
@@ -3706,6 +3769,11 @@
   frame().loader().client()->didChangeScrollOffset();
   if (frame().isMainFrame())
     frame().host()->chromeClient().mainFrameScrollOffsetChanged();
+
+  if (!RuntimeEnabledFeatures::rootLayerScrollingEnabled()) {
+    // The scroll translation paint property depends on scroll offset.
+    setNeedsPaintPropertyUpdate();
+  }
 }
 
 void FrameView::clearScrollAnchor() {
@@ -3714,12 +3782,6 @@
   m_scrollAnchor.clear();
 }
 
-bool FrameView::hasOverlayScrollbars() const {
-  return (horizontalScrollbar() &&
-          horizontalScrollbar()->isOverlayScrollbar()) ||
-         (verticalScrollbar() && verticalScrollbar()->isOverlayScrollbar());
-}
-
 void FrameView::computeScrollbarExistence(
     bool& newHasHorizontalScrollbar,
     bool& newHasVerticalScrollbar,
@@ -3752,15 +3814,17 @@
       (hScroll != ScrollbarAuto && vScroll != ScrollbarAuto))
     return;
 
+  ScrollableArea* scroller = m_scrollbarManager.scrollableArea();
   if (hScroll == ScrollbarAuto)
-    newHasHorizontalScrollbar = docSize.width() > visibleWidth();
+    newHasHorizontalScrollbar = docSize.width() > scroller->visibleWidth();
   if (vScroll == ScrollbarAuto)
-    newHasVerticalScrollbar = docSize.height() > visibleHeight();
+    newHasVerticalScrollbar = docSize.height() > scroller->visibleHeight();
 
   if (hasOverlayScrollbars())
     return;
 
-  IntSize fullVisibleSize = visibleContentRect(IncludeScrollbars).size();
+  IntSize fullVisibleSize =
+      scroller->visibleContentRect(IncludeScrollbars).size();
 
   bool attemptToRemoveScrollbars =
       (option == FirstPass && docSize.width() <= fullVisibleSize.width() &&
@@ -3773,46 +3837,6 @@
   }
 }
 
-void FrameView::updateScrollbarGeometry() {
-  if (horizontalScrollbar()) {
-    int thickness = horizontalScrollbar()->scrollbarThickness();
-    int clientWidth = visibleWidth();
-    IntRect oldRect(horizontalScrollbar()->frameRect());
-    IntRect hBarRect(
-        (shouldPlaceVerticalScrollbarOnLeft() && verticalScrollbar())
-            ? verticalScrollbar()->width()
-            : 0,
-        height() - thickness,
-        width() - (verticalScrollbar() ? verticalScrollbar()->width() : 0),
-        thickness);
-    horizontalScrollbar()->setFrameRect(hBarRect);
-    if (oldRect != horizontalScrollbar()->frameRect())
-      setScrollbarNeedsPaintInvalidation(HorizontalScrollbar);
-
-    horizontalScrollbar()->setEnabled(contentsWidth() > clientWidth);
-    horizontalScrollbar()->setProportion(clientWidth, contentsWidth());
-    horizontalScrollbar()->offsetDidChange();
-  }
-
-  if (verticalScrollbar()) {
-    int thickness = verticalScrollbar()->scrollbarThickness();
-    int clientHeight = visibleHeight();
-    IntRect oldRect(verticalScrollbar()->frameRect());
-    IntRect vBarRect(
-        shouldPlaceVerticalScrollbarOnLeft() ? 0 : (width() - thickness), 0,
-        thickness,
-        height() -
-            (horizontalScrollbar() ? horizontalScrollbar()->height() : 0));
-    verticalScrollbar()->setFrameRect(vBarRect);
-    if (oldRect != verticalScrollbar()->frameRect())
-      setScrollbarNeedsPaintInvalidation(VerticalScrollbar);
-
-    verticalScrollbar()->setEnabled(contentsHeight() > clientHeight);
-    verticalScrollbar()->setProportion(clientHeight, contentsHeight());
-    verticalScrollbar()->offsetDidChange();
-  }
-}
-
 bool FrameView::adjustScrollbarExistence(
     ComputeScrollbarExistenceOption option) {
   ASSERT(m_inUpdateScrollbars);
@@ -3911,7 +3935,7 @@
     scrollbarExistenceChanged = true;
   }
 
-  updateScrollbarGeometry();
+  m_scrollbarManager.updateScrollbarGeometry(size());
 
   if (scrollbarExistenceChanged) {
     // FIXME: Is frameRectsChanged really necessary here? Have any frame rects
diff --git a/third_party/WebKit/Source/core/frame/FrameView.h b/third_party/WebKit/Source/core/frame/FrameView.h
index c687523..672e6aa 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.h
+++ b/third_party/WebKit/Source/core/frame/FrameView.h
@@ -465,6 +465,8 @@
   }
   LayoutScrollbarPart* scrollCorner() const override { return m_scrollCorner; }
 
+  void updateScrollbars() override;
+
   void positionScrollbarLayers();
 
   // Functions for setting and retrieving the scrolling mode in each axis
@@ -724,6 +726,18 @@
     return m_totalPropertyTreeStateForContents.get();
   }
 
+  // Paint properties (e.g., m_preTranslation, etc.) are built from the
+  // FrameView's state (e.g., x(), y(), etc.) as well as inherited context.
+  // When these inputs change, setNeedsPaintPropertyUpdate will cause a property
+  // tree update during the next document lifecycle update.
+  // TODO(pdr): Add additional granularity such as the ability to signal that
+  // only a local paint property update is needed.
+  void setNeedsPaintPropertyUpdate() { m_needsPaintPropertyUpdate = true; }
+  void clearNeedsPaintPropertyUpdate() {
+    DCHECK_EQ(lifecycle().state(), DocumentLifecycle::InPrePaint);
+    m_needsPaintPropertyUpdate = false;
+  }
+  bool needsPaintPropertyUpdate() const { return m_needsPaintPropertyUpdate; }
   // TODO(ojan): Merge this with IntersectionObserver once it lands.
   IntRect computeVisibleArea();
 
@@ -760,11 +774,7 @@
       bool& newHasVerticalScrollbar,
       const IntSize& docSize,
       ComputeScrollbarExistenceOption = FirstPass) const;
-  void updateScrollbarGeometry();
 
-  // Called to update the scrollbars to accurately reflect the state of the
-  // view.
-  void updateScrollbars();
   void updateScrollbarsIfNeeded();
 
   class InUpdateScrollbarsScope {
@@ -797,6 +807,11 @@
     // setHas*Scrollbar functions above.
     Scrollbar* createScrollbar(ScrollbarOrientation) override;
 
+    // Updates the scrollbar geometry given the size of the frame view rect.
+    void updateScrollbarGeometry(IntSize viewSize);
+
+    ScrollableArea* scrollableArea() const;
+
    protected:
     void destroyScrollbar(ScrollbarOrientation) override;
   };
@@ -876,10 +891,6 @@
   static bool computeCompositedSelection(LocalFrame&, CompositedSelection&);
   void updateCompositedSelectionIfNeeded();
 
-  // Returns true if the FrameView's own scrollbars overlay its content when
-  // visible.
-  bool hasOverlayScrollbars() const;
-
   // Returns true if the frame should use custom scrollbars. If true, one of
   // either |customScrollbarElement| or |customScrollbarFrame| will be set to
   // the element or frame which owns the scrollbar with the other set to null.
@@ -1071,6 +1082,9 @@
   // properties are either created by this FrameView or are inherited from
   // an ancestor.
   std::unique_ptr<PropertyTreeState> m_totalPropertyTreeStateForContents;
+  // Whether the paint properties need to be updated. For more details, see
+  // FrameView::needsPaintPropertyUpdate().
+  bool m_needsPaintPropertyUpdate;
 
   // This is set on the local root frame view only.
   DocumentLifecycle::LifecycleState m_currentUpdateLifecyclePhasesTargetState;
diff --git a/third_party/WebKit/Source/core/frame/LocalFrame.cpp b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
index 06675fc..c73faf4a 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrame.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
@@ -70,6 +70,7 @@
 #include "core/paint/ObjectPainter.h"
 #include "core/paint/PaintInfo.h"
 #include "core/paint/PaintLayer.h"
+#include "core/paint/PaintLayerPainter.h"
 #include "core/paint/TransformRecorder.h"
 #include "core/svg/SVGDocumentExtensions.h"
 #include "core/timing/Performance.h"
diff --git a/third_party/WebKit/Source/core/frame/PerformanceMonitor.cpp b/third_party/WebKit/Source/core/frame/PerformanceMonitor.cpp
index 79c29be..b4949ce 100644
--- a/third_party/WebKit/Source/core/frame/PerformanceMonitor.cpp
+++ b/third_party/WebKit/Source/core/frame/PerformanceMonitor.cpp
@@ -5,63 +5,15 @@
 #include "core/frame/PerformanceMonitor.h"
 
 #include "bindings/core/v8/SourceLocation.h"
-#include "core/InstrumentingAgents.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExecutionContext.h"
-#include "core/frame/DOMWindow.h"
 #include "core/frame/Frame.h"
-#include "core/frame/FrameConsole.h"
 #include "core/frame/LocalFrame.h"
-#include "core/frame/UseCounter.h"
-#include "core/inspector/ConsoleMessage.h"
-#include "core/timing/DOMWindowPerformance.h"
-#include "core/timing/Performance.h"
 #include "public/platform/Platform.h"
 #include "wtf/CurrentTime.h"
 
 namespace blink {
 
-namespace {
-static const double kLongTaskThresholdMillis = 50.0;
-static const double kLongTaskWarningThresholdMillis = 150.0;
-
-static const double kSyncLayoutThresholdMillis = 30.0;
-static const double kSyncLayoutWarningThresholdMillis = 60.0;
-
-static const char kUnknownAttribution[] = "unknown";
-static const char kAmbugiousAttribution[] = "multiple-contexts";
-static const char kSameOriginAttribution[] = "same-origin";
-static const char kAncestorAttribution[] = "cross-origin-ancestor";
-static const char kDescendantAttribution[] = "cross-origin-descendant";
-static const char kCrossOriginAttribution[] = "cross-origin-unreachable";
-
-bool canAccessOrigin(Frame* frame1, Frame* frame2) {
-  SecurityOrigin* securityOrigin1 =
-      frame1->securityContext()->getSecurityOrigin();
-  SecurityOrigin* securityOrigin2 =
-      frame2->securityContext()->getSecurityOrigin();
-  return securityOrigin1->canAccess(securityOrigin2);
-}
-
-}  // namespace
-
-// static
-void PerformanceMonitor::performanceObserverAdded(Performance* performance) {
-  LocalFrame* frame = performance->frame();
-  PerformanceMonitor* monitor = frame->performanceMonitor();
-  monitor->m_webPerformanceObservers.add(performance);
-  monitor->updateInstrumentation();
-}
-
-// static
-void PerformanceMonitor::performanceObserverRemoved(Performance* performance) {
-  LocalFrame* frame = performance->frame();
-  DCHECK(frame);
-  PerformanceMonitor* monitor = frame->performanceMonitor();
-  monitor->m_webPerformanceObservers.remove(performance);
-  monitor->updateInstrumentation();
-}
-
 // static
 void PerformanceMonitor::willExecuteScript(ExecutionContext* context) {
   PerformanceMonitor* performanceMonitor =
@@ -111,71 +63,105 @@
 }
 
 // static
-bool PerformanceMonitor::enabled(ExecutionContext* context) {
-  return PerformanceMonitor::instrumentingMonitor(context);
-}
-
-// static
-void PerformanceMonitor::logViolation(MessageLevel level,
-                                      ExecutionContext* context,
-                                      const String& messageText) {
-  PerformanceMonitor* performanceMonitor =
+double PerformanceMonitor::threshold(ExecutionContext* context,
+                                     Violation violation) {
+  PerformanceMonitor* monitor =
       PerformanceMonitor::instrumentingMonitor(context);
-  if (performanceMonitor)
-    performanceMonitor->logViolation(level, messageText);
+  return monitor ? monitor->m_thresholds[violation] : 0;
 }
 
 // static
-void PerformanceMonitor::logViolation(
-    MessageLevel level,
-    ExecutionContext* context,
-    const String& messageText,
-    std::unique_ptr<SourceLocation> location) {
-  PerformanceMonitor* performanceMonitor =
+void PerformanceMonitor::reportGenericViolation(ExecutionContext* context,
+                                                Violation violation,
+                                                const String& text,
+                                                double time,
+                                                SourceLocation* location) {
+  PerformanceMonitor* monitor =
       PerformanceMonitor::instrumentingMonitor(context);
-  if (performanceMonitor)
-    performanceMonitor->logViolation(level, messageText, std::move(location));
+  if (!monitor)
+    return;
+  ClientThresholds* clientThresholds = monitor->m_subscriptions.get(violation);
+  if (!clientThresholds)
+    return;
+  for (const auto& it : *clientThresholds) {
+    if (it.value < time)
+      it.key->reportGenericViolation(violation, text, time, location);
+  }
 }
 
 // static
-PerformanceMonitor* PerformanceMonitor::instrumentingMonitor(
-    ExecutionContext* context) {
+PerformanceMonitor* PerformanceMonitor::monitor(
+    const ExecutionContext* context) {
   if (!context->isDocument())
     return nullptr;
   LocalFrame* frame = toDocument(context)->frame();
   if (!frame)
     return nullptr;
-  PerformanceMonitor* monitor = frame->performanceMonitor();
-  return monitor->m_enabled ? monitor : nullptr;
+  return frame->performanceMonitor();
+}
+
+// static
+PerformanceMonitor* PerformanceMonitor::instrumentingMonitor(
+    const ExecutionContext* context) {
+  PerformanceMonitor* monitor = PerformanceMonitor::monitor(context);
+  return monitor && monitor->m_enabled ? monitor : nullptr;
 }
 
 PerformanceMonitor::PerformanceMonitor(LocalFrame* localRoot)
-    : m_localRoot(localRoot) {}
+    : m_localRoot(localRoot) {
+  std::fill(std::begin(m_thresholds), std::end(m_thresholds), 0);
+}
 
 PerformanceMonitor::~PerformanceMonitor() {
-  m_webPerformanceObservers.clear();
-  m_loggingEnabled = false;
+  m_subscriptions.clear();
   updateInstrumentation();
 }
 
-void PerformanceMonitor::setLoggingEnabled(bool enabled) {
-  m_loggingEnabled = enabled;
+void PerformanceMonitor::subscribe(Violation violation,
+                                   double threshold,
+                                   Client* client) {
+  DCHECK(violation < kAfterLast);
+  ClientThresholds* clientThresholds = m_subscriptions.get(violation);
+  if (!clientThresholds) {
+    clientThresholds = new ClientThresholds();
+    m_subscriptions.set(violation, clientThresholds);
+  }
+  clientThresholds->set(client, threshold);
+  updateInstrumentation();
+}
+
+void PerformanceMonitor::unsubscribeAll(Client* client) {
+  for (const auto& it : m_subscriptions)
+    it.value->remove(client);
   updateInstrumentation();
 }
 
 void PerformanceMonitor::updateInstrumentation() {
-  bool shouldEnable = m_loggingEnabled || m_webPerformanceObservers.size();
-  if (shouldEnable == m_enabled)
-    return;
-  if (shouldEnable) {
-    UseCounter::count(m_localRoot, UseCounter::LongTaskObserver);
-    Platform::current()->currentThread()->addTaskTimeObserver(this);
-    Platform::current()->currentThread()->addTaskObserver(this);
-  } else {
-    Platform::current()->currentThread()->removeTaskTimeObserver(this);
-    Platform::current()->currentThread()->removeTaskObserver(this);
+  bool longTaskObserverEnabled = !!m_thresholds[kLongTask];
+  std::fill(std::begin(m_thresholds), std::end(m_thresholds), 0);
+
+  for (const auto& it : m_subscriptions) {
+    Violation violation = static_cast<Violation>(it.key);
+    ClientThresholds* clientThresholds = it.value;
+    for (const auto& clientThreshold : *clientThresholds) {
+      if (!m_thresholds[violation] ||
+          m_thresholds[violation] > clientThreshold.value)
+        m_thresholds[violation] = clientThreshold.value;
+    }
   }
-  m_enabled = shouldEnable;
+
+  if (!m_thresholds[kLongTask] != !longTaskObserverEnabled) {
+    if (m_thresholds[kLongTask]) {
+      Platform::current()->currentThread()->addTaskTimeObserver(this);
+      Platform::current()->currentThread()->addTaskObserver(this);
+    } else {
+      Platform::current()->currentThread()->removeTaskTimeObserver(this);
+      Platform::current()->currentThread()->removeTaskObserver(this);
+    }
+  }
+
+  m_enabled = std::count(std::begin(m_thresholds), std::end(m_thresholds), 0) <
+              static_cast<int>(kAfterLast);
 }
 
 void PerformanceMonitor::innerWillExecuteScript(ExecutionContext* context) {
@@ -227,110 +213,36 @@
 }
 
 void PerformanceMonitor::didProcessTask() {
-  if (m_perTaskStyleAndLayoutTime * 1000 < kSyncLayoutThresholdMillis)
+  double threshold = m_thresholds[kLongLayout];
+  if (!threshold || m_perTaskStyleAndLayoutTime < threshold)
     return;
 
-  if (m_loggingEnabled) {
-    logViolation(
-        m_perTaskStyleAndLayoutTime * 1000 < kSyncLayoutWarningThresholdMillis
-            ? InfoMessageLevel
-            : WarningMessageLevel,
-        String::format("Forced reflow while executing JavaScript took %ldms.",
-                       lround(m_perTaskStyleAndLayoutTime * 1000)));
+  ClientThresholds* clientThresholds = m_subscriptions.get(kLongLayout);
+  DCHECK(clientThresholds);
+  for (const auto& it : *clientThresholds) {
+    if (it.value < m_perTaskStyleAndLayoutTime)
+      it.key->reportLongLayout(m_perTaskStyleAndLayoutTime);
   }
 }
 
 void PerformanceMonitor::ReportTaskTime(scheduler::TaskQueue*,
                                         double startTime,
                                         double endTime) {
-  double taskTimeMs = (endTime - startTime) * 1000;
-  if (taskTimeMs < kLongTaskThresholdMillis)
+  double taskTime = endTime - startTime;
+  if (!m_thresholds[kLongTask] || taskTime < m_thresholds[kLongTask])
     return;
 
-  for (Performance* performance : m_webPerformanceObservers) {
-    if (!performance->frame())
-      continue;
-    std::pair<String, DOMWindow*> attribution =
-        sanitizedAttribution(m_frameContexts, performance->frame());
-    performance->addLongTaskTiming(startTime, endTime, attribution.first,
-                                   attribution.second);
+  ClientThresholds* clientThresholds = m_subscriptions.get(kLongTask);
+  for (const auto& it : *clientThresholds) {
+    if (it.value < taskTime)
+      it.key->reportLongTask(startTime, endTime, m_frameContexts);
   }
-  if (m_loggingEnabled) {
-    logViolation(taskTimeMs < kLongTaskWarningThresholdMillis
-                     ? InfoMessageLevel
-                     : WarningMessageLevel,
-                 String::format("Long running JavaScript task took %ldms.",
-                                lround(taskTimeMs)));
-  }
-}
-
-void PerformanceMonitor::logViolation(MessageLevel level,
-                                      const String& messageText) {
-  ConsoleMessage* message =
-      ConsoleMessage::create(ViolationMessageSource, level, messageText);
-  m_localRoot->console().addMessage(message);
-}
-
-void PerformanceMonitor::logViolation(
-    MessageLevel level,
-    const String& messageText,
-    std::unique_ptr<SourceLocation> location) {
-  ConsoleMessage* message = ConsoleMessage::create(
-      ViolationMessageSource, level, messageText, std::move(location));
-  m_localRoot->console().addMessage(message);
-}
-
-/**
- * Report sanitized name based on cross-origin policy.
- * See detailed Security doc here: http://bit.ly/2duD3F7
- */
-std::pair<String, DOMWindow*> PerformanceMonitor::sanitizedAttribution(
-    const HeapHashSet<Member<Frame>>& frames,
-    Frame* observerFrame) {
-  if (frames.size() == 0) {
-    // Unable to attribute as no script was involved.
-    return std::make_pair(kUnknownAttribution, nullptr);
-  }
-  if (frames.size() > 1) {
-    // Unable to attribute, multiple script execution contents were involved.
-    return std::make_pair(kAmbugiousAttribution, nullptr);
-  }
-  // Exactly one culprit location, attribute based on origin boundary.
-  DCHECK_EQ(1u, frames.size());
-  Frame* culpritFrame = *frames.begin();
-  DCHECK(culpritFrame);
-  if (canAccessOrigin(observerFrame, culpritFrame)) {
-    // From accessible frames or same origin, return culprit location URL.
-    return std::make_pair(kSameOriginAttribution, culpritFrame->domWindow());
-  }
-  // For cross-origin, if the culprit is the descendant or ancestor of
-  // observer then indicate the *closest* cross-origin frame between
-  // the observer and the culprit, in the corresponding direction.
-  if (culpritFrame->tree().isDescendantOf(observerFrame)) {
-    // If the culprit is a descendant of the observer, then walk up the tree
-    // from culprit to observer, and report the *last* cross-origin (from
-    // observer) frame.  If no intermediate cross-origin frame is found, then
-    // report the culprit directly.
-    Frame* lastCrossOriginFrame = culpritFrame;
-    for (Frame* frame = culpritFrame; frame != observerFrame;
-         frame = frame->tree().parent()) {
-      if (!canAccessOrigin(observerFrame, frame)) {
-        lastCrossOriginFrame = frame;
-      }
-    }
-    return std::make_pair(kDescendantAttribution,
-                          lastCrossOriginFrame->domWindow());
-  }
-  if (observerFrame->tree().isDescendantOf(culpritFrame)) {
-    return std::make_pair(kAncestorAttribution, nullptr);
-  }
-  return std::make_pair(kCrossOriginAttribution, nullptr);
 }
 
 DEFINE_TRACE(PerformanceMonitor) {
   visitor->trace(m_localRoot);
   visitor->trace(m_frameContexts);
-  visitor->trace(m_webPerformanceObservers);
+  visitor->trace(m_subscriptions);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/PerformanceMonitor.h b/third_party/WebKit/Source/core/frame/PerformanceMonitor.h
index 3d617876..07e018c 100644
--- a/third_party/WebKit/Source/core/frame/PerformanceMonitor.h
+++ b/third_party/WebKit/Source/core/frame/PerformanceMonitor.h
@@ -6,7 +6,6 @@
 #define PerformanceMonitor_h
 
 #include "core/CoreExport.h"
-#include "core/inspector/ConsoleTypes.h"
 #include "platform/heap/Handle.h"
 #include "public/platform/WebThread.h"
 #include "public/platform/scheduler/base/task_time_observer.h"
@@ -32,36 +31,51 @@
   WTF_MAKE_NONCOPYABLE(PerformanceMonitor);
 
  public:
-  // Instrumenting methods, TODO: codegen those.
-  static void performanceObserverAdded(Performance*);
-  static void performanceObserverRemoved(Performance*);
+  enum Violation : size_t { kLongTask, kLongLayout, kBlockedEvent, kAfterLast };
+
+  class CORE_EXPORT Client : public GarbageCollectedMixin {
+   public:
+    virtual void reportLongTask(
+        double startTime,
+        double endTime,
+        const HeapHashSet<Member<Frame>>& contextFrames){};
+    virtual void reportLongLayout(double duration){};
+    virtual void reportGenericViolation(Violation,
+                                        const String& text,
+                                        double time,
+                                        SourceLocation*){};
+    DEFINE_INLINE_VIRTUAL_TRACE() {}
+  };
+
+  // Instrumenting methods.
   static void willExecuteScript(ExecutionContext*);
   static void didExecuteScript(ExecutionContext*);
   static void willUpdateLayout(Document*);
   static void didUpdateLayout(Document*);
   static void willRecalculateStyle(Document*);
   static void didRecalculateStyle(Document*);
+  static void reportGenericViolation(ExecutionContext*,
+                                     Violation,
+                                     const String& text,
+                                     double time,
+                                     SourceLocation*);
+  static double threshold(ExecutionContext*, Violation);
 
-  // Direct logging API for core.
-  static bool enabled(ExecutionContext*);
-  static void logViolation(MessageLevel, ExecutionContext*, const String&);
-  static void logViolation(MessageLevel,
-                           ExecutionContext*,
-                           const String&,
-                           std::unique_ptr<SourceLocation>);
+  // Direct API for core.
+  void subscribe(Violation, double threshold, Client*);
+  void unsubscribeAll(Client*);
 
   explicit PerformanceMonitor(LocalFrame*);
   ~PerformanceMonitor();
 
-  void setLoggingEnabled(bool);
-
   DECLARE_VIRTUAL_TRACE();
 
  private:
   friend class PerformanceMonitorTest;
   friend class PerformanceTest;
 
-  static PerformanceMonitor* instrumentingMonitor(ExecutionContext*);
+  static PerformanceMonitor* monitor(const ExecutionContext*);
+  static PerformanceMonitor* instrumentingMonitor(const ExecutionContext*);
 
   void updateInstrumentation();
 
@@ -72,11 +86,6 @@
   void willRecalculateStyle();
   void didRecalculateStyle();
 
-  void logViolation(MessageLevel, const String&);
-  void logViolation(MessageLevel,
-                    const String&,
-                    std::unique_ptr<SourceLocation>);
-
   // WebThread::TaskObserver implementation.
   void willProcessTask() override;
   void didProcessTask() override;
@@ -91,14 +100,21 @@
       Frame* observerFrame);
 
   bool m_enabled = false;
-  bool m_loggingEnabled = false;
   bool m_isExecutingScript = false;
   double m_layoutStartTime = 0;
   double m_styleStartTime = 0;
   double m_perTaskStyleAndLayoutTime = 0;
+
+  double m_thresholds[kAfterLast];
+
   Member<LocalFrame> m_localRoot;
   HeapHashSet<Member<Frame>> m_frameContexts;
-  HeapHashSet<Member<Performance>> m_webPerformanceObservers;
+  using ClientThresholds = HeapHashMap<Member<Client>, double>;
+  HeapHashMap<Violation,
+              Member<ClientThresholds>,
+              typename DefaultHash<size_t>::Hash,
+              WTF::UnsignedWithZeroKeyHashTraits<size_t>>
+      m_subscriptions;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/PerformanceMonitorTest.cpp b/third_party/WebKit/Source/core/frame/PerformanceMonitorTest.cpp
index ea83c27..4dea09b1 100644
--- a/third_party/WebKit/Source/core/frame/PerformanceMonitorTest.cpp
+++ b/third_party/WebKit/Source/core/frame/PerformanceMonitorTest.cpp
@@ -46,11 +46,6 @@
   String frameContextURL();
   int numUniqueFrameContextsSeen();
 
-  String sanitizedLongTaskName(HeapHashSet<Member<Frame>> frameContexts,
-                               Frame* observerFrame) {
-    return m_monitor->sanitizedAttribution(frameContexts, observerFrame).first;
-  }
-
   Persistent<PerformanceMonitor> m_monitor;
   std::unique_ptr<DummyPageHolder> m_pageHolder;
   std::unique_ptr<DummyPageHolder> m_anotherPageHolder;
@@ -132,29 +127,4 @@
   EXPECT_EQ(0, numUniqueFrameContextsSeen());
 }
 
-TEST_F(PerformanceMonitorTest, SanitizedLongTaskName) {
-  HeapHashSet<Member<Frame>> frameContexts;
-  // Unable to attribute, when no execution contents are available.
-  EXPECT_EQ("unknown", sanitizedLongTaskName(frameContexts, frame()));
-
-  // Attribute for same context (and same origin).
-  frameContexts.add(frame());
-  EXPECT_EQ("same-origin", sanitizedLongTaskName(frameContexts, frame()));
-
-  // Unable to attribute, when multiple script execution contents are involved.
-  frameContexts.add(anotherFrame());
-  EXPECT_EQ("multiple-contexts", sanitizedLongTaskName(frameContexts, frame()));
-}
-
-TEST_F(PerformanceMonitorTest, SanitizedLongTaskName_CrossOrigin) {
-  HeapHashSet<Member<Frame>> frameContexts;
-  // Unable to attribute, when no execution contents are available.
-  EXPECT_EQ("unknown", sanitizedLongTaskName(frameContexts, frame()));
-
-  // Attribute for same context (and same origin).
-  frameContexts.add(anotherFrame());
-  EXPECT_EQ("cross-origin-unreachable",
-            sanitizedLongTaskName(frameContexts, frame()));
-}
-
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/RootFrameViewport.cpp b/third_party/WebKit/Source/core/frame/RootFrameViewport.cpp
index 611d449..4cd6c73 100644
--- a/third_party/WebKit/Source/core/frame/RootFrameViewport.cpp
+++ b/third_party/WebKit/Source/core/frame/RootFrameViewport.cpp
@@ -95,6 +95,8 @@
     if (ScrollAnchor* anchor = layoutViewport().scrollAnchor())
       anchor->clear();
   }
+
+  layoutViewport().updateScrollbars();
 }
 
 LayoutBox* RootFrameViewport::layoutBox() const {
@@ -202,6 +204,89 @@
   return layoutViewport().scrollBehaviorStyle();
 }
 
+Scrollbar* RootFrameViewport::horizontalScrollbar() const {
+  return layoutViewport().horizontalScrollbar();
+}
+
+Scrollbar* RootFrameViewport::verticalScrollbar() const {
+  return layoutViewport().verticalScrollbar();
+}
+
+bool RootFrameViewport::hasOverlayScrollbars() const {
+  return layoutViewport().hasOverlayScrollbars();
+}
+
+void RootFrameViewport::setScrollbarNeedsPaintInvalidation(
+    ScrollbarOrientation orientation) {
+  return layoutViewport().setScrollbarNeedsPaintInvalidation(orientation);
+}
+
+LayoutRect RootFrameViewport::visualRectForScrollbarParts() const {
+  return layoutViewport().visualRectForScrollbarParts();
+}
+
+ScrollbarOverlayColorTheme RootFrameViewport::getScrollbarOverlayColorTheme()
+    const {
+  return layoutViewport().getScrollbarOverlayColorTheme();
+}
+
+void RootFrameViewport::getTickmarks(Vector<IntRect>& tickmarks) const {
+  layoutViewport().getTickmarks(tickmarks);
+}
+
+void RootFrameViewport::mouseEnteredScrollbar(Scrollbar& scrollbar) {
+  layoutViewport().mouseEnteredScrollbar(scrollbar);
+}
+
+void RootFrameViewport::mouseExitedScrollbar(Scrollbar& scrollbar) {
+  layoutViewport().mouseExitedScrollbar(scrollbar);
+}
+
+void RootFrameViewport::scrollbarVisibilityChanged() {
+  layoutViewport().scrollbarVisibilityChanged();
+}
+
+bool RootFrameViewport::scrollbarsHidden() const {
+  return layoutViewport().scrollbarsHidden();
+}
+
+IntRect RootFrameViewport::convertFromScrollbarToContainingWidget(
+    const Scrollbar& scrollbar,
+    const IntRect& localRect) const {
+  return layoutViewport().convertFromScrollbarToContainingWidget(scrollbar,
+                                                                 localRect);
+}
+
+IntRect RootFrameViewport::convertFromContainingWidgetToScrollbar(
+    const Scrollbar& scrollbar,
+    const IntRect& parentRect) const {
+  return layoutViewport().convertFromContainingWidgetToScrollbar(scrollbar,
+                                                                 parentRect);
+}
+
+IntPoint RootFrameViewport::convertFromScrollbarToContainingWidget(
+    const Scrollbar& scrollbar,
+    const IntPoint& localPoint) const {
+  return layoutViewport().convertFromScrollbarToContainingWidget(scrollbar,
+                                                                 localPoint);
+}
+
+IntPoint RootFrameViewport::convertFromContainingWidgetToScrollbar(
+    const Scrollbar& scrollbar,
+    const IntPoint& parentPoint) const {
+  return layoutViewport().convertFromContainingWidgetToScrollbar(scrollbar,
+                                                                 parentPoint);
+}
+
+ScrollOffset RootFrameViewport::scrollAnimatorDesiredTargetOffset() const {
+  return layoutViewport().scrollAnimatorDesiredTargetOffset() +
+         visualViewport().scrollAnimatorDesiredTargetOffset();
+}
+
+void RootFrameViewport::setScrollCornerNeedsPaintInvalidation() {
+  return layoutViewport().setScrollCornerNeedsPaintInvalidation();
+}
+
 LayoutRect RootFrameViewport::scrollIntoView(const LayoutRect& rectInContent,
                                              const ScrollAlignment& alignX,
                                              const ScrollAlignment& alignY,
diff --git a/third_party/WebKit/Source/core/frame/RootFrameViewport.h b/third_party/WebKit/Source/core/frame/RootFrameViewport.h
index 93e554d..729ec72 100644
--- a/third_party/WebKit/Source/core/frame/RootFrameViewport.h
+++ b/third_party/WebKit/Source/core/frame/RootFrameViewport.h
@@ -64,11 +64,6 @@
   IntRect visibleContentRect(
       IncludeScrollbarsInRect = ExcludeScrollbars) const override;
   bool shouldUseIntegerScrollOffset() const override;
-  LayoutRect visualRectForScrollbarParts() const override {
-    ASSERT_NOT_REACHED();
-    return LayoutRect();
-  }
-  bool isActive() const override;
   int scrollSize(ScrollbarOrientation) const override;
   bool isScrollCornerVisible() const override;
   IntRect scrollCornerRect() const override;
@@ -107,6 +102,34 @@
                                       const LayoutObject*,
                                       unsigned = 0) const final;
 
+  // Scrollbar related
+  // TODO(crbug.com/661236): Seperate the scrollbar related logic from
+  // ScrollableArea.
+  Scrollbar* horizontalScrollbar() const override;
+  Scrollbar* verticalScrollbar() const override;
+  bool isActive() const override;
+  bool hasOverlayScrollbars() const override;
+  void setScrollbarNeedsPaintInvalidation(ScrollbarOrientation) override;
+  LayoutRect visualRectForScrollbarParts() const override;
+  ScrollbarOverlayColorTheme getScrollbarOverlayColorTheme() const override;
+  void getTickmarks(Vector<IntRect>& rects) const override;
+  void mouseEnteredScrollbar(Scrollbar&) override;
+  void mouseExitedScrollbar(Scrollbar&) override;
+  void scrollbarVisibilityChanged() override;
+  bool scrollbarsHidden() const override;
+  IntRect convertFromScrollbarToContainingWidget(const Scrollbar&,
+                                                 const IntRect&) const override;
+  IntRect convertFromContainingWidgetToScrollbar(const Scrollbar&,
+                                                 const IntRect&) const override;
+  IntPoint convertFromScrollbarToContainingWidget(
+      const Scrollbar&,
+      const IntPoint&) const override;
+  IntPoint convertFromContainingWidgetToScrollbar(
+      const Scrollbar&,
+      const IntPoint&) const override;
+  ScrollOffset scrollAnimatorDesiredTargetOffset() const override;
+  void setScrollCornerNeedsPaintInvalidation() override;
+
  private:
   RootFrameViewport(ScrollableArea& visualViewport,
                     ScrollableArea& layoutViewport);
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.h b/third_party/WebKit/Source/core/frame/UseCounter.h
index 31dce80a..589fff3 100644
--- a/third_party/WebKit/Source/core/frame/UseCounter.h
+++ b/third_party/WebKit/Source/core/frame/UseCounter.h
@@ -1387,6 +1387,8 @@
     HTMLMediaElementPreloadForcedMetadata = 1679,
     GenericSensorStart = 1680,
     GenericSensorStop = 1681,
+    TouchEventPreventedNoTouchAction = 1682,
+    TouchEventPreventedForcedDocumentPassiveNoTouchAction = 1683,
 
     // Add new features immediately above this line. Don't change assigned
     // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/WebKit/Source/core/html/HTMLImageElement.cpp b/third_party/WebKit/Source/core/html/HTMLImageElement.cpp
index 40065bb..01fc2ac 100644
--- a/third_party/WebKit/Source/core/html/HTMLImageElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLImageElement.cpp
@@ -141,7 +141,7 @@
 }
 
 HTMLImageElement* HTMLImageElement::createForJSConstructor(Document& document,
-                                                           int width) {
+                                                           unsigned width) {
   HTMLImageElement* image = new HTMLImageElement(document);
   image->setWidth(width);
   image->m_elementCreatedByParser = false;
@@ -149,8 +149,8 @@
 }
 
 HTMLImageElement* HTMLImageElement::createForJSConstructor(Document& document,
-                                                           int width,
-                                                           int height) {
+                                                           unsigned width,
+                                                           unsigned height) {
   HTMLImageElement* image = new HTMLImageElement(document);
   image->setWidth(width);
   image->setHeight(height);
@@ -426,25 +426,25 @@
   HTMLElement::removedFrom(insertionPoint);
 }
 
-int HTMLImageElement::width() {
+unsigned HTMLImageElement::width() {
   if (inActiveDocument())
     document().updateStyleAndLayoutIgnorePendingStylesheets();
 
   if (!layoutObject()) {
     // check the attribute first for an explicit pixel value
-    bool ok;
-    int width = getAttribute(widthAttr).toInt(&ok);
-    if (ok)
+    unsigned width = 0;
+    if (parseHTMLNonNegativeInteger(getAttribute(widthAttr), width))
       return width;
 
     // if the image is available, use its width
-    if (imageLoader().image())
+    if (imageLoader().image()) {
       return imageLoader()
           .image()
           ->imageSize(LayoutObject::shouldRespectImageOrientation(nullptr),
                       1.0f)
           .width()
-          .toInt();
+          .toUnsigned();
+    }
   }
 
   LayoutBox* box = layoutBox();
@@ -453,25 +453,25 @@
              : 0;
 }
 
-int HTMLImageElement::height() {
+unsigned HTMLImageElement::height() {
   if (inActiveDocument())
     document().updateStyleAndLayoutIgnorePendingStylesheets();
 
   if (!layoutObject()) {
     // check the attribute first for an explicit pixel value
-    bool ok;
-    int height = getAttribute(heightAttr).toInt(&ok);
-    if (ok)
+    unsigned height = 0;
+    if (parseHTMLNonNegativeInteger(getAttribute(heightAttr), height))
       return height;
 
     // if the image is available, use its height
-    if (imageLoader().image())
+    if (imageLoader().image()) {
       return imageLoader()
           .image()
           ->imageSize(LayoutObject::shouldRespectImageOrientation(nullptr),
                       1.0f)
           .height()
-          .toInt();
+          .toUnsigned();
+    }
   }
 
   LayoutBox* box = layoutBox();
@@ -480,7 +480,7 @@
              : 0;
 }
 
-int HTMLImageElement::naturalWidth() const {
+unsigned HTMLImageElement::naturalWidth() const {
   if (!imageLoader().image())
     return 0;
 
@@ -490,10 +490,10 @@
                   m_imageDevicePixelRatio,
                   ImageResource::IntrinsicCorrectedToDPR)
       .width()
-      .toInt();
+      .toUnsigned();
 }
 
-int HTMLImageElement::naturalHeight() const {
+unsigned HTMLImageElement::naturalHeight() const {
   if (!imageLoader().image())
     return 0;
 
@@ -503,7 +503,7 @@
                   m_imageDevicePixelRatio,
                   ImageResource::IntrinsicCorrectedToDPR)
       .height()
-      .toInt();
+      .toUnsigned();
 }
 
 const String& HTMLImageElement::currentSrc() const {
@@ -544,8 +544,8 @@
   return !equalIgnoringCase(getAttribute(draggableAttr), "false");
 }
 
-void HTMLImageElement::setHeight(int value) {
-  setIntegralAttribute(heightAttr, value);
+void HTMLImageElement::setHeight(unsigned value) {
+  setUnsignedIntegralAttribute(heightAttr, value);
 }
 
 KURL HTMLImageElement::src() const {
@@ -556,8 +556,8 @@
   setAttribute(srcAttr, AtomicString(value));
 }
 
-void HTMLImageElement::setWidth(int value) {
-  setIntegralAttribute(widthAttr, value);
+void HTMLImageElement::setWidth(unsigned value) {
+  setUnsignedIntegralAttribute(widthAttr, value);
 }
 
 int HTMLImageElement::x() const {
diff --git a/third_party/WebKit/Source/core/html/HTMLImageElement.h b/third_party/WebKit/Source/core/html/HTMLImageElement.h
index b093a00..1bc5c0cd 100644
--- a/third_party/WebKit/Source/core/html/HTMLImageElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLImageElement.h
@@ -56,19 +56,19 @@
                                   HTMLFormElement*,
                                   bool createdByParser);
   static HTMLImageElement* createForJSConstructor(Document&);
-  static HTMLImageElement* createForJSConstructor(Document&, int width);
+  static HTMLImageElement* createForJSConstructor(Document&, unsigned width);
   static HTMLImageElement* createForJSConstructor(Document&,
-                                                  int width,
-                                                  int height);
+                                                  unsigned width,
+                                                  unsigned height);
 
   ~HTMLImageElement() override;
   DECLARE_VIRTUAL_TRACE();
 
-  int width();
-  int height();
+  unsigned width();
+  unsigned height();
 
-  int naturalWidth() const;
-  int naturalHeight() const;
+  unsigned naturalWidth() const;
+  unsigned naturalHeight() const;
   const String& currentSrc() const;
 
   bool isServerMap() const;
@@ -80,12 +80,12 @@
 
   void setLoadingImageDocument() { imageLoader().setLoadingImageDocument(); }
 
-  void setHeight(int);
+  void setHeight(unsigned);
 
   KURL src() const;
   void setSrc(const String&);
 
-  void setWidth(int);
+  void setWidth(unsigned);
 
   int x() const;
   int y() const;
diff --git a/third_party/WebKit/Source/core/html/HTMLImageElement.idl b/third_party/WebKit/Source/core/html/HTMLImageElement.idl
index ddad655..9b95061f5 100644
--- a/third_party/WebKit/Source/core/html/HTMLImageElement.idl
+++ b/third_party/WebKit/Source/core/html/HTMLImageElement.idl
@@ -20,11 +20,10 @@
 
 // https://html.spec.whatwg.org/#the-img-element
 
-// TODO(foolip): All long types in this interfaces should be unsigned long.
 [
     ActiveScriptWrappable,
     ConstructorCallWith=Document,
-    NamedConstructor=Image(optional long width, optional long height),
+    NamedConstructor=Image(optional unsigned long width, optional unsigned long height),
 ] interface HTMLImageElement : HTMLElement {
     [CEReactions, Reflect] attribute DOMString alt;
     [CEReactions, Reflect, URL] attribute DOMString src;
@@ -33,10 +32,10 @@
     [CEReactions, Reflect, ReflectOnly=("anonymous","use-credentials"), ReflectEmpty="anonymous", ReflectInvalid="anonymous"] attribute DOMString? crossOrigin;
     [CEReactions, Reflect] attribute DOMString useMap;
     [CEReactions, Reflect] attribute boolean isMap;
-    [CEReactions] attribute long width;
-    [CEReactions] attribute long height;
-    readonly attribute long naturalWidth;
-    readonly attribute long naturalHeight;
+    [CEReactions] attribute unsigned long width;
+    [CEReactions] attribute unsigned long height;
+    readonly attribute unsigned long naturalWidth;
+    readonly attribute unsigned long naturalHeight;
     readonly attribute boolean complete;
     readonly attribute DOMString currentSrc;
     [CEReactions, Reflect, ReflectOnly=("","no-referrer","origin","no-referrer-when-downgrade","origin-when-cross-origin","unsafe-url"), ReflectMissing="", ReflectInvalid=""] attribute DOMString referrerPolicy;
diff --git a/third_party/WebKit/Source/core/input/EventHandler.cpp b/third_party/WebKit/Source/core/input/EventHandler.cpp
index 61d9d0b8..96af9d6 100644
--- a/third_party/WebKit/Source/core/input/EventHandler.cpp
+++ b/third_party/WebKit/Source/core/input/EventHandler.cpp
@@ -277,8 +277,10 @@
     return result;
 
   m_frame->contentLayoutItem().hitTest(result);
-  if (!request.readOnly())
-    m_frame->document()->updateHoverActiveState(request, result.innerElement());
+  if (!request.readOnly()) {
+    m_frame->document()->updateHoverActiveState(request, result.innerElement(),
+                                                result.scrollbar());
+  }
 
   return result;
 }
@@ -528,6 +530,10 @@
 OptionalCursor EventHandler::selectAutoCursor(const HitTestResult& result,
                                               Node* node,
                                               const Cursor& iBeam) {
+  if (result.scrollbar()) {
+    return pointerCursor();
+  }
+
   bool editable = (node && hasEditableStyle(*node));
 
   const bool isOverLink =
@@ -810,7 +816,7 @@
   // So we must force the hit-test to fail, while still clearing hover/active
   // state.
   if (forceLeave) {
-    m_frame->document()->updateHoverActiveState(request, 0);
+    m_frame->document()->updateHoverActiveState(request, nullptr, false);
   } else {
     mev = EventHandlingUtil::performMouseEventHitTest(m_frame, request,
                                                       mouseEvent);
@@ -1524,13 +1530,13 @@
       // If the old hovered frame is different from the new hovered frame.
       // we should clear the old hovered node from the old hovered frame.
       if (newHoverFrame != oldHoverFrame)
-        doc->updateHoverActiveState(request, nullptr);
+        doc->updateHoverActiveState(request, nullptr, false);
     }
   }
 
   // Recursively set the new active/hover states on every frame in the chain of
   // innerElement.
-  m_frame->document()->updateHoverActiveState(request, innerElement);
+  m_frame->document()->updateHoverActiveState(request, innerElement, false);
 }
 
 // Update the mouseover/mouseenter/mouseout/mouseleave events across all frames
@@ -1856,7 +1862,8 @@
   HitTestRequest request(HitTestRequest::Active);
   HitTestResult result(request, locationInRootFrame);
   result.setInnerNode(targetNode);
-  doc->updateHoverActiveState(request, result.innerElement());
+  doc->updateHoverActiveState(request, result.innerElement(),
+                              result.scrollbar());
 
   // The contextmenu event is a mouse event even when invoked using the
   // keyboard.  This is required for web compatibility.
@@ -1922,8 +1929,8 @@
                            view->rootFrameToContents(
                                m_mouseEventManager->lastKnownMousePosition()));
       layoutItem.hitTest(result);
-      m_frame->document()->updateHoverActiveState(request,
-                                                  result.innerElement());
+      m_frame->document()->updateHoverActiveState(
+          request, result.innerElement(), result.scrollbar());
     }
   }
 }
@@ -1937,8 +1944,8 @@
     // m_lastDeferredTapElement.get() == m_frame->document()->activeElement()
     HitTestRequest request(HitTestRequest::TouchEvent |
                            HitTestRequest::Release);
-    m_frame->document()->updateHoverActiveState(request,
-                                                m_lastDeferredTapElement.get());
+    m_frame->document()->updateHoverActiveState(
+        request, m_lastDeferredTapElement.get(), false);
   }
   m_lastDeferredTapElement = nullptr;
 }
diff --git a/third_party/WebKit/Source/core/input/TouchEventManager.cpp b/third_party/WebKit/Source/core/input/TouchEventManager.cpp
index 69c4190..6880e9f 100644
--- a/third_party/WebKit/Source/core/input/TouchEventManager.cpp
+++ b/third_party/WebKit/Source/core/input/TouchEventManager.cpp
@@ -94,6 +94,7 @@
   m_regionForTouchID.clear();
   m_touchPressed = false;
   m_currentEvent = PlatformEvent::NoType;
+  m_currentTouchAction = TouchActionAuto;
 }
 
 DEFINE_TRACE(TouchEventManager) {
@@ -167,6 +168,7 @@
 
   if (allTouchesReleased) {
     m_touchSequenceDocument.clear();
+    m_currentTouchAction = TouchActionAuto;
   }
 
   WebInputEventResult eventResult = WebInputEventResult::NotHandled;
@@ -182,13 +184,14 @@
         static_cast<PlatformTouchPoint::TouchState>(state)));
     for (const auto& eventTarget : changedTouches[state].m_targets) {
       EventTarget* touchEventTarget = eventTarget;
-      TouchEvent* touchEvent = TouchEvent::create(
-          touches, touchesByTarget.get(touchEventTarget),
-          changedTouches[state].m_touches.get(), eventName,
-          touchEventTarget->toNode()->document().domWindow(),
-          event.getModifiers(), event.cancelable(),
-          event.causesScrollingIfUncanceled(),
-          event.touchStartOrFirstTouchMove(), event.timestamp());
+      TouchEvent* touchEvent =
+          TouchEvent::create(touches, touchesByTarget.get(touchEventTarget),
+                             changedTouches[state].m_touches.get(), eventName,
+                             touchEventTarget->toNode()->document().domWindow(),
+                             event.getModifiers(), event.cancelable(),
+                             event.causesScrollingIfUncanceled(),
+                             event.touchStartOrFirstTouchMove(),
+                             event.timestamp(), m_currentTouchAction);
 
       DispatchEventResult domDispatchResult =
           touchEventTarget->dispatchEvent(touchEvent);
@@ -333,8 +336,13 @@
 
       TouchAction effectiveTouchAction =
           TouchActionUtil::computeEffectiveTouchAction(*touchInfo.touchNode);
-      if (effectiveTouchAction != TouchActionAuto)
+      if (effectiveTouchAction != TouchActionAuto) {
         m_frame->page()->chromeClient().setTouchAction(effectiveTouchAction);
+
+        // Combine the current touch action sequence with the touch action
+        // for the current finger press.
+        m_currentTouchAction &= effectiveTouchAction;
+      }
     }
   }
 }
diff --git a/third_party/WebKit/Source/core/input/TouchEventManager.h b/third_party/WebKit/Source/core/input/TouchEventManager.h
index e6c7d5e8..7504490 100644
--- a/third_party/WebKit/Source/core/input/TouchEventManager.h
+++ b/third_party/WebKit/Source/core/input/TouchEventManager.h
@@ -96,6 +96,10 @@
   bool m_touchPressed;
   // The touch event currently being handled or NoType if none.
   PlatformEvent::EventType m_currentEvent;
+
+  // The current touch action, computed on each touch start and is
+  // a union of all touches. Reset when all touches are released.
+  TouchAction m_currentTouchAction;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/inspector/InspectorDOMDebuggerAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorDOMDebuggerAgent.cpp
index 606b0df..1b467b0c 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorDOMDebuggerAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorDOMDebuggerAgent.cpp
@@ -216,7 +216,8 @@
         continue;
       bool useCapture = listeners->at(k).capture();
       eventInformation.append(V8EventListenerInfo(
-          type, useCapture, listeners->at(k).passive(), handler,
+          type, useCapture, listeners->at(k).passive(), listeners->at(k).once(),
+          handler,
           createRemoveFunction(context, value, handler, type, useCapture)));
     }
   }
@@ -525,6 +526,7 @@
           .setType(info.eventType)
           .setUseCapture(info.useCapture)
           .setPassive(info.passive)
+          .setOnce(info.once)
           .setScriptId(scriptId)
           .setLineNumber(lineNumber)
           .setColumnNumber(columnNumber)
diff --git a/third_party/WebKit/Source/core/inspector/InspectorLogAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorLogAgent.cpp
index daf96bf..d2c295e 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorLogAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorLogAgent.cpp
@@ -14,7 +14,7 @@
 
 namespace LogAgentState {
 static const char logEnabled[] = "logEnabled";
-static const char logViolationsEnabled[] = "logViolationsEnabled";
+static const char logViolations[] = "logViolations";
 }
 
 namespace {
@@ -67,6 +67,8 @@
 
 }  // namespace
 
+using protocol::Log::ViolationSetting;
+
 InspectorLogAgent::InspectorLogAgent(ConsoleMessageStorage* storage,
                                      PerformanceMonitor* performanceMonitor)
     : m_enabled(false),
@@ -79,14 +81,19 @@
   visitor->trace(m_storage);
   visitor->trace(m_performanceMonitor);
   InspectorBaseAgent::trace(visitor);
+  PerformanceMonitor::Client::trace(visitor);
 }
 
 void InspectorLogAgent::restore() {
   if (!m_state->booleanProperty(LogAgentState::logEnabled, false))
     return;
   enable();
-  if (m_state->booleanProperty(LogAgentState::logViolationsEnabled, false))
-    setReportViolationsEnabled(true);
+  protocol::Value* config = m_state->get(LogAgentState::logViolations);
+  if (config) {
+    protocol::ErrorSupport errors;
+    startViolationsReport(
+        protocol::Array<ViolationSetting>::parse(config, &errors));
+  }
 }
 
 void InspectorLogAgent::consoleMessageAdded(ConsoleMessage* message) {
@@ -146,6 +153,7 @@
   if (!m_enabled)
     return Response::OK();
   m_state->setBoolean(LogAgentState::logEnabled, false);
+  stopViolationsReport();
   m_enabled = false;
   m_instrumentingAgents->removeInspectorLogAgent(this);
   return Response::OK();
@@ -156,13 +164,72 @@
   return Response::OK();
 }
 
-Response InspectorLogAgent::setReportViolationsEnabled(bool enabled) {
+static PerformanceMonitor::Violation parseViolation(const String& name) {
+  if (name == ViolationSetting::NameEnum::LongTask)
+    return PerformanceMonitor::kLongTask;
+  if (name == ViolationSetting::NameEnum::LongLayout)
+    return PerformanceMonitor::kLongLayout;
+  if (name == ViolationSetting::NameEnum::BlockedEvent)
+    return PerformanceMonitor::kBlockedEvent;
+  return PerformanceMonitor::kAfterLast;
+}
+
+Response InspectorLogAgent::startViolationsReport(
+    std::unique_ptr<protocol::Array<ViolationSetting>> settings) {
   if (!m_enabled)
     return Response::Error("Log is not enabled");
-  m_state->setBoolean(LogAgentState::logViolationsEnabled, enabled);
-  if (m_performanceMonitor)
-    m_performanceMonitor->setLoggingEnabled(enabled);
+  m_state->setValue(LogAgentState::logViolations, settings->serialize());
+  if (!m_performanceMonitor)
+    return Response::Error("Violations are not supported for this target");
+  m_performanceMonitor->unsubscribeAll(this);
+  for (size_t i = 0; i < settings->length(); ++i) {
+    PerformanceMonitor::Violation violation =
+        parseViolation(settings->get(i)->getName());
+    if (violation == PerformanceMonitor::kAfterLast)
+      continue;
+    m_performanceMonitor->subscribe(
+        violation, settings->get(i)->getThreshold() / 1000, this);
+  }
   return Response::OK();
 }
 
+Response InspectorLogAgent::stopViolationsReport() {
+  m_state->remove(LogAgentState::logViolations);
+  if (!m_performanceMonitor)
+    return Response::Error("Violations are not supported for this target");
+  m_performanceMonitor->unsubscribeAll(this);
+  return Response::OK();
+}
+
+void InspectorLogAgent::reportLongTask(
+    double startTime,
+    double endTime,
+    const HeapHashSet<Member<Frame>>& contextFrames) {
+  double time = (endTime - startTime) * 1000;
+  String messageText =
+      String::format("Long running JavaScript task took %ldms.", lround(time));
+  ConsoleMessage* message = ConsoleMessage::create(
+      ViolationMessageSource, WarningMessageLevel, messageText);
+  consoleMessageAdded(message);
+}
+
+void InspectorLogAgent::reportLongLayout(double duration) {
+  String messageText =
+      String::format("Forced reflow while executing JavaScript took %ldms.",
+                     lround(duration * 1000));
+  ConsoleMessage* message = ConsoleMessage::create(
+      ViolationMessageSource, WarningMessageLevel, messageText);
+  consoleMessageAdded(message);
+}
+
+void InspectorLogAgent::reportGenericViolation(PerformanceMonitor::Violation,
+                                               const String& text,
+                                               double time,
+                                               SourceLocation* location) {
+  ConsoleMessage* message =
+      ConsoleMessage::create(ViolationMessageSource, WarningMessageLevel, text,
+                             location ? location->clone() : nullptr);
+  consoleMessageAdded(message);
+};
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/inspector/InspectorLogAgent.h b/third_party/WebKit/Source/core/inspector/InspectorLogAgent.h
index d9de42d8..d4ef9bf 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorLogAgent.h
+++ b/third_party/WebKit/Source/core/inspector/InspectorLogAgent.h
@@ -6,6 +6,7 @@
 #define InspectorLogAgent_h
 
 #include "core/CoreExport.h"
+#include "core/frame/PerformanceMonitor.h"
 #include "core/inspector/InspectorBaseAgent.h"
 #include "core/inspector/protocol/Log.h"
 
@@ -13,10 +14,11 @@
 
 class ConsoleMessage;
 class ConsoleMessageStorage;
-class PerformanceMonitor;
 
 class CORE_EXPORT InspectorLogAgent
-    : public InspectorBaseAgent<protocol::Log::Metainfo> {
+    : public InspectorBaseAgent<protocol::Log::Metainfo>,
+      public PerformanceMonitor::Client {
+  USING_GARBAGE_COLLECTED_MIXIN(InspectorLogAgent);
   WTF_MAKE_NONCOPYABLE(InspectorLogAgent);
 
  public:
@@ -33,9 +35,22 @@
   Response enable() override;
   Response disable() override;
   Response clear() override;
-  Response setReportViolationsEnabled(bool) override;
+  Response startViolationsReport(
+      std::unique_ptr<protocol::Array<protocol::Log::ViolationSetting>>)
+      override;
+  Response stopViolationsReport() override;
 
  private:
+  // PerformanceMonitor::Client implementation.
+  void reportLongTask(double startTime,
+                      double endTime,
+                      const HeapHashSet<Member<Frame>>& contextFrames) override;
+  void reportLongLayout(double duration) override;
+  void reportGenericViolation(PerformanceMonitor::Violation,
+                              const String& text,
+                              double time,
+                              SourceLocation*) override;
+
   bool m_enabled;
   Member<ConsoleMessageStorage> m_storage;
   Member<PerformanceMonitor> m_performanceMonitor;
diff --git a/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp b/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp
index 72e00c6b..56dffbb 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp
@@ -15,6 +15,7 @@
 #include "core/fetch/CSSStyleSheetResource.h"
 #include "core/frame/FrameView.h"
 #include "core/frame/LocalFrame.h"
+#include "core/html/HTMLFrameOwnerElement.h"
 #include "core/inspector/IdentifiersFactory.h"
 #include "core/layout/HitTestResult.h"
 #include "core/layout/LayoutImage.h"
@@ -784,22 +785,37 @@
 
 std::unique_ptr<TracedValue> frameEventData(LocalFrame* frame) {
   std::unique_ptr<TracedValue> value = TracedValue::create();
-  value->setString("frame", toHexString(frame));
   bool isMainFrame = frame && frame->isMainFrame();
   value->setBoolean("isMainFrame", isMainFrame);
   value->setString("page", toHexString(frame->localFrameRoot()));
   return value;
 }
 
-std::unique_ptr<TracedValue> InspectorCommitLoadEvent::data(LocalFrame* frame) {
-  std::unique_ptr<TracedValue> frameData = frameEventData(frame);
+void fillCommonFrameData(TracedValue* frameData, LocalFrame* frame) {
+  frameData->setString("frame", toHexString(frame));
   frameData->setString("url", urlForFrame(frame));
   frameData->setString("name", frame->tree().name());
+
+  FrameOwner* owner = frame->owner();
+  if (owner && owner->isLocal()) {
+    frameData->setInteger(
+        "nodeId", DOMNodeIds::idForNode(toHTMLFrameOwnerElement(owner)));
+  }
+  Frame* parent = frame->tree().parent();
+  if (parent && parent->isLocalFrame())
+    frameData->setString("parent", toHexString(parent));
+}
+
+std::unique_ptr<TracedValue> InspectorCommitLoadEvent::data(LocalFrame* frame) {
+  std::unique_ptr<TracedValue> frameData = frameEventData(frame);
+  fillCommonFrameData(frameData.get(), frame);
   return frameData;
 }
 
 std::unique_ptr<TracedValue> InspectorMarkLoadEvent::data(LocalFrame* frame) {
-  return frameEventData(frame);
+  std::unique_ptr<TracedValue> frameData = frameEventData(frame);
+  frameData->setString("frame", toHexString(frame));
+  return frameData;
 }
 
 std::unique_ptr<TracedValue> InspectorScrollLayerEvent::data(
@@ -982,9 +998,7 @@
     if (!f->isLocalFrame())
       continue;
     value->beginDictionary();
-    value->setString("frame", toHexString(f));
-    value->setString("url", urlForFrame(toLocalFrame(f)));
-    value->setString("name", f->tree().name());
+    fillCommonFrameData(value.get(), toLocalFrame(f));
     value->endDictionary();
   }
   value->endArray();
diff --git a/third_party/WebKit/Source/core/inspector/ThreadDebugger.cpp b/third_party/WebKit/Source/core/inspector/ThreadDebugger.cpp
index f3e1edfd..8bcebd03 100644
--- a/third_party/WebKit/Source/core/inspector/ThreadDebugger.cpp
+++ b/third_party/WebKit/Source/core/inspector/ThreadDebugger.cpp
@@ -414,6 +414,8 @@
                        v8::Boolean::New(isolate, info.useCapture));
     createDataProperty(context, listenerObject, v8String(isolate, "passive"),
                        v8::Boolean::New(isolate, info.passive));
+    createDataProperty(context, listenerObject, v8String(isolate, "once"),
+                       v8::Boolean::New(isolate, info.once));
     createDataProperty(context, listenerObject, v8String(isolate, "type"),
                        v8String(isolate, currentEventType));
     v8::Local<v8::Function> removeFunction;
diff --git a/third_party/WebKit/Source/core/inspector/browser_protocol.json b/third_party/WebKit/Source/core/inspector/browser_protocol.json
index 1b318bb..554556b 100644
--- a/third_party/WebKit/Source/core/inspector/browser_protocol.json
+++ b/third_party/WebKit/Source/core/inspector/browser_protocol.json
@@ -3205,6 +3205,7 @@
                     { "name": "type", "type": "string", "description": "<code>EventListener</code>'s type." },
                     { "name": "useCapture", "type": "boolean", "description": "<code>EventListener</code>'s useCapture." },
                     { "name": "passive", "type": "boolean", "description": "<code>EventListener</code>'s passive flag." },
+                    { "name": "once", "type": "boolean", "description": "<code>EventListener</code>'s once flag." },
                     { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Script id of the handler code." },
                     { "name": "lineNumber", "type": "integer", "description": "Line number in the script (0-based)." },
                     { "name": "columnNumber", "type": "integer", "description": "Column number in the script (0-based)." },
@@ -4374,6 +4375,15 @@
                     { "name": "networkRequestId", "$ref": "Network.RequestId", "optional": true, "description": "Identifier of the network request associated with this entry." },
                     { "name": "workerId", "type": "string", "optional": true, "description": "Identifier of the worker associated with this entry." }
                 ]
+            },
+            {
+                "id": "ViolationSetting",
+                "type": "object",
+                "description": "Violation configuration setting.",
+                "properties": [
+                    { "name": "name", "type": "string", "enum": ["longTask", "longLayout", "blockedEvent"], "description": "Violation type." },
+                    { "name": "threshold", "type": "number", "description": "Time threshold to trigger upon." }
+                ]
             }
         ],
         "commands": [
@@ -4390,11 +4400,15 @@
                 "description": "Clears the log."
             },
             {
-                "name": "setReportViolationsEnabled",
+                "name": "startViolationsReport",
                 "parameters": [
-                    { "name": "enabled", "type": "boolean", "description": "Pass true to enable." }
+                    { "name": "config", "type": "array", "items": { "$ref": "ViolationSetting" }, "description": "Configuration for violations." }
                 ],
-                "description": "Reports runtime audit violations as log entries."
+                "description": "start violation reporting."
+            },
+            {
+                "name": "stopViolationsReport",
+                "description": "Stop violation reporting."
             }
         ],
         "events": [
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.cpp b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
index a23fb37..05643de 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
@@ -128,6 +128,7 @@
   unsigned m_bitfields2;
   LayoutRect m_visualRect;
   LayoutPoint m_paintOffset;
+  std::unique_ptr<void*> m_paintProperties;
 };
 
 static_assert(sizeof(LayoutObject) == sizeof(SameSizeAsLayoutObject),
@@ -135,15 +136,6 @@
 
 bool LayoutObject::s_affectsParentBlock = false;
 
-// The pointer to paint properties is implemented as a global hash map
-// temporarily, to avoid memory regression during the transition towards SPv2.
-typedef HashMap<const LayoutObject*, std::unique_ptr<ObjectPaintProperties>>
-    PaintPropertiesMap;
-static PaintPropertiesMap& paintPropertiesMap() {
-  DEFINE_STATIC_LOCAL(PaintPropertiesMap, staticPaintPropertiesMap, ());
-  return staticPaintPropertiesMap;
-}
-
 void* LayoutObject::operator new(size_t sz) {
   ASSERT(isMainThread());
   return partitionAlloc(WTF::Partitions::layoutPartition(), sz,
@@ -1477,6 +1469,11 @@
       // for changed paint property or paint order. Raster invalidation will be
       // issued if needed during paint.
       ObjectPaintInvalidator(*this).slowSetPaintingLayerNeedsRepaint();
+
+      // When transform, opacity, etc. change, paint properties will also change
+      // so we need to mark this object as needing an update.
+      // TODO(pdr): Also update in the non-spv2 codepath?
+      getMutableForPainting().setNeedsPaintPropertyUpdate();
     }
   } else {
     // If transform changed, and the layer does not paint into its own separate
@@ -2578,9 +2575,6 @@
 
   ObjectPaintInvalidator::objectWillBeDestroyed(*this);
 
-  if (RuntimeEnabledFeatures::slimmingPaintV2Enabled())
-    paintPropertiesMap().remove(this);
-
   clearLayoutRootIfNeeded();
 
   if (m_style) {
@@ -3498,16 +3492,14 @@
 
 const ObjectPaintProperties* LayoutObject::paintProperties() const {
   DCHECK(RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled());
-  return paintPropertiesMap().get(this);
+  return m_paintProperties.get();
 }
 
 ObjectPaintProperties& LayoutObject::ensurePaintProperties() {
   DCHECK(RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled());
-  auto addResult = paintPropertiesMap().add(this, nullptr);
-  if (addResult.isNewEntry)
-    addResult.storedValue->value = ObjectPaintProperties::create();
-
-  return *addResult.storedValue->value;
+  if (!m_paintProperties)
+    m_paintProperties = ObjectPaintProperties::create();
+  return *m_paintProperties;
 }
 
 LayoutRect LayoutObject::debugRect() const {
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.h b/third_party/WebKit/Source/core/layout/LayoutObject.h
index 7e254c6..e92c28f 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.h
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.h
@@ -1702,6 +1702,13 @@
     void clearPreviousVisualRects() {
       m_layoutObject.clearPreviousVisualRects();
     }
+    void setNeedsPaintPropertyUpdate() {
+      m_layoutObject.setNeedsPaintPropertyUpdate();
+    }
+    void clearNeedsPaintPropertyUpdate() {
+      m_layoutObject.clearNeedsPaintPropertyUpdate();
+    }
+
    protected:
     friend class PaintPropertyTreeBuilder;
     // The following two functions can be called from PaintPropertyTreeBuilder
@@ -1724,6 +1731,23 @@
     return MutableForPainting(*this);
   }
 
+  // Paint properties (see: |ObjectPaintProperties|) are built from an object's
+  // state (location, transform, etc) as well as properties from ancestors.
+  // When these inputs change, setNeedsPaintPropertyUpdate will cause a property
+  // tree update during the next document lifecycle update.
+  // TODO(pdr): Add additional granularity such as the ability to signal that
+  // only a local paint property update is needed.
+  void setNeedsPaintPropertyUpdate() {
+    m_bitfields.setNeedsPaintPropertyUpdate(true);
+  }
+  void clearNeedsPaintPropertyUpdate() {
+    DCHECK_EQ(document().lifecycle().state(), DocumentLifecycle::InPrePaint);
+    m_bitfields.setNeedsPaintPropertyUpdate(false);
+  }
+  bool needsPaintPropertyUpdate() const {
+    return m_bitfields.needsPaintPropertyUpdate();
+  }
+
   void setIsScrollAnchorObject() { m_bitfields.setIsScrollAnchorObject(true); }
   // Clears the IsScrollAnchorObject bit if and only if no ScrollAnchors still
   // reference this LayoutObject.
@@ -2110,6 +2134,7 @@
           m_hasPreviousLocationInBacking(false),
           m_hasPreviousSelectionVisualRect(false),
           m_hasPreviousBoxGeometries(false),
+          m_needsPaintPropertyUpdate(true),
           m_positionedState(IsStaticallyPositioned),
           m_selectionState(SelectionNone),
           m_backgroundObscurationState(BackgroundObscurationStatusInvalid),
@@ -2278,9 +2303,13 @@
                          HasPreviousSelectionVisualRect);
     ADD_BOOLEAN_BITFIELD(hasPreviousBoxGeometries, HasPreviousBoxGeometries);
 
+    // Whether the paint properties need to be updated. For more details, see
+    // LayoutObject::needsPaintPropertyUpdate().
+    ADD_BOOLEAN_BITFIELD(needsPaintPropertyUpdate, NeedsPaintPropertyUpdate);
+
    protected:
     // Use protected to avoid warning about unused variable.
-    unsigned m_unusedBits : 10;
+    unsigned m_unusedBits : 9;
 
    private:
     // This is the cached 'position' value of this object
@@ -2380,6 +2409,10 @@
   // paint offset change for paint invalidation on SPv2, and partial paint
   // property tree update for SlimmingPaintInvalidation on SPv1 and SPv2.
   LayoutPoint m_previousPaintOffset;
+
+  // For SPv2 only. The ObjectPaintProperties structure holds references to the
+  // property tree nodes that are created by the layout object for painting.
+  std::unique_ptr<ObjectPaintProperties> m_paintProperties;
 };
 
 // FIXME: remove this once the layout object lifecycle ASSERTS are no longer
diff --git a/third_party/WebKit/Source/core/layout/LayoutTable.cpp b/third_party/WebKit/Source/core/layout/LayoutTable.cpp
index 6f0b62e..44373b79 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTable.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTable.cpp
@@ -752,7 +752,7 @@
 }
 
 void LayoutTable::invalidateCollapsedBorders() {
-  m_collapsedBorders.clear();
+  m_collapsedBordersInfo = nullptr;
   if (!collapseBorders())
     return;
 
@@ -765,10 +765,15 @@
 // cache of its containing section, and invalidates itself if any border
 // changes. This method doesn't affect layout.
 void LayoutTable::recalcCollapsedBordersIfNeeded() {
-  if (m_collapsedBordersValid || !collapseBorders())
+  if (m_collapsedBordersValid)
     return;
   m_collapsedBordersValid = true;
-  m_collapsedBorders.clear();
+  m_collapsedBordersInfo = nullptr;
+  if (!collapseBorders())
+    return;
+
+  LayoutRect boundsOfChangedCells;
+  Vector<CollapsedBorderValue> values;
   for (LayoutObject* section = firstChild(); section;
        section = section->nextSibling()) {
     if (!section->isTableSection())
@@ -777,12 +782,23 @@
          row = row->nextRow()) {
       for (LayoutTableCell* cell = row->firstCell(); cell;
            cell = cell->nextCell()) {
-        ASSERT(cell->table() == this);
-        cell->collectBorderValues(m_collapsedBorders);
+        DCHECK(cell->table() == this);
+        if (cell->collectBorderValues(values) &&
+            !shouldDoFullPaintInvalidation()) {
+          LayoutRect cellRect = cell->localVisualRect();
+          cell->mapToVisualRectInAncestorSpace(this, cellRect);
+          boundsOfChangedCells.unite(cellRect);
+        }
       }
     }
   }
-  LayoutTableCell::sortBorderValues(m_collapsedBorders);
+  if (!values.isEmpty()) {
+    LayoutTableCell::sortBorderValues(values);
+    m_collapsedBordersInfo =
+        wrapUnique(new CollapsedBordersInfo(std::move(values)));
+  }
+
+  invalidatePaintRectangle(boundsOfChangedCells);
 }
 
 void LayoutTable::addOverflowFromChildren() {
@@ -1662,10 +1678,10 @@
 
 PaintInvalidationReason LayoutTable::invalidatePaintIfNeeded(
     const PaintInvalidationState& paintInvalidationState) {
-  if (collapseBorders() && !m_collapsedBorders.isEmpty())
+  if (hasCollapsedBorders()) {
     paintInvalidationState.paintingLayer()
         .setNeedsPaintPhaseDescendantBlockBackgrounds();
-
+  }
   return LayoutBlock::invalidatePaintIfNeeded(paintInvalidationState);
 }
 
diff --git a/third_party/WebKit/Source/core/layout/LayoutTable.h b/third_party/WebKit/Source/core/layout/LayoutTable.h
index adc7a6b..5f1bcde 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTable.h
+++ b/third_party/WebKit/Source/core/layout/LayoutTable.h
@@ -29,7 +29,9 @@
 #include "core/CSSPropertyNames.h"
 #include "core/CoreExport.h"
 #include "core/layout/LayoutBlock.h"
+#include "core/paint/PaintResult.h"
 #include "core/style/CollapsedBorderValue.h"
+#include "platform/graphics/paint/CullRect.h"
 #include "wtf/Vector.h"
 #include <memory>
 
@@ -375,7 +377,6 @@
   LayoutTableCell* cellBefore(const LayoutTableCell*) const;
   LayoutTableCell* cellAfter(const LayoutTableCell*) const;
 
-  typedef Vector<CollapsedBorderValue> CollapsedBorderValues;
   void invalidateCollapsedBorders();
 
   bool hasSections() const { return m_head || m_foot || m_firstBody; }
@@ -405,9 +406,23 @@
 
   void paintMask(const PaintInfo&, const LayoutPoint&) const final;
 
-  const CollapsedBorderValues& collapsedBorders() const {
-    ASSERT(m_collapsedBordersValid);
-    return m_collapsedBorders;
+  struct CollapsedBordersInfo {
+    explicit CollapsedBordersInfo(const Vector<CollapsedBorderValue>& values)
+        : values(std::move(values)) {}
+
+    PaintResult lastPaintResult = FullyPainted;
+    CullRect lastPaintRect;
+    const Vector<CollapsedBorderValue> values;
+  };
+
+  bool hasCollapsedBorders() const {
+    DCHECK(m_collapsedBordersValid);
+    DCHECK(!m_collapsedBordersInfo || collapseBorders());
+    return !!m_collapsedBordersInfo;
+  }
+  CollapsedBordersInfo& getCollapsedBordersInfo() const {
+    DCHECK(hasCollapsedBorders());
+    return *m_collapsedBordersInfo;
   }
 
   void subtractCaptionRect(LayoutRect&) const;
@@ -550,7 +565,7 @@
   // need to compare a cells border against all the adjoining cells, rows,
   // row groups, column, column groups and table. Thus we cache them in this
   // field.
-  CollapsedBorderValues m_collapsedBorders;
+  std::unique_ptr<CollapsedBordersInfo> m_collapsedBordersInfo;
   bool m_collapsedBordersValid : 1;
 
   mutable bool m_hasColElements : 1;
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableCell.cpp b/third_party/WebKit/Source/core/layout/LayoutTableCell.cpp
index b842de0d..e848406e 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableCell.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTableCell.cpp
@@ -66,34 +66,6 @@
   updateColAndRowSpanFlags();
 }
 
-LayoutTableCell::CollapsedBorderValues::CollapsedBorderValues(
-    const LayoutTable& layoutTable,
-    const CollapsedBorderValue& startBorder,
-    const CollapsedBorderValue& endBorder,
-    const CollapsedBorderValue& beforeBorder,
-    const CollapsedBorderValue& afterBorder)
-    : m_layoutTable(layoutTable),
-      m_startBorder(startBorder),
-      m_endBorder(endBorder),
-      m_beforeBorder(beforeBorder),
-      m_afterBorder(afterBorder) {}
-
-void LayoutTableCell::CollapsedBorderValues::setCollapsedBorderValues(
-    const CollapsedBorderValues& other) {
-  m_startBorder = other.startBorder();
-  m_endBorder = other.endBorder();
-  m_beforeBorder = other.beforeBorder();
-  m_afterBorder = other.afterBorder();
-}
-
-String LayoutTableCell::CollapsedBorderValues::debugName() const {
-  return "CollapsedBorderValues";
-}
-
-LayoutRect LayoutTableCell::CollapsedBorderValues::visualRect() const {
-  return m_layoutTable.visualRect();
-}
-
 void LayoutTableCell::willBeRemovedFromTree() {
   LayoutBlockFlow::willBeRemovedFromTree();
 
@@ -1280,7 +1252,7 @@
   TableCellPainter(*this).paint(paintInfo, paintOffset);
 }
 
-static void addBorderStyle(LayoutTable::CollapsedBorderValues& borderValues,
+static void addBorderStyle(Vector<CollapsedBorderValue>& borderValues,
                            CollapsedBorderValue borderValue) {
   if (!borderValue.isVisible())
     return;
@@ -1292,56 +1264,45 @@
   borderValues.append(borderValue);
 }
 
-void LayoutTableCell::collectBorderValues(
-    LayoutTable::CollapsedBorderValues& borderValues) {
-  CollapsedBorderValues newValues(
-      *table(), computeCollapsedStartBorder(), computeCollapsedEndBorder(),
-      computeCollapsedBeforeBorder(), computeCollapsedAfterBorder());
+bool LayoutTableCell::collectBorderValues(
+    Vector<CollapsedBorderValue>& borderValues) {
+  CollapsedBorderValues newValues = {
+      computeCollapsedStartBorder(), computeCollapsedEndBorder(),
+      computeCollapsedBeforeBorder(), computeCollapsedAfterBorder()};
 
   bool changed = false;
-  if (!newValues.startBorder().isVisible() &&
-      !newValues.endBorder().isVisible() &&
-      !newValues.beforeBorder().isVisible() &&
-      !newValues.afterBorder().isVisible()) {
+  if (!newValues.startBorder.isVisible() && !newValues.endBorder.isVisible() &&
+      !newValues.beforeBorder.isVisible() &&
+      !newValues.afterBorder.isVisible()) {
     changed = !!m_collapsedBorderValues;
     m_collapsedBorderValues = nullptr;
   } else if (!m_collapsedBorderValues) {
     changed = true;
-    m_collapsedBorderValues = wrapUnique(new CollapsedBorderValues(
-        *table(), newValues.startBorder(), newValues.endBorder(),
-        newValues.beforeBorder(), newValues.afterBorder()));
+    m_collapsedBorderValues = wrapUnique(new CollapsedBorderValues(newValues));
   } else {
     // We check visuallyEquals so that the table cell is invalidated only if a
     // changed collapsed border is visible in the first place.
-    changed = !m_collapsedBorderValues->startBorder().visuallyEquals(
-                  newValues.startBorder()) ||
-              !m_collapsedBorderValues->endBorder().visuallyEquals(
-                  newValues.endBorder()) ||
-              !m_collapsedBorderValues->beforeBorder().visuallyEquals(
-                  newValues.beforeBorder()) ||
-              !m_collapsedBorderValues->afterBorder().visuallyEquals(
-                  newValues.afterBorder());
+    changed = !m_collapsedBorderValues->startBorder.visuallyEquals(
+                  newValues.startBorder) ||
+              !m_collapsedBorderValues->endBorder.visuallyEquals(
+                  newValues.endBorder) ||
+              !m_collapsedBorderValues->beforeBorder.visuallyEquals(
+                  newValues.beforeBorder) ||
+              !m_collapsedBorderValues->afterBorder.visuallyEquals(
+                  newValues.afterBorder);
     if (changed)
-      m_collapsedBorderValues->setCollapsedBorderValues(newValues);
+      *m_collapsedBorderValues = newValues;
   }
 
-  // If collapsed borders changed, invalidate the cell's display item client on
-  // the table's backing.
-  // TODO(crbug.com/451090#c5): Need a way to invalidate/repaint the borders
-  // only.
-  if (changed)
-    ObjectPaintInvalidator(*table())
-        .slowSetPaintingLayerNeedsRepaintAndInvalidateDisplayItemClient(
-            *this, PaintInvalidationStyleChange);
-
-  addBorderStyle(borderValues, newValues.startBorder());
-  addBorderStyle(borderValues, newValues.endBorder());
-  addBorderStyle(borderValues, newValues.beforeBorder());
-  addBorderStyle(borderValues, newValues.afterBorder());
+  addBorderStyle(borderValues, newValues.startBorder);
+  addBorderStyle(borderValues, newValues.endBorder);
+  addBorderStyle(borderValues, newValues.beforeBorder);
+  addBorderStyle(borderValues, newValues.afterBorder);
+  return changed;
 }
 
 void LayoutTableCell::sortBorderValues(
-    LayoutTable::CollapsedBorderValues& borderValues) {
+    Vector<CollapsedBorderValue>& borderValues) {
   std::sort(borderValues.begin(), borderValues.end(), compareBorders);
 }
 
@@ -1416,23 +1377,6 @@
   return LayoutBlockFlow::backgroundIsKnownToBeOpaqueInRect(localRect);
 }
 
-bool LayoutTableCell::usesTableAsAdditionalDisplayItemClient() const {
-  // In certain cases such as collapsed borders for composited table cells we
-  // paint content for the cell into the table graphics layer backing and so
-  // must use the table's visual rect.
-  return (hasLayer() && layer()->compositingState() != NotComposited) ||
-         RuntimeEnabledFeatures::slimmingPaintV2Enabled();
-}
-
-void LayoutTableCell::invalidateDisplayItemClients(
-    PaintInvalidationReason reason) const {
-  if (m_collapsedBorderValues && usesTableAsAdditionalDisplayItemClient()) {
-    ObjectPaintInvalidator(*this).invalidateDisplayItemClient(
-        *m_collapsedBorderValues, reason);
-  }
-  LayoutBlockFlow::invalidateDisplayItemClients(reason);
-}
-
 // TODO(lunalu): Deliberately dump the "inner" box of table cells, since that
 // is what current results reflect.  We'd like to clean up the results to dump
 // both the outer box and the intrinsic padding so that both bits of information
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableCell.h b/third_party/WebKit/Source/core/layout/LayoutTableCell.h
index 6ce3209..0b0fd6a 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableCell.h
+++ b/third_party/WebKit/Source/core/layout/LayoutTableCell.h
@@ -185,8 +185,9 @@
   int borderBefore() const override;
   int borderAfter() const override;
 
-  void collectBorderValues(LayoutTable::CollapsedBorderValues&);
-  static void sortBorderValues(LayoutTable::CollapsedBorderValues&);
+  // Returns true if any collapsed borders related to this cell changed.
+  bool collectBorderValues(Vector<CollapsedBorderValue>&);
+  static void sortBorderValues(Vector<CollapsedBorderValue>&);
 
   void layout() override;
 
@@ -286,38 +287,14 @@
   const char* name() const override { return "LayoutTableCell"; }
 
   bool backgroundIsKnownToBeOpaqueInRect(const LayoutRect&) const override;
-  void invalidateDisplayItemClients(PaintInvalidationReason) const override;
 
-  // TODO(wkorman): Consider renaming to more clearly differentiate from
-  // CollapsedBorderValue.
-  class CollapsedBorderValues : public DisplayItemClient {
-   public:
-    CollapsedBorderValues(const LayoutTable&,
-                          const CollapsedBorderValue& startBorder,
-                          const CollapsedBorderValue& endBorder,
-                          const CollapsedBorderValue& beforeBorder,
-                          const CollapsedBorderValue& afterBorder);
-
-    const CollapsedBorderValue& startBorder() const { return m_startBorder; }
-    const CollapsedBorderValue& endBorder() const { return m_endBorder; }
-    const CollapsedBorderValue& beforeBorder() const { return m_beforeBorder; }
-    const CollapsedBorderValue& afterBorder() const { return m_afterBorder; }
-
-    void setCollapsedBorderValues(const CollapsedBorderValues& other);
-
-    // DisplayItemClient methods.
-    String debugName() const;
-    LayoutRect visualRect() const;
-
-   private:
-    const LayoutTable& m_layoutTable;
-    CollapsedBorderValue m_startBorder;
-    CollapsedBorderValue m_endBorder;
-    CollapsedBorderValue m_beforeBorder;
-    CollapsedBorderValue m_afterBorder;
+  struct CollapsedBorderValues {
+    CollapsedBorderValue startBorder;
+    CollapsedBorderValue endBorder;
+    CollapsedBorderValue beforeBorder;
+    CollapsedBorderValue afterBorder;
   };
 
-  bool usesTableAsAdditionalDisplayItemClient() const;
   const CollapsedBorderValues* collapsedBorderValues() const {
     return m_collapsedBorderValues.get();
   }
@@ -329,6 +306,8 @@
   // A table cell's location is relative to its containing section.
   LayoutBox* locationContainer() const override { return section(); }
 
+  LayoutRect localVisualRect() const override;
+
  protected:
   void styleDidChange(StyleDifference, const ComputedStyle* oldStyle) override;
   void computePreferredLogicalWidths() override;
@@ -352,7 +331,6 @@
   void paintMask(const PaintInfo&, const LayoutPoint&) const override;
 
   LayoutSize offsetFromContainer(const LayoutObject*) const override;
-  LayoutRect localVisualRect() const override;
 
   int borderHalfLeft(bool outer) const;
   int borderHalfRight(bool outer) const;
@@ -390,8 +368,8 @@
   // See also https://code.google.com/p/chromium/issues/detail?id=128227 for
   // some history.
   //
-  // Those functions are called when the cache (m_collapsedBorders) is
-  // invalidated on LayoutTable.
+  // Those functions are called before paint invalidation if the collapsed
+  // borders cache is invalidated on LayoutTable.
   CollapsedBorderValue computeCollapsedStartBorder(
       IncludeBorderColorOrNot = IncludeBorderColor) const;
   CollapsedBorderValue computeCollapsedEndBorder(
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableSection.h b/third_party/WebKit/Source/core/layout/LayoutTableSection.h
index 5c0b9f70..6c82c3ad 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableSection.h
+++ b/third_party/WebKit/Source/core/layout/LayoutTableSection.h
@@ -57,6 +57,13 @@
   unsigned m_end;
 };
 
+inline bool operator==(const CellSpan& a, const CellSpan& b) {
+  return a.start() == b.start() && a.end() == b.end();
+}
+inline bool operator!=(const CellSpan& a, const CellSpan& b) {
+  return !(a == b);
+}
+
 class LayoutTableCell;
 class LayoutTableRow;
 
@@ -296,8 +303,13 @@
   // columnPos vectors.
   LayoutRect logicalRectForWritingModeAndDirection(const LayoutRect&) const;
 
+  CellSpan fullTableRowSpan() const { return CellSpan(0, m_grid.size()); }
+  CellSpan fullTableEffectiveColumnSpan() const {
+    return CellSpan(0, table()->numEffectiveColumns());
+  }
   CellSpan dirtiedRows(const LayoutRect& visualRect) const;
   CellSpan dirtiedEffectiveColumns(const LayoutRect& visualRect) const;
+
   const HashSet<LayoutTableCell*>& overflowingCells() const {
     return m_overflowingCells;
   }
@@ -401,11 +413,6 @@
 
   void computeOverflowFromCells(unsigned totalRows, unsigned nEffCols);
 
-  CellSpan fullTableRowSpan() const { return CellSpan(0, m_grid.size()); }
-  CellSpan fullTableEffectiveColumnSpan() const {
-    return CellSpan(0, table()->numEffectiveColumns());
-  }
-
   // These two functions take a rectangle as input that has been flipped by
   // logicalRectForWritingModeAndDirection.
   // The returned span of rows or columns is end-exclusive, and empty if
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.h b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.h
index c83206a1..54f1541 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.h
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.h
@@ -28,6 +28,7 @@
 
 #include "core/layout/compositing/GraphicsLayerUpdater.h"
 #include "core/paint/PaintLayer.h"
+#include "core/paint/PaintLayerPaintingInfo.h"
 #include "platform/geometry/FloatPoint.h"
 #include "platform/geometry/FloatPoint3D.h"
 #include "platform/graphics/GraphicsLayer.h"
diff --git a/third_party/WebKit/Source/core/layout/ng/layout_ng_block_flow.cc b/third_party/WebKit/Source/core/layout/ng/layout_ng_block_flow.cc
index 1d2794d1..87528e64 100644
--- a/third_party/WebKit/Source/core/layout/ng/layout_ng_block_flow.cc
+++ b/third_party/WebKit/Source/core/layout/ng/layout_ng_block_flow.cc
@@ -11,7 +11,7 @@
 namespace blink {
 
 LayoutNGBlockFlow::LayoutNGBlockFlow(Element* element)
-    : LayoutBlockFlow(element), m_box(new NGBox(this)) {}
+    : LayoutBlockFlow(element) {}
 
 bool LayoutNGBlockFlow::isOfType(LayoutObjectType type) const {
   return type == LayoutObjectNGBlockFlow || LayoutBlockFlow::isOfType(type);
@@ -22,6 +22,12 @@
 
   const auto* constraint_space =
       NGConstraintSpace::CreateFromLayoutObject(*this);
+
+  // TODO(layout-dev): This should be created in the constructor once instead.
+  // There is some internal state which needs to be cleared between layout
+  // passes (probably FirstChild(), etc).
+  m_box = new NGBox(this);
+
   NGFragmentBase* fragment;
   while (!m_box->Layout(constraint_space, &fragment))
     ;
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
index b9d815f..0be79a6 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
@@ -150,7 +150,10 @@
   DCHECK(style_);
 }
 
-bool NGBlockLayoutAlgorithm::Layout(NGPhysicalFragmentBase** out) {
+NGLayoutStatus NGBlockLayoutAlgorithm::Layout(
+    NGFragmentBase*,
+    NGPhysicalFragmentBase** fragment_out,
+    NGLayoutAlgorithm**) {
   switch (state_) {
     case kStateInit: {
       border_and_padding_ =
@@ -197,20 +200,20 @@
         space_for_current_child_ = CreateConstraintSpaceForCurrentChild();
 
       state_ = kStateChildLayout;
-      return false;
+      return NotFinished;
     }
     case kStateChildLayout: {
       if (current_child_) {
         if (!LayoutCurrentChild())
-          return false;
+          return NotFinished;
         current_child_ = current_child_->NextSibling();
         if (current_child_) {
           space_for_current_child_ = CreateConstraintSpaceForCurrentChild();
-          return false;
+          return NotFinished;
         }
       }
       state_ = kStateFinalize;
-      return false;
+      return NotFinished;
     }
     case kStateFinalize: {
       content_size_ += border_and_padding_.block_end;
@@ -222,14 +225,14 @@
       builder_->SetBlockSize(block_size)
           .SetInlineOverflow(max_inline_size_)
           .SetBlockOverflow(content_size_);
-      *out = builder_->ToFragment();
+      *fragment_out = builder_->ToFragment();
       state_ = kStateInit;
-      return true;
+      return NewFragment;
     }
   };
   NOTREACHED();
-  *out = nullptr;
-  return true;
+  *fragment_out = nullptr;
+  return NewFragment;
 }
 
 bool NGBlockLayoutAlgorithm::LayoutCurrentChild() {
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h
index 45302e0..c6b7fb39 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h
@@ -34,7 +34,9 @@
                          NGConstraintSpace* space,
                          NGBreakToken* break_token = nullptr);
 
-  bool Layout(NGPhysicalFragmentBase**) override;
+  NGLayoutStatus Layout(NGFragmentBase*,
+                        NGPhysicalFragmentBase**,
+                        NGLayoutAlgorithm**) override;
 
   DECLARE_VIRTUAL_TRACE();
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc
index 1fd9c12..5c176cc 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc
@@ -35,7 +35,7 @@
                                               NGBox* first_child) {
     NGBlockLayoutAlgorithm algorithm(style_, first_child, space);
     NGPhysicalFragmentBase* frag;
-    while (!algorithm.Layout(&frag))
+    while (!algorithm.Layout(nullptr, &frag, nullptr))
       continue;
     return toNGPhysicalFragment(frag);
   }
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_box.cc b/third_party/WebKit/Source/core/layout/ng/ng_box.cc
index 6fd7f9f7..a47ba5f 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_box.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_box.cc
@@ -60,7 +60,7 @@
     }
 
     NGPhysicalFragmentBase* fragment = nullptr;
-    if (!layout_algorithm_->Layout(&fragment))
+    if (!layout_algorithm_->Layout(nullptr, &fragment, nullptr))
       return false;
     fragment_ = toNGPhysicalFragment(fragment);
 
@@ -128,7 +128,7 @@
 
   // Have to synthesize this value.
   NGPhysicalFragmentBase* physical_fragment;
-  while (!minmax_algorithm_->Layout(&physical_fragment))
+  while (!minmax_algorithm_->Layout(nullptr, &physical_fragment, nullptr))
     continue;
   NGFragment* fragment = new NGFragment(
       FromPlatformWritingMode(Style()->getWritingMode()), Style()->direction(),
@@ -148,7 +148,7 @@
 
   minmax_algorithm_ = new NGBlockLayoutAlgorithm(Style(), toNGBox(FirstChild()),
                                                  constraint_space);
-  while (!minmax_algorithm_->Layout(&physical_fragment))
+  while (!minmax_algorithm_->Layout(nullptr, &physical_fragment, nullptr))
     continue;
 
   fragment = new NGFragment(FromPlatformWritingMode(Style()->getWritingMode()),
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_inline_box.cc b/third_party/WebKit/Source/core/layout/ng/ng_inline_box.cc
index 4b2730f..ab69e70 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_inline_box.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_inline_box.cc
@@ -11,12 +11,14 @@
 #include "core/layout/ng/ng_text_layout_algorithm.h"
 #include "core/layout/ng/ng_constraint_space_builder.h"
 #include "core/layout/ng/ng_constraint_space.h"
-#include "platform/text/TextDirection.h"
 #include "core/layout/ng/ng_physical_text_fragment.h"
 #include "core/layout/ng/ng_text_fragment.h"
 #include "core/layout/ng/ng_fragment_builder.h"
 #include "core/layout/ng/ng_length_utils.h"
 #include "core/layout/ng/ng_writing_mode.h"
+#include "platform/text/TextDirection.h"
+#include "platform/fonts/shaping/CachingWordShaper.h"
+#include "platform/fonts/shaping/CachingWordShapeIterator.h"
 #include "wtf/text/CharacterNames.h"
 
 namespace blink {
@@ -73,7 +75,7 @@
   if (length) {
     unsigned start_offset = *offset;
     *offset = *offset + length;
-    items_.append(NGLayoutInlineItem(start_offset, *offset));
+    items_.append(NGLayoutInlineItem(start_offset, *offset, node->style()));
   }
 }
 
@@ -165,7 +167,23 @@
   items[index + 1].start_offset_ = offset;
 }
 
-void NGInlineBox::ShapeText() {}
+void NGInlineBox::ShapeText() {
+  // TODO(layout-dev): Should pass the entire range to the shaper as context
+  // and then shape each item based on the relevant font.
+  for (auto& item : items_) {
+    String item_text = text_content_.substring(
+        item.start_offset_, item.end_offset_ - item.start_offset_);
+    const Font& item_font = item.style_->font();
+    ShapeCache* shape_cache = item_font.shapeCache();
+
+    TextRun item_run(item_text);
+    CachingWordShapeIterator iterator(shape_cache, item_run, &item_font);
+    RefPtr<const ShapeResult> word_result;
+    while (iterator.next(&word_result)) {
+      item.shape_results_.append(word_result.get());
+    };
+  }
+}
 
 bool NGInlineBox::Layout(const NGConstraintSpace* constraint_space,
                          NGFragmentBase** out) {
@@ -182,7 +200,7 @@
     layout_algorithm_ = new NGTextLayoutAlgorithm(this, child_constraint_space);
 
   NGPhysicalFragmentBase* fragment = nullptr;
-  if (!layout_algorithm_->Layout(&fragment))
+  if (!layout_algorithm_->Layout(nullptr, &fragment, nullptr))
     return false;
 
   // TODO(layout-dev): Implement copying of fragment data to LayoutObject tree.
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_inline_box.h b/third_party/WebKit/Source/core/layout/ng/ng_inline_box.h
index 356c4f42..229eb754 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_inline_box.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_inline_box.h
@@ -31,8 +31,6 @@
 // TODO(layout-dev): Make this and NGBox inherit from a common class.
 class CORE_EXPORT NGInlineBox : public NGLayoutInputNode {
  public:
-  using const_iterator = Vector<NGLayoutInlineItem>::const_iterator;
-
   NGInlineBox(LayoutObject* start_inline, ComputedStyle* block_style);
   ~NGInlineBox() override;
 
@@ -43,10 +41,6 @@
   // calling the Layout method.
   void PrepareLayout();
 
-  // Iterator for the individual NGLayoutInlineItem objects that make up the
-  // inline children of a NGInlineBox instance.
-  const_iterator begin() const { return items_.begin(); }
-  const_iterator end() const { return items_.end(); }
   String Text(unsigned start_offset, unsigned end_offset) const {
     return text_content_.substring(start_offset, end_offset);
   }
@@ -81,11 +75,14 @@
 // element where possible.
 class NGLayoutInlineItem {
  public:
-  NGLayoutInlineItem(unsigned start, unsigned end)
+  NGLayoutInlineItem(unsigned start, unsigned end, const ComputedStyle* style)
       : start_offset_(start),
         end_offset_(end),
         bidi_level_(UBIDI_LTR),
-        script_(USCRIPT_INVALID_CODE) {
+        script_(USCRIPT_INVALID_CODE),
+        fallback_priority_(FontFallbackPriority::Invalid),
+        rotate_sideways_(false),
+        style_(style) {
     DCHECK(end >= start);
   }
 
@@ -93,6 +90,7 @@
   unsigned EndOffset() const { return end_offset_; }
   TextDirection Direction() const { return bidi_level_ & 1 ? RTL : LTR; }
   UScriptCode Script() const { return script_; }
+  const ComputedStyle* Style() const { return style_; }
 
   static void Split(Vector<NGLayoutInlineItem>&,
                     unsigned index,
@@ -107,9 +105,10 @@
   unsigned end_offset_;
   UBiDiLevel bidi_level_;
   UScriptCode script_;
-  // FontFallbackPriority fallback_priority_;
-  // bool rotate_sideways_;
-  RefPtr<ShapeResult> shape_result_;
+  FontFallbackPriority fallback_priority_;
+  bool rotate_sideways_;
+  const ComputedStyle* style_;
+  Vector<RefPtr<const ShapeResult>> shape_results_;
 
   friend class NGInlineBox;
 };
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_inline_box_test.cc b/third_party/WebKit/Source/core/layout/ng/ng_inline_box_test.cc
index 4b3329c7..bf487d13 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_inline_box_test.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_inline_box_test.cc
@@ -24,7 +24,7 @@
   void AppendText(const String& text) {
     unsigned start = text_content_.length();
     text_content_.append(text);
-    items_.emplaceAppend(start, start + text.length());
+    items_.emplaceAppend(start, start + text.length(), nullptr);
   }
 
   void AppendText(const char16_t* text) {
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_inline_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/ng_inline_layout_algorithm.cc
index d922a05..341fcb0 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_inline_layout_algorithm.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_inline_layout_algorithm.cc
@@ -24,11 +24,14 @@
   DCHECK(style_);
 }
 
-bool NGInlineLayoutAlgorithm::Layout(NGPhysicalFragmentBase** out) {
+NGLayoutStatus NGInlineLayoutAlgorithm::Layout(
+    NGFragmentBase*,
+    NGPhysicalFragmentBase** fragment_out,
+    NGLayoutAlgorithm**) {
   NGFragmentBuilder builder(NGPhysicalFragmentBase::FragmentBox);
 
-  *out = builder.ToFragment();
-  return true;
+  *fragment_out = builder.ToFragment();
+  return NewFragment;
 }
 
 DEFINE_TRACE(NGInlineLayoutAlgorithm) {
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_inline_layout_algorithm.h b/third_party/WebKit/Source/core/layout/ng/ng_inline_layout_algorithm.h
index ac09083..beaa199 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_inline_layout_algorithm.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_inline_layout_algorithm.h
@@ -36,7 +36,9 @@
                           NGConstraintSpace* space,
                           NGBreakToken* break_token = nullptr);
 
-  bool Layout(NGPhysicalFragmentBase**) override;
+  NGLayoutStatus Layout(NGFragmentBase*,
+                        NGPhysicalFragmentBase**,
+                        NGLayoutAlgorithm**) override;
 
   DECLARE_VIRTUAL_TRACE();
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_algorithm.h b/third_party/WebKit/Source/core/layout/ng/ng_layout_algorithm.h
index f51917c..ec9fa92 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_algorithm.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_algorithm.h
@@ -13,9 +13,12 @@
 namespace blink {
 
 struct MinAndMaxContentSizes;
+class NGBox;
 class NGConstraintSpace;
 class NGPhysicalFragmentBase;
 
+enum NGLayoutStatus { NotFinished, ChildAlgorithmRequired, NewFragment };
+
 // Base class for all LayoutNG algorithms.
 class CORE_EXPORT NGLayoutAlgorithm
     : public GarbageCollectedFinalized<NGLayoutAlgorithm> {
@@ -30,10 +33,14 @@
   // resulting layout information.
   // This function can not be const because for interruptible layout, we have
   // to be able to store state information.
-  // Returns true when done; when this function returns false, it has to be
-  // called again. The out parameter will only be set when this function
-  // returns true.
-  virtual bool Layout(NGPhysicalFragmentBase**) = 0;
+  // If this function returns NotFinished, it has to be called again.
+  // If it returns ChildAlgorithmRequired, the NGBox out parameter will
+  // be set with the NGBox that needs to be layed out next.
+  // If it returns NewFragment, the NGPhysicalFragmentBase out parameter
+  // will contain the new fragment.
+  virtual NGLayoutStatus Layout(NGFragmentBase*,
+                                NGPhysicalFragmentBase**,
+                                NGLayoutAlgorithm**) = 0;
 
   enum MinAndMaxState { Success, Pending, NotImplemented };
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_length_utils.cc b/third_party/WebKit/Source/core/layout/ng/ng_length_utils.cc
index 6f3c979..86e5fd7 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_length_utils.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_length_utils.cc
@@ -16,47 +16,6 @@
 // - positioned and/or replaced calculations
 // - Take scrollbars into account
 
-namespace {
-
-// Converts physical dimensions to logical ones per
-// https://drafts.csswg.org/css-writing-modes-3/#logical-to-physical
-// For now it's only used to calculate abstract values for margins.
-NGBoxStrut ToLogicalDimensions(const NGPhysicalBoxStrut& physical_dim,
-                               const NGWritingMode writing_mode,
-                               const TextDirection direction) {
-  bool is_ltr = direction == LTR;
-  NGBoxStrut logical_dim;
-  switch (writing_mode) {
-    case VerticalRightLeft:
-    case SidewaysRightLeft:
-      logical_dim = {is_ltr ? physical_dim.top : physical_dim.bottom,
-                     is_ltr ? physical_dim.bottom : physical_dim.top,
-                     physical_dim.right, physical_dim.left};
-      break;
-    case VerticalLeftRight:
-      logical_dim = {is_ltr ? physical_dim.top : physical_dim.bottom,
-                     is_ltr ? physical_dim.bottom : physical_dim.top,
-                     physical_dim.left, physical_dim.right};
-      break;
-    case SidewaysLeftRight:
-      logical_dim = {is_ltr ? physical_dim.bottom : physical_dim.top,
-                     is_ltr ? physical_dim.top : physical_dim.bottom,
-                     physical_dim.left, physical_dim.right};
-      break;
-    default:
-      NOTREACHED();
-    /* FALLTHROUGH */
-    case HorizontalTopBottom:
-      logical_dim = {is_ltr ? physical_dim.left : physical_dim.right,
-                     is_ltr ? physical_dim.right : physical_dim.left,
-                     physical_dim.top, physical_dim.bottom};
-      break;
-  }
-  return logical_dim;
-}
-
-}  // namespace
-
 bool NeedMinAndMaxContentSizes(const ComputedStyle& style) {
   // TODO(layout-ng): In the future we may pass a shrink-to-fit flag through the
   // constraint space; if so, this function needs to take a constraint space
@@ -134,8 +93,7 @@
         LayoutUnit fill_available =
             std::max(LayoutUnit(), available_size - margins.InlineSum() -
                                        border_and_padding.InlineSum());
-        value = std::min(min_and_max->max_content,
-                         std::max(min_and_max->min_content, fill_available));
+        value = min_and_max->ShrinkToFit(fill_available);
       }
       return value + border_and_padding.InlineSum();
     }
@@ -291,7 +249,7 @@
   physical_dim.bottom = ResolveInlineLength(
       constraintSpace, style, empty_sizes, style.marginBottom(),
       LengthResolveType::MarginBorderPaddingSize);
-  return ToLogicalDimensions(physical_dim, writing_mode, direction);
+  return physical_dim.ConvertToLogical(writing_mode, direction);
 }
 
 NGBoxStrut ComputeBorders(const ComputedStyle& style) {
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_text_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/ng_text_layout_algorithm.cc
index 4659bc3..f0048e8 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_text_layout_algorithm.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_text_layout_algorithm.cc
@@ -19,10 +19,13 @@
   DCHECK(inline_box_);
 }
 
-bool NGTextLayoutAlgorithm::Layout(NGPhysicalFragmentBase** out) {
+NGLayoutStatus NGTextLayoutAlgorithm::Layout(
+    NGFragmentBase*,
+    NGPhysicalFragmentBase** fragment_out,
+    NGLayoutAlgorithm**) {
   // TODO(layout-dev): implement.
-  *out = nullptr;
-  return true;
+  *fragment_out = nullptr;
+  return NewFragment;
 }
 
 DEFINE_TRACE(NGTextLayoutAlgorithm) {
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_text_layout_algorithm.h b/third_party/WebKit/Source/core/layout/ng/ng_text_layout_algorithm.h
index fd25469..f0aed7fe 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_text_layout_algorithm.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_text_layout_algorithm.h
@@ -33,7 +33,9 @@
                         NGConstraintSpace* space,
                         NGBreakToken* break_token = nullptr);
 
-  bool Layout(NGPhysicalFragmentBase**) override;
+  NGLayoutStatus Layout(NGFragmentBase*,
+                        NGPhysicalFragmentBase**,
+                        NGLayoutAlgorithm**) override;
 
   DECLARE_VIRTUAL_TRACE();
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_units.cc b/third_party/WebKit/Source/core/layout/ng/ng_units.cc
index 279ba7a4..9eeb0134 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_units.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_units.cc
@@ -8,6 +8,10 @@
 
 namespace blink {
 
+LayoutUnit MinAndMaxContentSizes::ShrinkToFit(LayoutUnit available_size) const {
+  return std::min(max_content, std::max(min_content, available_size));
+}
+
 NGPhysicalSize NGLogicalSize::ConvertToPhysical(NGWritingMode mode) const {
   return mode == HorizontalTopBottom ? NGPhysicalSize(inline_size, block_size)
                                      : NGPhysicalSize(block_size, inline_size);
@@ -139,6 +143,31 @@
          std::tie(inline_start, inline_end, block_start, block_end);
 }
 
+// Converts physical dimensions to logical ones per
+// https://drafts.csswg.org/css-writing-modes-3/#logical-to-physical
+NGBoxStrut NGPhysicalBoxStrut::ConvertToLogical(NGWritingMode writing_mode,
+                                                TextDirection direction) const {
+  NGBoxStrut strut;
+  switch (writing_mode) {
+    case HorizontalTopBottom:
+      strut = {left, right, top, bottom};
+      break;
+    case VerticalRightLeft:
+    case SidewaysRightLeft:
+      strut = {top, bottom, right, left};
+      break;
+    case VerticalLeftRight:
+      strut = {top, bottom, left, right};
+      break;
+    case SidewaysLeftRight:
+      strut = {bottom, top, left, right};
+      break;
+  }
+  if (direction == RTL)
+    std::swap(strut.inline_start, strut.inline_end);
+  return strut;
+}
+
 LayoutUnit NGMarginStrut::BlockEndSum() const {
   return margin_block_end + negative_margin_block_end;
 }
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_units.h b/third_party/WebKit/Source/core/layout/ng/ng_units.h
index 8ea43bdc..a6cad5b 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_units.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_units.h
@@ -16,10 +16,12 @@
 class LayoutUnit;
 struct NGPhysicalOffset;
 struct NGPhysicalSize;
+struct NGBoxStrut;
 
 struct MinAndMaxContentSizes {
   LayoutUnit min_content;
   LayoutUnit max_content;
+  LayoutUnit ShrinkToFit(LayoutUnit available_size) const;
 };
 
 struct NGLogicalSize {
@@ -38,6 +40,11 @@
   }
 };
 
+inline std::ostream& operator<<(std::ostream& stream,
+                                const NGLogicalSize& value) {
+  return stream << value.inline_size << "x" << value.block_size;
+}
+
 // NGLogicalOffset is the position of a rect (typically a fragment) relative to
 // its parent rect in the logical coordinate system.
 struct NGLogicalOffset {
@@ -167,16 +174,17 @@
 // Struct to store physical dimensions, independent of writing mode and
 // direction.
 // See https://drafts.csswg.org/css-writing-modes-3/#abstract-box
-struct NGPhysicalBoxStrut {
+struct CORE_EXPORT NGPhysicalBoxStrut {
   LayoutUnit left;
   LayoutUnit right;
   LayoutUnit top;
   LayoutUnit bottom;
+  NGBoxStrut ConvertToLogical(NGWritingMode, TextDirection) const;
 };
 
 // This struct is used for storing margins, borders or padding of a box on all
 // four edges.
-struct NGBoxStrut {
+struct CORE_EXPORT NGBoxStrut {
   LayoutUnit inline_start;
   LayoutUnit inline_end;
   LayoutUnit block_start;
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_units_test.cc b/third_party/WebKit/Source/core/layout/ng/ng_units_test.cc
index c353df5..10d4834 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_units_test.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_units_test.cc
@@ -69,6 +69,37 @@
   EXPECT_EQ(LayoutUnit(20), offset.top);
 }
 
+// Ideally, this would be tested by NGBoxStrut::ConvertToPhysical, but
+// this has not been implemented yet.
+TEST(NGUnitsTest, ConvertPhysicalStrutToLogical) {
+  LayoutUnit left{5}, right{10}, top{15}, bottom{20};
+  NGPhysicalBoxStrut physical{left, right, top, bottom};
+
+  NGBoxStrut logical = physical.ConvertToLogical(HorizontalTopBottom, LTR);
+  EXPECT_EQ(left, logical.inline_start);
+  EXPECT_EQ(top, logical.block_start);
+
+  logical = physical.ConvertToLogical(HorizontalTopBottom, RTL);
+  EXPECT_EQ(right, logical.inline_start);
+  EXPECT_EQ(top, logical.block_start);
+
+  logical = physical.ConvertToLogical(VerticalLeftRight, LTR);
+  EXPECT_EQ(top, logical.inline_start);
+  EXPECT_EQ(left, logical.block_start);
+
+  logical = physical.ConvertToLogical(VerticalLeftRight, RTL);
+  EXPECT_EQ(bottom, logical.inline_start);
+  EXPECT_EQ(left, logical.block_start);
+
+  logical = physical.ConvertToLogical(VerticalRightLeft, LTR);
+  EXPECT_EQ(top, logical.inline_start);
+  EXPECT_EQ(right, logical.block_start);
+
+  logical = physical.ConvertToLogical(VerticalRightLeft, RTL);
+  EXPECT_EQ(bottom, logical.inline_start);
+  EXPECT_EQ(right, logical.block_start);
+}
+
 }  // namespace
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/page/ChromeClient.cpp b/third_party/WebKit/Source/core/page/ChromeClient.cpp
index c048b73..5184b74e 100644
--- a/third_party/WebKit/Source/core/page/ChromeClient.cpp
+++ b/third_party/WebKit/Source/core/page/ChromeClient.cpp
@@ -151,13 +151,16 @@
 
 void ChromeClient::mouseDidMoveOverElement(LocalFrame& frame,
                                            const HitTestResult& result) {
-  if (result.innerNode() &&
+  if (!result.scrollbar() && result.innerNode() &&
       result.innerNode()->document().isDNSPrefetchEnabled())
     prefetchDNS(result.absoluteLinkURL().host());
 
   showMouseOverURL(result);
 
-  setToolTip(frame, result);
+  if (result.scrollbar())
+    clearToolTip(frame);
+  else
+    setToolTip(frame, result);
 }
 
 void ChromeClient::setToolTip(LocalFrame& frame, const HitTestResult& result) {
diff --git a/third_party/WebKit/Source/core/page/DragController.cpp b/third_party/WebKit/Source/core/page/DragController.cpp
index dab1f265..8f343e5 100644
--- a/third_party/WebKit/Source/core/page/DragController.cpp
+++ b/third_party/WebKit/Source/core/page/DragController.cpp
@@ -1150,13 +1150,10 @@
                                   DataTransfer* dataTransfer,
                                   LocalFrame* frame,
                                   bool forLink) {
-  // TODO(dcheng): Drag and drop is not yet supported for OOPI.
-  if (m_page->mainFrame()->isRemoteFrame())
-    return;
   m_didInitiateDrag = true;
   m_dragInitiator = frame->document();
 
-  LocalFrame* mainFrame = m_page->deprecatedLocalMainFrame();
+  LocalFrame* mainFrame = frame->localFrameRoot();
   FrameView* mainFrameView = mainFrame->view();
   IntPoint adjustedDragLocation = mainFrameView->rootFrameToContents(
       frame->view()->contentsToRootFrame(dragLocation));
diff --git a/third_party/WebKit/Source/core/paint/BUILD.gn b/third_party/WebKit/Source/core/paint/BUILD.gn
index 4343f64..a2b993f 100644
--- a/third_party/WebKit/Source/core/paint/BUILD.gn
+++ b/third_party/WebKit/Source/core/paint/BUILD.gn
@@ -47,6 +47,7 @@
     "FilterEffectBuilder.h",
     "FilterPainter.cpp",
     "FilterPainter.h",
+    "FindPropertiesNeedingUpdate.h",
     "FirstMeaningfulPaintDetector.cpp",
     "FirstMeaningfulPaintDetector.h",
     "FloatClipRecorder.cpp",
diff --git a/third_party/WebKit/Source/core/paint/FindPropertiesNeedingUpdate.h b/third_party/WebKit/Source/core/paint/FindPropertiesNeedingUpdate.h
new file mode 100644
index 0000000..fb3358b
--- /dev/null
+++ b/third_party/WebKit/Source/core/paint/FindPropertiesNeedingUpdate.h
@@ -0,0 +1,144 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FindPropertiesNeedingUpdate_h
+#define FindPropertiesNeedingUpdate_h
+
+#if DCHECK_IS_ON()
+namespace blink {
+
+#define DCHECK_PTR_VAL_EQ(ptrA, ptrB) \
+  DCHECK((!!ptrA == !!ptrB) && ((!ptrA && !ptrB) || (*ptrA == *ptrB)));
+
+// This file contains two scope classes for catching cases where paint
+// properties need an update but where not marked as such. If paint properties
+// will change, the object must be marked as needing a paint property update
+// using {FrameView, LayoutObject}::setNeedsPaintPropertyUpdate().
+//
+// Both scope classes work by recording the paint property state of an object
+// before rebuilding properties, forcing the properties to get updated, then
+// checking that the updated properties match the original properties.
+
+class FindFrameViewPropertiesNeedingUpdateScope {
+ public:
+  FindFrameViewPropertiesNeedingUpdateScope(FrameView* frameView)
+      : m_frameView(frameView),
+        m_neededPaintPropertyUpdate(frameView->needsPaintPropertyUpdate()) {
+    // No need to check when already marked as needing an update.
+    if (m_neededPaintPropertyUpdate)
+      return;
+
+    // Mark the properties as needing an update to ensure they are rebuilt.
+    m_frameView->setNeedsPaintPropertyUpdate();
+    if (auto* preTranslation = m_frameView->preTranslation())
+      m_preTranslation = preTranslation->clone();
+    if (auto* contentClip = m_frameView->contentClip())
+      m_contentClip = contentClip->clone();
+    if (auto* scrollTranslation = m_frameView->scrollTranslation())
+      m_scrollTranslation = scrollTranslation->clone();
+    if (auto* scroll = m_frameView->scroll())
+      m_scroll = scroll->clone();
+  }
+
+  ~FindFrameViewPropertiesNeedingUpdateScope() {
+    // No need to check when already marked as needing an update.
+    if (m_neededPaintPropertyUpdate)
+      return;
+
+    // If paint properties are not marked as needing an update but still change,
+    // we are missing a call to FrameView::setNeedsPaintPropertyUpdate().
+    DCHECK_PTR_VAL_EQ(m_preTranslation, m_frameView->preTranslation());
+    DCHECK_PTR_VAL_EQ(m_contentClip, m_frameView->contentClip());
+    DCHECK_PTR_VAL_EQ(m_scrollTranslation, m_frameView->scrollTranslation());
+    DCHECK_PTR_VAL_EQ(m_scroll, m_frameView->scroll());
+    // Restore original clean bit.
+    m_frameView->clearNeedsPaintPropertyUpdate();
+  }
+
+ private:
+  Persistent<FrameView> m_frameView;
+  bool m_neededPaintPropertyUpdate;
+  RefPtr<TransformPaintPropertyNode> m_preTranslation;
+  RefPtr<ClipPaintPropertyNode> m_contentClip;
+  RefPtr<TransformPaintPropertyNode> m_scrollTranslation;
+  RefPtr<ScrollPaintPropertyNode> m_scroll;
+};
+
+class FindObjectPropertiesNeedingUpdateScope {
+ public:
+  FindObjectPropertiesNeedingUpdateScope(const LayoutObject& object)
+      : m_object(object),
+        m_neededPaintPropertyUpdate(object.needsPaintPropertyUpdate()) {
+    // No need to check when already marked as needing an update.
+    if (m_neededPaintPropertyUpdate)
+      return;
+
+    // Mark the properties as needing an update to ensure they are rebuilt.
+    const_cast<LayoutObject&>(m_object).setNeedsPaintPropertyUpdate();
+    if (const auto* properties = m_object.paintProperties())
+      m_properties = properties->clone();
+  }
+
+  ~FindObjectPropertiesNeedingUpdateScope() {
+    // No need to check when already marked as needing an update.
+    if (m_neededPaintPropertyUpdate)
+      return;
+
+    // If paint properties are not marked as needing an update but still change,
+    // we are missing a call to LayoutObject::setNeedsPaintPropertyUpdate().
+    const auto* objectProperties = m_object.paintProperties();
+    if (m_properties && objectProperties) {
+      DCHECK_PTR_VAL_EQ(m_properties->paintOffsetTranslation(),
+                        objectProperties->paintOffsetTranslation());
+      DCHECK_PTR_VAL_EQ(m_properties->transform(),
+                        objectProperties->transform());
+      DCHECK_PTR_VAL_EQ(m_properties->effect(), objectProperties->effect());
+      DCHECK_PTR_VAL_EQ(m_properties->cssClip(), objectProperties->cssClip());
+      DCHECK_PTR_VAL_EQ(m_properties->cssClipFixedPosition(),
+                        objectProperties->cssClipFixedPosition());
+      DCHECK_PTR_VAL_EQ(m_properties->innerBorderRadiusClip(),
+                        objectProperties->innerBorderRadiusClip());
+      DCHECK_PTR_VAL_EQ(m_properties->overflowClip(),
+                        objectProperties->overflowClip());
+      DCHECK_PTR_VAL_EQ(m_properties->perspective(),
+                        objectProperties->perspective());
+      DCHECK_PTR_VAL_EQ(m_properties->svgLocalToBorderBoxTransform(),
+                        objectProperties->svgLocalToBorderBoxTransform());
+      DCHECK_PTR_VAL_EQ(m_properties->scrollTranslation(),
+                        objectProperties->scrollTranslation());
+      DCHECK_PTR_VAL_EQ(m_properties->scrollbarPaintOffset(),
+                        objectProperties->scrollbarPaintOffset());
+      const auto* borderBox = m_properties->localBorderBoxProperties();
+      const auto* objectBorderBox =
+          objectProperties->localBorderBoxProperties();
+      if (borderBox && objectBorderBox) {
+        DCHECK(borderBox->paintOffset == objectBorderBox->paintOffset);
+        DCHECK_EQ(borderBox->propertyTreeState.transform(),
+                  objectBorderBox->propertyTreeState.transform());
+        DCHECK_EQ(borderBox->propertyTreeState.clip(),
+                  objectBorderBox->propertyTreeState.clip());
+        DCHECK_EQ(borderBox->propertyTreeState.effect(),
+                  objectBorderBox->propertyTreeState.effect());
+        DCHECK_EQ(borderBox->propertyTreeState.scroll(),
+                  objectBorderBox->propertyTreeState.scroll());
+      } else {
+        DCHECK_EQ(!!borderBox, !!objectBorderBox);
+      }
+    } else {
+      DCHECK_EQ(!!m_properties, !!objectProperties);
+    }
+    // Restore original clean bit.
+    const_cast<LayoutObject&>(m_object).clearNeedsPaintPropertyUpdate();
+  }
+
+ private:
+  const LayoutObject& m_object;
+  bool m_neededPaintPropertyUpdate;
+  std::unique_ptr<const ObjectPaintProperties> m_properties;
+};
+
+}  // namespace blink
+#endif  // DCHECK_IS_ON()
+
+#endif  // FindPropertiesNeedingUpdate_h
diff --git a/third_party/WebKit/Source/core/paint/ObjectPaintProperties.h b/third_party/WebKit/Source/core/paint/ObjectPaintProperties.h
index 3e266a8..cd4e15a 100644
--- a/third_party/WebKit/Source/core/paint/ObjectPaintProperties.h
+++ b/third_party/WebKit/Source/core/paint/ObjectPaintProperties.h
@@ -207,6 +207,48 @@
     updateProperty(m_overflowClip, std::forward<Args>(args)...);
   }
 
+#if DCHECK_IS_ON()
+  // Used by FindPropertiesNeedingUpdate.h for recording the current properties.
+  std::unique_ptr<ObjectPaintProperties> clone() const {
+    std::unique_ptr<ObjectPaintProperties> cloned = create();
+    if (m_paintOffsetTranslation)
+      cloned->m_paintOffsetTranslation = m_paintOffsetTranslation->clone();
+    if (m_transform)
+      cloned->m_transform = m_transform->clone();
+    if (m_effect)
+      cloned->m_effect = m_effect->clone();
+    if (m_cssClip)
+      cloned->m_cssClip = m_cssClip->clone();
+    if (m_cssClipFixedPosition)
+      cloned->m_cssClipFixedPosition = m_cssClipFixedPosition->clone();
+    if (m_innerBorderRadiusClip)
+      cloned->m_innerBorderRadiusClip = m_innerBorderRadiusClip->clone();
+    if (m_overflowClip)
+      cloned->m_overflowClip = m_overflowClip->clone();
+    if (m_perspective)
+      cloned->m_perspective = m_perspective->clone();
+    if (m_svgLocalToBorderBoxTransform) {
+      cloned->m_svgLocalToBorderBoxTransform =
+          m_svgLocalToBorderBoxTransform->clone();
+    }
+    if (m_scrollTranslation)
+      cloned->m_scrollTranslation = m_scrollTranslation->clone();
+    if (m_scrollbarPaintOffset)
+      cloned->m_scrollbarPaintOffset = m_scrollbarPaintOffset->clone();
+    if (m_scroll)
+      cloned->m_scroll = m_scroll->clone();
+    if (m_localBorderBoxProperties) {
+      auto& state = m_localBorderBoxProperties->propertyTreeState;
+      cloned->m_localBorderBoxProperties =
+          wrapUnique(new PropertyTreeStateWithOffset(
+              m_localBorderBoxProperties->paintOffset,
+              PropertyTreeState(state.transform(), state.clip(), state.effect(),
+                                state.scroll())));
+    }
+    return cloned;
+  }
+#endif
+
  private:
   ObjectPaintProperties() {}
 
diff --git a/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp b/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
index 23d71cbf..e75f691 100644
--- a/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
@@ -214,11 +214,8 @@
     context.paintingLayer->setNeedsPaintPhaseDescendantBlockBackgrounds();
   }
 
-  if (object.isTable()) {
-    const LayoutTable& table = toLayoutTable(object);
-    if (table.collapseBorders() && !table.collapsedBorders().isEmpty())
-      context.paintingLayer->setNeedsPaintPhaseDescendantBlockBackgrounds();
-  }
+  if (object.isTable() && toLayoutTable(object).hasCollapsedBorders())
+    context.paintingLayer->setNeedsPaintPhaseDescendantBlockBackgrounds();
 }
 
 namespace {
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.cpp b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
index f782b1f..9c040f8 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayer.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
@@ -145,7 +145,7 @@
       m_shouldIsolateCompositedDescendants(false),
       m_lostGroupedMapping(false),
       m_needsRepaint(false),
-      m_previousPaintResult(PaintLayerPainter::FullyPainted),
+      m_previousPaintResult(FullyPainted),
       m_needsPaintPhaseDescendantOutlines(false),
       m_previousPaintPhaseDescendantOutlinesWasEmpty(false),
       m_needsPaintPhaseFloat(false),
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.h b/third_party/WebKit/Source/core/paint/PaintLayer.h
index edda077..1789db0 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayer.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayer.h
@@ -51,10 +51,10 @@
 #include "core/paint/PaintLayerClipper.h"
 #include "core/paint/PaintLayerFilterInfo.h"
 #include "core/paint/PaintLayerFragment.h"
-#include "core/paint/PaintLayerPainter.h"
 #include "core/paint/PaintLayerScrollableArea.h"
 #include "core/paint/PaintLayerStackingNode.h"
 #include "core/paint/PaintLayerStackingNodeIterator.h"
+#include "core/paint/PaintResult.h"
 #include "platform/graphics/CompositingReasons.h"
 #include "platform/graphics/SquashingDisallowedReasons.h"
 #include "wtf/Allocator.h"
@@ -907,10 +907,10 @@
     m_previousPaintDirtyRect = rect;
   }
 
-  PaintLayerPainter::PaintResult previousPaintResult() const {
-    return static_cast<PaintLayerPainter::PaintResult>(m_previousPaintResult);
+  PaintResult previousPaintResult() const {
+    return static_cast<PaintResult>(m_previousPaintResult);
   }
-  void setPreviousPaintResult(PaintLayerPainter::PaintResult result) {
+  void setPreviousPaintResult(PaintResult result) {
     m_previousPaintResult = static_cast<unsigned>(result);
     DCHECK(m_previousPaintResult == static_cast<unsigned>(result));
   }
@@ -1162,7 +1162,9 @@
   unsigned m_lostGroupedMapping : 1;
 
   unsigned m_needsRepaint : 1;
-  unsigned m_previousPaintResult : 1;  // PaintLayerPainter::PaintResult
+  unsigned m_previousPaintResult : 1;  // PaintResult
+  static_assert(MaxPaintResult <= 2,
+                "Should update number of bits of m_previousPaintResult");
 
   unsigned m_needsPaintPhaseDescendantOutlines : 1;
   unsigned m_previousPaintPhaseDescendantOutlinesWasEmpty : 1;
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp b/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
index 51ff3a2..c96cfca 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
@@ -63,7 +63,7 @@
              : RespectOverflowClip;
 }
 
-PaintLayerPainter::PaintResult PaintLayerPainter::paintLayer(
+PaintResult PaintLayerPainter::paintLayer(
     GraphicsContext& context,
     const PaintLayerPaintingInfo& paintingInfo,
     PaintLayerFlags paintFlags) {
@@ -110,8 +110,7 @@
                                                 paintFlags);
 }
 
-PaintLayerPainter::PaintResult
-PaintLayerPainter::paintLayerContentsCompositingAllPhases(
+PaintResult PaintLayerPainter::paintLayerContentsCompositingAllPhases(
     GraphicsContext& context,
     const PaintLayerPaintingInfo& paintingInfo,
     PaintLayerFlags paintFlags,
@@ -207,8 +206,7 @@
 
   // Repaint if previously the layer might be clipped by paintDirtyRect and
   // paintDirtyRect changes.
-  if (paintLayer.previousPaintResult() ==
-          PaintLayerPainter::MayBeClippedByPaintDirtyRect &&
+  if (paintLayer.previousPaintResult() == MayBeClippedByPaintDirtyRect &&
       paintLayer.previousPaintDirtyRect() != paintingInfo.paintDirtyRect) {
     needsRepaint = true;
     shouldClearEmptyPaintPhaseFlags = true;
@@ -227,7 +225,7 @@
   return needsRepaint;
 }
 
-PaintLayerPainter::PaintResult PaintLayerPainter::paintLayerContents(
+PaintResult PaintLayerPainter::paintLayerContents(
     GraphicsContext& context,
     const PaintLayerPaintingInfo& paintingInfoArg,
     PaintLayerFlags paintFlags,
@@ -580,7 +578,7 @@
   return false;
 }
 
-PaintLayerPainter::PaintResult PaintLayerPainter::paintLayerWithTransform(
+PaintResult PaintLayerPainter::paintLayerWithTransform(
     GraphicsContext& context,
     const PaintLayerPaintingInfo& paintingInfo,
     PaintLayerFlags paintFlags) {
@@ -710,8 +708,7 @@
   return result;
 }
 
-PaintLayerPainter::PaintResult
-PaintLayerPainter::paintFragmentByApplyingTransform(
+PaintResult PaintLayerPainter::paintFragmentByApplyingTransform(
     GraphicsContext& context,
     const PaintLayerPaintingInfo& paintingInfo,
     PaintLayerFlags paintFlags,
@@ -752,7 +749,7 @@
       context, transformedPaintingInfo, paintFlags, ForceSingleFragment);
 }
 
-PaintLayerPainter::PaintResult PaintLayerPainter::paintChildren(
+PaintResult PaintLayerPainter::paintChildren(
     unsigned childrenToVisit,
     GraphicsContext& context,
     const PaintLayerPaintingInfo& paintingInfo,
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerPainter.h b/third_party/WebKit/Source/core/paint/PaintLayerPainter.h
index b767e7a..a0520c9 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerPainter.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayerPainter.h
@@ -8,6 +8,7 @@
 #include "core/CoreExport.h"
 #include "core/paint/PaintLayerFragment.h"
 #include "core/paint/PaintLayerPaintingInfo.h"
+#include "core/paint/PaintResult.h"
 #include "wtf/Allocator.h"
 
 namespace blink {
@@ -27,18 +28,6 @@
  public:
   enum FragmentPolicy { AllowMultipleFragments, ForceSingleFragment };
 
-  // When adding new values, must update the number of bits of
-  // PaintLayer::m_previousPaintingResult.
-  enum PaintResult {
-    // The layer is fully painted. This includes cases that nothing needs
-    // painting regardless of the paint rect.
-    FullyPainted,
-    // Some part of the layer is out of the paint rect and may be not fully
-    // painted.  The results cannot be cached because they may change when paint
-    // rect changes.
-    MayBeClippedByPaintDirtyRect
-  };
-
   PaintLayerPainter(PaintLayer& paintLayer) : m_paintLayer(paintLayer) {}
 
   // The paint() method paints the layers that intersect the damage rect from
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
index f4d15b2e..5a799cd8 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
@@ -1758,6 +1758,16 @@
   return nullptr;
 }
 
+void PaintLayerScrollableArea::getTickmarks(Vector<IntRect>& tickmarks) const {
+  if (!layer()->isRootLayer())
+    return;
+
+  // TODO(crbug.com/417782): Verify that tickmarks work with root layer
+  // scrolling.
+  if (LocalFrame* frame = box().frame())
+    frame->view()->getTickmarks(tickmarks);
+}
+
 PaintLayerScrollableArea*
 PaintLayerScrollableArea::ScrollbarManager::scrollableArea() {
   return toPaintLayerScrollableArea(m_scrollableArea.get());
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h
index 5f36438..114f525 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h
@@ -293,6 +293,7 @@
   int pageStep(ScrollbarOrientation) const override;
   ScrollBehavior scrollBehaviorStyle() const override;
   CompositorAnimationTimeline* compositorAnimationTimeline() const override;
+  void getTickmarks(Vector<IntRect>& rects) const override;
 
   void visibleSizeChanged();
 
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
index 78097181..2ca4622 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
@@ -10,6 +10,7 @@
 #include "core/layout/LayoutInline.h"
 #include "core/layout/LayoutView.h"
 #include "core/layout/svg/LayoutSVGRoot.h"
+#include "core/paint/FindPropertiesNeedingUpdate.h"
 #include "core/paint/ObjectPaintProperties.h"
 #include "core/paint/PaintLayer.h"
 #include "core/paint/SVGRootPainter.h"
@@ -101,7 +102,7 @@
   }
 }
 
-void PaintPropertyTreeBuilder::updateFramePropertiesAndContext(
+void PaintPropertyTreeBuilder::updateProperties(
     FrameView& frameView,
     PaintPropertyTreeBuilderContext& context) {
   if (RuntimeEnabledFeatures::rootLayerScrollingEnabled()) {
@@ -118,38 +119,44 @@
     return;
   }
 
-  TransformationMatrix frameTranslate;
-  frameTranslate.translate(frameView.x() + context.current.paintOffset.x(),
-                           frameView.y() + context.current.paintOffset.y());
-  updateFrameViewPreTranslation(frameView, context.current.transform,
-                                frameTranslate, FloatPoint3D());
+#if DCHECK_IS_ON()
+  FindFrameViewPropertiesNeedingUpdateScope checkNeedsUpdateScope(&frameView);
+#endif
 
-  FloatRoundedRect contentClip(
-      IntRect(IntPoint(), frameView.visibleContentSize()));
-  updateFrameViewContentClip(frameView, context.current.clip,
-                             frameView.preTranslation(), contentClip);
+  if (frameView.needsPaintPropertyUpdate()) {
+    TransformationMatrix frameTranslate;
+    frameTranslate.translate(frameView.x() + context.current.paintOffset.x(),
+                             frameView.y() + context.current.paintOffset.y());
+    updateFrameViewPreTranslation(frameView, context.current.transform,
+                                  frameTranslate, FloatPoint3D());
 
-  ScrollOffset scrollOffset = frameView.scrollOffset();
-  if (frameView.isScrollable() || !scrollOffset.isZero()) {
-    TransformationMatrix frameScroll;
-    frameScroll.translate(-scrollOffset.width(), -scrollOffset.height());
-    updateFrameViewScrollTranslation(frameView, frameView.preTranslation(),
-                                     frameScroll, FloatPoint3D());
+    FloatRoundedRect contentClip(
+        IntRect(IntPoint(), frameView.visibleContentSize()));
+    updateFrameViewContentClip(frameView, context.current.clip,
+                               frameView.preTranslation(), contentClip);
 
-    IntSize scrollClip = frameView.visibleContentSize();
-    IntSize scrollBounds = frameView.contentsSize();
-    bool userScrollableHorizontal =
-        frameView.userInputScrollable(HorizontalScrollbar);
-    bool userScrollableVertical =
-        frameView.userInputScrollable(VerticalScrollbar);
-    updateFrameViewScroll(frameView, context.current.scroll,
-                          frameView.scrollTranslation(), scrollClip,
-                          scrollBounds, userScrollableHorizontal,
-                          userScrollableVertical);
-  } else {
-    // Ensure pre-existing properties are cleared when there is no scrolling.
-    frameView.setScrollTranslation(nullptr);
-    frameView.setScroll(nullptr);
+    ScrollOffset scrollOffset = frameView.scrollOffset();
+    if (frameView.isScrollable() || !scrollOffset.isZero()) {
+      TransformationMatrix frameScroll;
+      frameScroll.translate(-scrollOffset.width(), -scrollOffset.height());
+      updateFrameViewScrollTranslation(frameView, frameView.preTranslation(),
+                                       frameScroll, FloatPoint3D());
+
+      IntSize scrollClip = frameView.visibleContentSize();
+      IntSize scrollBounds = frameView.contentsSize();
+      bool userScrollableHorizontal =
+          frameView.userInputScrollable(HorizontalScrollbar);
+      bool userScrollableVertical =
+          frameView.userInputScrollable(VerticalScrollbar);
+      updateFrameViewScroll(frameView, context.current.scroll,
+                            frameView.scrollTranslation(), scrollClip,
+                            scrollBounds, userScrollableHorizontal,
+                            userScrollableVertical);
+    } else {
+      // Ensure pre-existing properties are cleared when there is no scrolling.
+      frameView.setScrollTranslation(nullptr);
+      frameView.setScroll(nullptr);
+    }
   }
 
   // Initialize the context for current, absolute and fixed position cases.
@@ -210,18 +217,20 @@
   LayoutPoint fractionalPaintOffset =
       LayoutPoint(context.current.paintOffset - roundedPaintOffset);
 
-  if (usesPaintOffsetTranslation) {
-    object.getMutableForPainting()
-        .ensurePaintProperties()
-        .updatePaintOffsetTranslation(
-            context.current.transform,
-            TransformationMatrix().translate(roundedPaintOffset.x(),
-                                             roundedPaintOffset.y()),
-            FloatPoint3D(), context.current.shouldFlattenInheritedTransform,
-            context.current.renderingContextID);
-  } else {
-    if (auto* properties = object.getMutableForPainting().paintProperties())
-      properties->clearPaintOffsetTranslation();
+  if (object.needsPaintPropertyUpdate()) {
+    if (usesPaintOffsetTranslation) {
+      object.getMutableForPainting()
+          .ensurePaintProperties()
+          .updatePaintOffsetTranslation(
+              context.current.transform,
+              TransformationMatrix().translate(roundedPaintOffset.x(),
+                                               roundedPaintOffset.y()),
+              FloatPoint3D(), context.current.shouldFlattenInheritedTransform,
+              context.current.renderingContextID);
+    } else {
+      if (auto* properties = object.getMutableForPainting().paintProperties())
+        properties->clearPaintOffsetTranslation();
+    }
   }
 
   const auto* properties = object.paintProperties();
@@ -257,23 +266,25 @@
   DCHECK(object.isSVGForeignObject() ||
          context.current.paintOffset == LayoutPoint());
 
-  // TODO(pdr): Refactor this so all non-root SVG objects use the same
-  // transform function.
-  const AffineTransform& transform = object.isSVGForeignObject()
-                                         ? object.localSVGTransform()
-                                         : object.localToSVGParentTransform();
-  // TODO(pdr): Check for the presence of a transform instead of the value.
-  // Checking for an identity matrix will cause the property tree structure
-  // to change during animations if the animation passes through the
-  // identity matrix.
-  if (!transform.isIdentity()) {
-    // The origin is included in the local transform, so leave origin empty.
-    object.getMutableForPainting().ensurePaintProperties().updateTransform(
-        context.current.transform, TransformationMatrix(transform),
-        FloatPoint3D());
-  } else {
-    if (auto* properties = object.getMutableForPainting().paintProperties())
-      properties->clearTransform();
+  if (object.needsPaintPropertyUpdate()) {
+    // TODO(pdr): Refactor this so all non-root SVG objects use the same
+    // transform function.
+    const AffineTransform& transform = object.isSVGForeignObject()
+                                           ? object.localSVGTransform()
+                                           : object.localToSVGParentTransform();
+    // TODO(pdr): Check for the presence of a transform instead of the value.
+    // Checking for an identity matrix will cause the property tree structure
+    // to change during animations if the animation passes through the
+    // identity matrix.
+    if (!transform.isIdentity()) {
+      // The origin is included in the local transform, so leave origin empty.
+      object.getMutableForPainting().ensurePaintProperties().updateTransform(
+          context.current.transform, TransformationMatrix(transform),
+          FloatPoint3D());
+    } else {
+      if (auto* properties = object.getMutableForPainting().paintProperties())
+        properties->clearTransform();
+    }
   }
 
   if (object.paintProperties() && object.paintProperties()->transform()) {
@@ -291,28 +302,32 @@
     return;
   }
 
-  const ComputedStyle& style = object.styleRef();
-  if (object.isBox() && (style.hasTransform() || style.preserves3D())) {
-    TransformationMatrix matrix;
-    style.applyTransform(matrix, toLayoutBox(object).size(),
-                         ComputedStyle::ExcludeTransformOrigin,
-                         ComputedStyle::IncludeMotionPath,
-                         ComputedStyle::IncludeIndependentTransformProperties);
+  if (object.needsPaintPropertyUpdate()) {
+    const ComputedStyle& style = object.styleRef();
+    if (object.isBox() && (style.hasTransform() || style.preserves3D())) {
+      TransformationMatrix matrix;
+      style.applyTransform(
+          matrix, toLayoutBox(object).size(),
+          ComputedStyle::ExcludeTransformOrigin,
+          ComputedStyle::IncludeMotionPath,
+          ComputedStyle::IncludeIndependentTransformProperties);
 
-    // TODO(trchen): transform-style should only be respected if a PaintLayer
-    // is created.
-    // If a node with transform-style: preserve-3d does not exist in an
-    // existing rendering context, it establishes a new one.
-    unsigned renderingContextID = context.current.renderingContextID;
-    if (style.preserves3D() && !renderingContextID)
-      renderingContextID = PtrHash<const LayoutObject>::hash(&object);
+      // TODO(trchen): transform-style should only be respected if a PaintLayer
+      // is created.
+      // If a node with transform-style: preserve-3d does not exist in an
+      // existing rendering context, it establishes a new one.
+      unsigned renderingContextID = context.current.renderingContextID;
+      if (style.preserves3D() && !renderingContextID)
+        renderingContextID = PtrHash<const LayoutObject>::hash(&object);
 
-    object.getMutableForPainting().ensurePaintProperties().updateTransform(
-        context.current.transform, matrix, transformOrigin(toLayoutBox(object)),
-        context.current.shouldFlattenInheritedTransform, renderingContextID);
-  } else {
-    if (auto* properties = object.getMutableForPainting().paintProperties())
-      properties->clearTransform();
+      object.getMutableForPainting().ensurePaintProperties().updateTransform(
+          context.current.transform, matrix,
+          transformOrigin(toLayoutBox(object)),
+          context.current.shouldFlattenInheritedTransform, renderingContextID);
+    } else {
+      if (auto* properties = object.getMutableForPainting().paintProperties())
+        properties->clearTransform();
+    }
   }
 
   const auto* properties = object.paintProperties();
@@ -335,62 +350,66 @@
   const ComputedStyle& style = object.styleRef();
 
   if (!style.isStackingContext()) {
-    if (auto* properties = object.getMutableForPainting().paintProperties())
-      properties->clearEffect();
+    if (object.needsPaintPropertyUpdate()) {
+      if (auto* properties = object.getMutableForPainting().paintProperties())
+        properties->clearEffect();
+    }
     return;
   }
 
   // TODO(trchen): Can't omit effect node if we have 3D children.
   // TODO(trchen): Can't omit effect node if we have blending children.
-  bool effectNodeNeeded = false;
+  if (object.needsPaintPropertyUpdate()) {
+    bool effectNodeNeeded = false;
 
-  float opacity = style.opacity();
-  if (opacity != 1.0f)
-    effectNodeNeeded = true;
+    float opacity = style.opacity();
+    if (opacity != 1.0f)
+      effectNodeNeeded = true;
 
-  CompositorFilterOperations filter;
-  if (object.isSVG() && !object.isSVGRoot()) {
-    // TODO(trchen): SVG caches filters in SVGResources. Implement it.
-  } else if (PaintLayer* layer = toLayoutBoxModelObject(object).layer()) {
-    // TODO(trchen): Eliminate PaintLayer dependency.
-    filter = layer->createCompositorFilterOperationsForFilter(style);
-  }
+    CompositorFilterOperations filter;
+    if (object.isSVG() && !object.isSVGRoot()) {
+      // TODO(trchen): SVG caches filters in SVGResources. Implement it.
+    } else if (PaintLayer* layer = toLayoutBoxModelObject(object).layer()) {
+      // TODO(trchen): Eliminate PaintLayer dependency.
+      filter = layer->createCompositorFilterOperationsForFilter(style);
+    }
 
-  const ClipPaintPropertyNode* outputClip = ClipPaintPropertyNode::root();
-  // The CSS filter spec didn't specify how filters interact with overflow
-  // clips. The implementation here mimics the old Blink/WebKit behavior for
-  // backward compatibility.
-  // Basically the output of the filter will be affected by clips that applies
-  // to the current element. The descendants that paints into the input of the
-  // filter ignores any clips collected so far. For example:
-  // <div style="overflow:scroll">
-  //   <div style="filter:blur(1px);">
-  //     <div>A</div>
-  //     <div style="position:absolute;">B</div>
-  //   </div>
-  // </div>
-  // In this example "A" should be clipped if the filter was not present.
-  // With the filter, "A" will be rastered without clipping, but instead
-  // the blurred result will be clipped.
-  // On the other hand, "B" should not be clipped because the overflow clip is
-  // not in its containing block chain, but as the filter output will be
-  // clipped, so a blurred "B" may still be invisible.
-  if (!filter.isEmpty()) {
-    effectNodeNeeded = true;
-    outputClip = context.current.clip;
+    const ClipPaintPropertyNode* outputClip = ClipPaintPropertyNode::root();
+    // The CSS filter spec didn't specify how filters interact with overflow
+    // clips. The implementation here mimics the old Blink/WebKit behavior for
+    // backward compatibility.
+    // Basically the output of the filter will be affected by clips that applies
+    // to the current element. The descendants that paints into the input of the
+    // filter ignores any clips collected so far. For example:
+    // <div style="overflow:scroll">
+    //   <div style="filter:blur(1px);">
+    //     <div>A</div>
+    //     <div style="position:absolute;">B</div>
+    //   </div>
+    // </div>
+    // In this example "A" should be clipped if the filter was not present.
+    // With the filter, "A" will be rastered without clipping, but instead
+    // the blurred result will be clipped.
+    // On the other hand, "B" should not be clipped because the overflow clip is
+    // not in its containing block chain, but as the filter output will be
+    // clipped, so a blurred "B" may still be invisible.
+    if (!filter.isEmpty()) {
+      effectNodeNeeded = true;
+      outputClip = context.current.clip;
 
-    // TODO(trchen): A filter may contain spatial operations such that an
-    // output pixel may depend on an input pixel outside of the output clip.
-    // We should generate a special clip node to represent this expansion.
-  }
+      // TODO(trchen): A filter may contain spatial operations such that an
+      // output pixel may depend on an input pixel outside of the output clip.
+      // We should generate a special clip node to represent this expansion.
+    }
 
-  if (effectNodeNeeded) {
-    object.getMutableForPainting().ensurePaintProperties().updateEffect(
-        context.currentEffect, context.current.transform, outputClip,
-        std::move(filter), opacity);
-  } else {
-    if (auto* properties = object.getMutableForPainting().paintProperties())
-      properties->clearEffect();
+    if (effectNodeNeeded) {
+      object.getMutableForPainting().ensurePaintProperties().updateEffect(
+          context.currentEffect, context.current.transform, outputClip,
+          std::move(filter), opacity);
+    } else {
+      if (auto* properties = object.getMutableForPainting().paintProperties())
+        properties->clearEffect();
+    }
   }
 
   const auto* properties = object.paintProperties();
@@ -407,20 +426,22 @@
 void PaintPropertyTreeBuilder::updateCssClip(
     const LayoutObject& object,
     PaintPropertyTreeBuilderContext& context) {
-  if (object.hasClip()) {
-    // Create clip node for descendants that are not fixed position.
-    // We don't have to setup context.absolutePosition.clip here because this
-    // object must be a container for absolute position descendants, and will
-    // copy from in-flow context later at updateOutOfFlowContext() step.
-    DCHECK(object.canContainAbsolutePositionObjects());
-    LayoutRect clipRect =
-        toLayoutBox(object).clipRect(context.current.paintOffset);
-    object.getMutableForPainting().ensurePaintProperties().updateCssClip(
-        context.current.clip, context.current.transform,
-        FloatRoundedRect(FloatRect(clipRect)));
-  } else {
-    if (auto* properties = object.getMutableForPainting().paintProperties())
-      properties->clearCssClip();
+  if (object.needsPaintPropertyUpdate()) {
+    if (object.hasClip()) {
+      // Create clip node for descendants that are not fixed position.
+      // We don't have to setup context.absolutePosition.clip here because this
+      // object must be a container for absolute position descendants, and will
+      // copy from in-flow context later at updateOutOfFlowContext() step.
+      DCHECK(object.canContainAbsolutePositionObjects());
+      LayoutRect clipRect =
+          toLayoutBox(object).clipRect(context.current.paintOffset);
+      object.getMutableForPainting().ensurePaintProperties().updateCssClip(
+          context.current.clip, context.current.transform,
+          FloatRoundedRect(FloatRect(clipRect)));
+    } else {
+      if (auto* properties = object.getMutableForPainting().paintProperties())
+        properties->clearCssClip();
+    }
   }
 
   const auto* properties = object.paintProperties();
@@ -431,6 +452,9 @@
 void PaintPropertyTreeBuilder::updateLocalBorderBoxContext(
     const LayoutObject& object,
     PaintPropertyTreeBuilderContext& context) {
+  if (!object.needsPaintPropertyUpdate())
+    return;
+
   // Avoid adding an ObjectPaintProperties for non-boxes to save memory, since
   // we don't need them at the moment.
   if (!object.isBox() && !object.hasLayer()) {
@@ -454,6 +478,9 @@
 void PaintPropertyTreeBuilder::updateScrollbarPaintOffset(
     const LayoutObject& object,
     const PaintPropertyTreeBuilderContext& context) {
+  if (!object.needsPaintPropertyUpdate())
+    return;
+
   bool needsScrollbarPaintOffset = false;
   IntPoint roundedPaintOffset = roundedIntPoint(context.current.paintOffset);
   if (roundedPaintOffset != IntPoint() && object.isBoxModelObject()) {
@@ -478,6 +505,10 @@
 void PaintPropertyTreeBuilder::updateMainThreadScrollingReasons(
     const LayoutObject& object,
     PaintPropertyTreeBuilderContext& context) {
+  // TODO(pdr): Mark properties as needing an update for main thread scroll
+  // reasons and ensure reason changes are propagated to ancestors to account
+  // for the parent walk below. https://crbug.com/664672.
+
   if (context.current.scroll &&
       !object.document().settings()->threadedScrollingEnabled()) {
     context.current.scroll->addMainThreadScrollingReasons(
@@ -502,45 +533,48 @@
     PaintPropertyTreeBuilderContext& context) {
   if (!object.isBox())
     return;
-  const LayoutBox& box = toLayoutBox(object);
-  // The <input> elements can't have contents thus CSS overflow property
-  // doesn't apply.  However for layout purposes we do generate child layout
-  // objects for them, e.g. button label.  We should clip the overflow from
-  // those children. This is called control clip and we technically treat them
-  // like overflow clip.
-  LayoutRect clipRect;
-  if (box.hasControlClip()) {
-    clipRect = box.controlClipRect(context.current.paintOffset);
-  } else if (box.hasOverflowClip() || box.styleRef().containsPaint() ||
-             (box.isSVGRoot() &&
-              toLayoutSVGRoot(box).shouldApplyViewportClip())) {
-    clipRect = LayoutRect(
-        pixelSnappedIntRect(box.overflowClipRect(context.current.paintOffset)));
-  } else {
-    if (auto* properties = object.getMutableForPainting().paintProperties()) {
-      properties->clearInnerBorderRadiusClip();
-      properties->clearOverflowClip();
+
+  if (object.needsPaintPropertyUpdate()) {
+    const LayoutBox& box = toLayoutBox(object);
+    // The <input> elements can't have contents thus CSS overflow property
+    // doesn't apply.  However for layout purposes we do generate child layout
+    // objects for them, e.g. button label.  We should clip the overflow from
+    // those children. This is called control clip and we technically treat them
+    // like overflow clip.
+    LayoutRect clipRect;
+    if (box.hasControlClip()) {
+      clipRect = box.controlClipRect(context.current.paintOffset);
+    } else if (box.hasOverflowClip() || box.styleRef().containsPaint() ||
+               (box.isSVGRoot() &&
+                toLayoutSVGRoot(box).shouldApplyViewportClip())) {
+      clipRect = LayoutRect(pixelSnappedIntRect(
+          box.overflowClipRect(context.current.paintOffset)));
+    } else {
+      if (auto* properties = object.getMutableForPainting().paintProperties()) {
+        properties->clearInnerBorderRadiusClip();
+        properties->clearOverflowClip();
+      }
+      return;
     }
-    return;
-  }
 
-  const auto* currentClip = context.current.clip;
-  if (box.styleRef().hasBorderRadius()) {
-    auto innerBorder = box.styleRef().getRoundedInnerBorderFor(
-        LayoutRect(context.current.paintOffset, box.size()));
-    object.getMutableForPainting()
-        .ensurePaintProperties()
-        .updateInnerBorderRadiusClip(context.current.clip,
-                                     context.current.transform, innerBorder);
-    currentClip = object.paintProperties()->innerBorderRadiusClip();
-  } else if (auto* properties =
-                 object.getMutableForPainting().paintProperties()) {
-    properties->clearInnerBorderRadiusClip();
-  }
+    const auto* currentClip = context.current.clip;
+    if (box.styleRef().hasBorderRadius()) {
+      auto innerBorder = box.styleRef().getRoundedInnerBorderFor(
+          LayoutRect(context.current.paintOffset, box.size()));
+      object.getMutableForPainting()
+          .ensurePaintProperties()
+          .updateInnerBorderRadiusClip(context.current.clip,
+                                       context.current.transform, innerBorder);
+      currentClip = object.paintProperties()->innerBorderRadiusClip();
+    } else if (auto* properties =
+                   object.getMutableForPainting().paintProperties()) {
+      properties->clearInnerBorderRadiusClip();
+    }
 
-  object.getMutableForPainting().ensurePaintProperties().updateOverflowClip(
-      currentClip, context.current.transform,
-      FloatRoundedRect(FloatRect(clipRect)));
+    object.getMutableForPainting().ensurePaintProperties().updateOverflowClip(
+        currentClip, context.current.transform,
+        FloatRoundedRect(FloatRect(clipRect)));
+  }
 
   const auto* properties = object.paintProperties();
   if (properties && properties->overflowClip())
@@ -558,22 +592,24 @@
 void PaintPropertyTreeBuilder::updatePerspective(
     const LayoutObject& object,
     PaintPropertyTreeBuilderContext& context) {
-  const ComputedStyle& style = object.styleRef();
-  if (object.isBox() && style.hasPerspective()) {
-    // The perspective node must not flatten (else nothing will get
-    // perspective), but it should still extend the rendering context as
-    // most transform nodes do.
-    TransformationMatrix matrix =
-        TransformationMatrix().applyPerspective(style.perspective());
-    FloatPoint3D origin = perspectiveOrigin(toLayoutBox(object)) +
-                          toLayoutSize(context.current.paintOffset);
-    object.getMutableForPainting().ensurePaintProperties().updatePerspective(
-        context.current.transform, matrix, origin,
-        context.current.shouldFlattenInheritedTransform,
-        context.current.renderingContextID);
-  } else {
-    if (auto* properties = object.getMutableForPainting().paintProperties())
-      properties->clearPerspective();
+  if (object.needsPaintPropertyUpdate()) {
+    const ComputedStyle& style = object.styleRef();
+    if (object.isBox() && style.hasPerspective()) {
+      // The perspective node must not flatten (else nothing will get
+      // perspective), but it should still extend the rendering context as
+      // most transform nodes do.
+      TransformationMatrix matrix =
+          TransformationMatrix().applyPerspective(style.perspective());
+      FloatPoint3D origin = perspectiveOrigin(toLayoutBox(object)) +
+                            toLayoutSize(context.current.paintOffset);
+      object.getMutableForPainting().ensurePaintProperties().updatePerspective(
+          context.current.transform, matrix, origin,
+          context.current.shouldFlattenInheritedTransform,
+          context.current.renderingContextID);
+    } else {
+      if (auto* properties = object.getMutableForPainting().paintProperties())
+        properties->clearPerspective();
+    }
   }
 
   const auto* properties = object.paintProperties();
@@ -589,17 +625,19 @@
   if (!object.isSVGRoot())
     return;
 
-  AffineTransform transformToBorderBox =
-      SVGRootPainter(toLayoutSVGRoot(object))
-          .transformToPixelSnappedBorderBox(context.current.paintOffset);
-  if (!transformToBorderBox.isIdentity()) {
-    object.getMutableForPainting()
-        .ensurePaintProperties()
-        .updateSvgLocalToBorderBoxTransform(
-            context.current.transform, transformToBorderBox, FloatPoint3D());
-  } else {
-    if (auto* properties = object.getMutableForPainting().paintProperties())
-      properties->clearSvgLocalToBorderBoxTransform();
+  if (object.needsPaintPropertyUpdate()) {
+    AffineTransform transformToBorderBox =
+        SVGRootPainter(toLayoutSVGRoot(object))
+            .transformToPixelSnappedBorderBox(context.current.paintOffset);
+    if (!transformToBorderBox.isIdentity()) {
+      object.getMutableForPainting()
+          .ensurePaintProperties()
+          .updateSvgLocalToBorderBoxTransform(
+              context.current.transform, transformToBorderBox, FloatPoint3D());
+    } else {
+      if (auto* properties = object.getMutableForPainting().paintProperties())
+        properties->clearSvgLocalToBorderBoxTransform();
+    }
   }
 
   const auto* properties = object.paintProperties();
@@ -616,37 +654,39 @@
 void PaintPropertyTreeBuilder::updateScrollAndScrollTranslation(
     const LayoutObject& object,
     PaintPropertyTreeBuilderContext& context) {
-  if (object.hasOverflowClip()) {
-    const LayoutBox& box = toLayoutBox(object);
-    const PaintLayerScrollableArea* scrollableArea = box.getScrollableArea();
-    IntSize scrollOffset = box.scrolledContentOffset();
-    if (!scrollOffset.isZero() || scrollableArea->scrollsOverflow()) {
-      TransformationMatrix matrix = TransformationMatrix().translate(
-          -scrollOffset.width(), -scrollOffset.height());
-      object.getMutableForPainting()
-          .ensurePaintProperties()
-          .updateScrollTranslation(
-              context.current.transform, matrix, FloatPoint3D(),
-              context.current.shouldFlattenInheritedTransform,
-              context.current.renderingContextID);
+  if (object.needsPaintPropertyUpdate()) {
+    if (object.hasOverflowClip()) {
+      const LayoutBox& box = toLayoutBox(object);
+      const PaintLayerScrollableArea* scrollableArea = box.getScrollableArea();
+      IntSize scrollOffset = box.scrolledContentOffset();
+      if (!scrollOffset.isZero() || scrollableArea->scrollsOverflow()) {
+        TransformationMatrix matrix = TransformationMatrix().translate(
+            -scrollOffset.width(), -scrollOffset.height());
+        object.getMutableForPainting()
+            .ensurePaintProperties()
+            .updateScrollTranslation(
+                context.current.transform, matrix, FloatPoint3D(),
+                context.current.shouldFlattenInheritedTransform,
+                context.current.renderingContextID);
 
-      IntSize scrollClip = scrollableArea->visibleContentRect().size();
-      IntSize scrollBounds = scrollableArea->contentsSize();
-      bool userScrollableHorizontal =
-          scrollableArea->userInputScrollable(HorizontalScrollbar);
-      bool userScrollableVertical =
-          scrollableArea->userInputScrollable(VerticalScrollbar);
-      object.getMutableForPainting().ensurePaintProperties().updateScroll(
-          context.current.scroll, object.paintProperties()->scrollTranslation(),
-          scrollClip, scrollBounds, userScrollableHorizontal,
-          userScrollableVertical);
-    } else {
-      // Ensure pre-existing properties are cleared when there is no
-      // scrolling.
-      auto* properties = object.getMutableForPainting().paintProperties();
-      if (properties) {
-        properties->clearScrollTranslation();
-        properties->clearScroll();
+        IntSize scrollClip = scrollableArea->visibleContentRect().size();
+        IntSize scrollBounds = scrollableArea->contentsSize();
+        bool userScrollableHorizontal =
+            scrollableArea->userInputScrollable(HorizontalScrollbar);
+        bool userScrollableVertical =
+            scrollableArea->userInputScrollable(VerticalScrollbar);
+        object.getMutableForPainting().ensurePaintProperties().updateScroll(
+            context.current.scroll,
+            object.paintProperties()->scrollTranslation(), scrollClip,
+            scrollBounds, userScrollableHorizontal, userScrollableVertical);
+      } else {
+        // Ensure pre-existing properties are cleared when there is no
+        // scrolling.
+        auto* properties = object.getMutableForPainting().paintProperties();
+        if (properties) {
+          properties->clearScrollTranslation();
+          properties->clearScroll();
+        }
       }
     }
   }
@@ -696,12 +736,14 @@
     if (context.fixedPosition.clip == cssClip->parent()) {
       context.fixedPosition.clip = cssClip;
     } else {
-      object.getMutableForPainting()
-          .ensurePaintProperties()
-          .updateCssClipFixedPosition(context.fixedPosition.clip,
-                                      const_cast<TransformPaintPropertyNode*>(
-                                          cssClip->localTransformSpace()),
-                                      cssClip->clipRect());
+      if (object.needsPaintPropertyUpdate()) {
+        object.getMutableForPainting()
+            .ensurePaintProperties()
+            .updateCssClipFixedPosition(context.fixedPosition.clip,
+                                        const_cast<TransformPaintPropertyNode*>(
+                                            cssClip->localTransformSpace()),
+                                        cssClip->clipRect());
+      }
       const auto* properties = object.paintProperties();
       if (properties && properties->cssClipFixedPosition())
         context.fixedPosition.clip = properties->cssClipFixedPosition();
@@ -709,8 +751,10 @@
     }
   }
 
-  if (auto* properties = object.getMutableForPainting().paintProperties())
-    properties->clearCssClipFixedPosition();
+  if (object.needsPaintPropertyUpdate()) {
+    if (auto* properties = object.getMutableForPainting().paintProperties())
+      properties->clearCssClipFixedPosition();
+  }
 }
 
 // Override ContainingBlockContext based on the properties of a containing block
@@ -835,12 +879,16 @@
   }
 }
 
-void PaintPropertyTreeBuilder::updatePropertiesAndContextForSelf(
+void PaintPropertyTreeBuilder::updatePropertiesForSelf(
     const LayoutObject& object,
     PaintPropertyTreeBuilderContext& context) {
   if (!object.isBoxModelObject() && !object.isSVG())
     return;
 
+#if DCHECK_IS_ON()
+  FindObjectPropertiesNeedingUpdateScope checkNeedsUpdateScope(object);
+#endif
+
   deriveBorderBoxFromContainerContext(object, context);
 
   updatePaintOffsetTranslation(object, context);
@@ -852,12 +900,16 @@
   updateMainThreadScrollingReasons(object, context);
 }
 
-void PaintPropertyTreeBuilder::updatePropertiesAndContextForChildren(
+void PaintPropertyTreeBuilder::updatePropertiesForChildren(
     const LayoutObject& object,
     PaintPropertyTreeBuilderContext& context) {
   if (!object.isBoxModelObject() && !object.isSVG())
     return;
 
+#if DCHECK_IS_ON()
+  FindObjectPropertiesNeedingUpdateScope checkNeedsUpdateScope(object);
+#endif
+
   updateOverflowClip(object, context);
   updatePerspective(object, context);
   updateSvgLocalToBorderBoxTransform(object, context);
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.h b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.h
index 8ea21046..35414d90 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.h
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.h
@@ -84,17 +84,16 @@
   PaintPropertyTreeBuilderContext setupInitialContext();
   // Update the paint properties for a frame and ensure the context is up to
   // date.
-  void updateFramePropertiesAndContext(FrameView&,
-                                       PaintPropertyTreeBuilderContext&);
+  void updateProperties(FrameView&, PaintPropertyTreeBuilderContext&);
 
   // Update the paint properties that affect this object (e.g., properties like
   // paint offset translation) and ensure the context is up to date.
-  void updatePropertiesAndContextForSelf(const LayoutObject&,
-                                         PaintPropertyTreeBuilderContext&);
+  void updatePropertiesForSelf(const LayoutObject&,
+                               PaintPropertyTreeBuilderContext&);
   // Update the paint properties that affect children of this object (e.g.,
   // scroll offset transform) and ensure the context is up to date.
-  void updatePropertiesAndContextForChildren(const LayoutObject&,
-                                             PaintPropertyTreeBuilderContext&);
+  void updatePropertiesForChildren(const LayoutObject&,
+                                   PaintPropertyTreeBuilderContext&);
 
  private:
   static void updatePaintOffsetTranslation(const LayoutObject&,
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
index accf69f..dfc4dd7 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
@@ -2626,8 +2626,11 @@
                       MainThreadScrollingReason::kThreadedScrollingDisabled));
 }
 
-TEST_P(PaintPropertyTreeBuilderTest,
-       BackgroundAttachmentFixedMainThreadScrollReasonsWithNestedScrollers) {
+// Disabled due to missing main thread scrolling property invalidation support.
+// See: https://crbug.com/664672
+TEST_P(
+    PaintPropertyTreeBuilderTest,
+    DISABLED_BackgroundAttachmentFixedMainThreadScrollReasonsWithNestedScrollers) {
   setBodyInnerHTML(
       "<style>"
       "  #overflowA {"
@@ -2712,8 +2715,11 @@
               MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects));
 }
 
-TEST_P(PaintPropertyTreeBuilderTest,
-       BackgroundAttachmentFixedMainThreadScrollReasonsWithFixedScroller) {
+// Disabled due to missing main thread scrolling property invalidation support.
+// See: https://crbug.com/664672
+TEST_P(
+    PaintPropertyTreeBuilderTest,
+    DISABLED_BackgroundAttachmentFixedMainThreadScrollReasonsWithFixedScroller) {
   setBodyInnerHTML(
       "<style>"
       "  #overflowA {"
diff --git a/third_party/WebKit/Source/core/paint/PaintResult.h b/third_party/WebKit/Source/core/paint/PaintResult.h
new file mode 100644
index 0000000..a755ac8
--- /dev/null
+++ b/third_party/WebKit/Source/core/paint/PaintResult.h
@@ -0,0 +1,25 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PaintResult_h
+#define PaintResult_h
+
+namespace blink {
+
+// Used as the return type of some paint methods.
+enum PaintResult {
+  // The layer/object is fully painted. This includes cases that nothing needs
+  // painting regardless of the paint rect.
+  FullyPainted,
+  // Some part of the layer/object is out of the paint rect and may be not fully
+  // painted.  The results cannot be cached because they may change when paint
+  // rect changes.
+  MayBeClippedByPaintDirtyRect,
+
+  MaxPaintResult = MayBeClippedByPaintDirtyRect,
+};
+
+}  // namespace blink
+
+#endif  // PaintResult.h
diff --git a/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp b/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp
index 3382c63..bc59d57 100644
--- a/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp
+++ b/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp
@@ -20,6 +20,7 @@
         paintInvalidatorContext(treeBuilderContext,
                                 parentContext.paintInvalidatorContext) {}
 
+  bool needToUpdatePaintPropertySubtree = false;
   PaintPropertyTreeBuilderContext treeBuilderContext;
   PaintInvalidatorContext paintInvalidatorContext;
 };
@@ -41,8 +42,26 @@
     return;
 
   PrePaintTreeWalkContext localContext(context);
-  m_propertyTreeBuilder.updateFramePropertiesAndContext(
-      frameView, localContext.treeBuilderContext);
+
+  // Check whether we need to update the paint property trees.
+  if (!localContext.needToUpdatePaintPropertySubtree) {
+    if (context.paintInvalidatorContext.forcedSubtreeInvalidationFlags) {
+      // forcedSubtreeInvalidationFlags will be true if locations have changed
+      // which will affect paint properties (e.g., PaintOffset).
+      localContext.needToUpdatePaintPropertySubtree = true;
+    } else if (frameView.needsPaintPropertyUpdate()) {
+      localContext.needToUpdatePaintPropertySubtree = true;
+    }
+  }
+  // Paint properties can depend on their ancestor properties so ensure the
+  // entire subtree is rebuilt on any changes.
+  // TODO(pdr): Add additional granularity to the needs update approach such as
+  // the ability to do local updates that don't change the subtree.
+  if (localContext.needToUpdatePaintPropertySubtree)
+    frameView.setNeedsPaintPropertyUpdate();
+
+  m_propertyTreeBuilder.updateProperties(frameView,
+                                         localContext.treeBuilderContext);
 
   m_paintInvalidator.invalidatePaintIfNeeded(
       frameView, localContext.paintInvalidatorContext);
@@ -53,12 +72,42 @@
 #if DCHECK_IS_ON()
   frameView.layoutView()->assertSubtreeClearedPaintInvalidationFlags();
 #endif
+
+  frameView.clearNeedsPaintPropertyUpdate();
 }
 
 void PrePaintTreeWalk::walk(const LayoutObject& object,
                             const PrePaintTreeWalkContext& context) {
   PrePaintTreeWalkContext localContext(context);
 
+  // Check whether we need to update the paint property trees.
+  if (!localContext.needToUpdatePaintPropertySubtree) {
+    if (context.paintInvalidatorContext.forcedSubtreeInvalidationFlags) {
+      // forcedSubtreeInvalidationFlags will be true if locations have changed
+      // which will affect paint properties (e.g., PaintOffset).
+      localContext.needToUpdatePaintPropertySubtree = true;
+    } else if (object.needsPaintPropertyUpdate()) {
+      localContext.needToUpdatePaintPropertySubtree = true;
+    } else if (object.mayNeedPaintInvalidation()) {
+      // mayNeedpaintInvalidation will be true when locations change which will
+      // affect paint properties (e.g., PaintOffset).
+      localContext.needToUpdatePaintPropertySubtree = true;
+    } else if (object.shouldDoFullPaintInvalidation()) {
+      // shouldDoFullPaintInvalidation will be true when locations or overflow
+      // changes which will affect paint properties (e.g., PaintOffset, scroll).
+      localContext.needToUpdatePaintPropertySubtree = true;
+    }
+  }
+
+  // Paint properties can depend on their ancestor properties so ensure the
+  // entire subtree is rebuilt on any changes.
+  // TODO(pdr): Add additional granularity to the needs update approach such as
+  // the ability to do local updates that don't change the subtree.
+  if (localContext.needToUpdatePaintPropertySubtree)
+    object.getMutableForPainting().setNeedsPaintPropertyUpdate();
+
+  // TODO(pdr): Ensure multi column works with incremental property tree
+  // construction.
   if (object.isLayoutMultiColumnSpannerPlaceholder()) {
     // Walk multi-column spanner as if it replaces the placeholder.
     // Set the flag so that the tree builder can specially handle out-of-flow
@@ -72,11 +121,11 @@
     return;
   }
 
-  m_propertyTreeBuilder.updatePropertiesAndContextForSelf(
+  m_propertyTreeBuilder.updatePropertiesForSelf(
       object, localContext.treeBuilderContext);
   m_paintInvalidator.invalidatePaintIfNeeded(
       object, localContext.paintInvalidatorContext);
-  m_propertyTreeBuilder.updatePropertiesAndContextForChildren(
+  m_propertyTreeBuilder.updatePropertiesForChildren(
       object, localContext.treeBuilderContext);
 
   for (const LayoutObject* child = object.slowFirstChild(); child;
@@ -100,7 +149,9 @@
     }
     // TODO(pdr): Investigate RemoteFrameView (crbug.com/579281).
   }
+
   object.getMutableForPainting().clearPaintInvalidationFlags();
+  object.getMutableForPainting().clearNeedsPaintPropertyUpdate();
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/PrePaintTreeWalkTest.cpp b/third_party/WebKit/Source/core/paint/PrePaintTreeWalkTest.cpp
new file mode 100644
index 0000000..c651f90
--- /dev/null
+++ b/third_party/WebKit/Source/core/paint/PrePaintTreeWalkTest.cpp
@@ -0,0 +1,155 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/layout/LayoutTestHelper.h"
+#include "core/layout/LayoutTreeAsText.h"
+#include "core/layout/api/LayoutViewItem.h"
+#include "core/paint/ObjectPaintProperties.h"
+#include "core/paint/PaintPropertyTreePrinter.h"
+#include "platform/graphics/paint/GeometryMapper.h"
+#include "platform/graphics/paint/ScrollPaintPropertyNode.h"
+#include "platform/graphics/paint/TransformPaintPropertyNode.h"
+#include "platform/testing/RuntimeEnabledFeaturesTestHelpers.h"
+#include "platform/testing/UnitTestHelpers.h"
+#include "platform/text/TextStream.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "wtf/HashMap.h"
+#include "wtf/Vector.h"
+
+namespace blink {
+
+typedef bool TestParamRootLayerScrolling;
+class PrePaintTreeWalkTest
+    : public ::testing::WithParamInterface<TestParamRootLayerScrolling>,
+      private ScopedSlimmingPaintV2ForTest,
+      private ScopedRootLayerScrollingForTest,
+      public RenderingTest {
+ public:
+  PrePaintTreeWalkTest()
+      : ScopedSlimmingPaintV2ForTest(true),
+        ScopedRootLayerScrollingForTest(GetParam()),
+        RenderingTest(SingleChildFrameLoaderClient::create()) {}
+
+  const TransformPaintPropertyNode* framePreTranslation() {
+    FrameView* frameView = document().view();
+    if (RuntimeEnabledFeatures::rootLayerScrollingEnabled()) {
+      return frameView->layoutView()
+          ->paintProperties()
+          ->paintOffsetTranslation();
+    }
+    return frameView->preTranslation();
+  }
+
+  const TransformPaintPropertyNode* frameScrollTranslation() {
+    FrameView* frameView = document().view();
+    if (RuntimeEnabledFeatures::rootLayerScrollingEnabled()) {
+      return frameView->layoutView()->paintProperties()->scrollTranslation();
+    }
+    return frameView->scrollTranslation();
+  }
+
+ private:
+  void SetUp() override {
+    Settings::setMockScrollbarsEnabled(true);
+
+    RenderingTest::SetUp();
+    enableCompositing();
+  }
+
+  void TearDown() override {
+    RenderingTest::TearDown();
+
+    Settings::setMockScrollbarsEnabled(false);
+  }
+};
+
+INSTANTIATE_TEST_CASE_P(All, PrePaintTreeWalkTest, ::testing::Bool());
+
+TEST_P(PrePaintTreeWalkTest, PropertyTreesRebuiltWithBorderInvalidation) {
+  setBodyInnerHTML(
+      "<style>"
+      "  body { margin: 0; }"
+      "  #transformed { transform: translate(100px, 100px); }"
+      "  .border { border: 10px solid black; }"
+      "</style>"
+      "<div id='transformed'></div>");
+
+  auto* transformedElement = document().getElementById("transformed");
+  const auto* transformedProperties =
+      transformedElement->layoutObject()->paintProperties();
+  EXPECT_EQ(TransformationMatrix().translate(100, 100),
+            transformedProperties->transform()->matrix());
+
+  // Artifically change the transform node.
+  const_cast<ObjectPaintProperties*>(transformedProperties)->clearTransform();
+  EXPECT_EQ(nullptr, transformedProperties->transform());
+
+  // Cause a paint invalidation.
+  transformedElement->setAttribute(HTMLNames::classAttr, "border");
+  document().view()->updateAllLifecyclePhases();
+
+  // Should have changed back.
+  EXPECT_EQ(TransformationMatrix().translate(100, 100),
+            transformedProperties->transform()->matrix());
+}
+
+TEST_P(PrePaintTreeWalkTest, PropertyTreesRebuiltWithFrameScroll) {
+  setBodyInnerHTML("<style> body { height: 10000px; } </style>");
+  EXPECT_EQ(TransformationMatrix().translate(0, 0),
+            frameScrollTranslation()->matrix());
+
+  // Cause a scroll invalidation and ensure the translation is updated.
+  document().domWindow()->scrollTo(0, 100);
+  document().view()->updateAllLifecyclePhases();
+
+  EXPECT_EQ(TransformationMatrix().translate(0, -100),
+            frameScrollTranslation()->matrix());
+}
+
+TEST_P(PrePaintTreeWalkTest, PropertyTreesRebuiltWithCSSTransformInvalidation) {
+  setBodyInnerHTML(
+      "<style>"
+      "  .transformA { transform: translate(100px, 100px); }"
+      "  .transformB { transform: translate(200px, 200px); }"
+      "  #transformed { will-change: transform; }"
+      "</style>"
+      "<div id='transformed' class='transformA'></div>");
+
+  auto* transformedElement = document().getElementById("transformed");
+  const auto* transformedProperties =
+      transformedElement->layoutObject()->paintProperties();
+  EXPECT_EQ(TransformationMatrix().translate(100, 100),
+            transformedProperties->transform()->matrix());
+
+  // Invalidate the CSS transform property.
+  transformedElement->setAttribute(HTMLNames::classAttr, "transformB");
+  document().view()->updateAllLifecyclePhases();
+
+  // The transform should have changed.
+  EXPECT_EQ(TransformationMatrix().translate(200, 200),
+            transformedProperties->transform()->matrix());
+}
+
+TEST_P(PrePaintTreeWalkTest, PropertyTreesRebuiltWithOpacityInvalidation) {
+  setBodyInnerHTML(
+      "<style>"
+      "  .opacityA { opacity: 0.9; }"
+      "  .opacityB { opacity: 0.4; }"
+      "</style>"
+      "<div id='transparent' class='opacityA'></div>");
+
+  auto* transparentElement = document().getElementById("transparent");
+  const auto* transparentProperties =
+      transparentElement->layoutObject()->paintProperties();
+  EXPECT_EQ(0.9f, transparentProperties->effect()->opacity());
+
+  // Invalidate the opacity property.
+  transparentElement->setAttribute(HTMLNames::classAttr, "opacityB");
+  document().view()->updateAllLifecyclePhases();
+
+  // The opacity should have changed.
+  EXPECT_EQ(0.4f, transparentProperties->effect()->opacity());
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/ScrollbarManager.cpp b/third_party/WebKit/Source/core/paint/ScrollbarManager.cpp
index 48c0173..a267065 100644
--- a/third_party/WebKit/Source/core/paint/ScrollbarManager.cpp
+++ b/third_party/WebKit/Source/core/paint/ScrollbarManager.cpp
@@ -17,6 +17,15 @@
   visitor->trace(m_vBar);
 }
 
+void ScrollbarManager::setScroller(ScrollableArea* scroller) {
+  DCHECK(m_scrollableArea != scroller);
+  DCHECK(scroller);
+  DCHECK(scroller->isRootFrameViewport() || scroller->isFrameView() ||
+         scroller->isPaintLayerScrollableArea());
+  dispose();
+  m_scrollableArea = scroller;
+}
+
 void ScrollbarManager::dispose() {
   m_hBarIsAttached = m_vBarIsAttached = 0;
   destroyScrollbar(HorizontalScrollbar);
diff --git a/third_party/WebKit/Source/core/paint/ScrollbarManager.h b/third_party/WebKit/Source/core/paint/ScrollbarManager.h
index ff638a85..e6b5781 100644
--- a/third_party/WebKit/Source/core/paint/ScrollbarManager.h
+++ b/third_party/WebKit/Source/core/paint/ScrollbarManager.h
@@ -17,6 +17,11 @@
  public:
   ScrollbarManager(ScrollableArea&);
 
+  // TODO(crbug.com/661236): The scroller should be the one that owns this
+  // ScrollbarManager and this setter should be removed.
+  // The scroller that this scrollbar it attached to.
+  void setScroller(ScrollableArea*);
+
   void dispose();
 
   Scrollbar* horizontalScrollbar() const {
diff --git a/third_party/WebKit/Source/core/paint/TableCellPainter.cpp b/third_party/WebKit/Source/core/paint/TableCellPainter.cpp
index 2fc5973e..ae5d437 100644
--- a/third_party/WebKit/Source/core/paint/TableCellPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/TableCellPainter.cpp
@@ -19,40 +19,40 @@
     const ComputedStyle& styleForCellFlow,
     const LayoutTableCell::CollapsedBorderValues& values) {
   if (styleForCellFlow.isHorizontalWritingMode()) {
-    return styleForCellFlow.isLeftToRightDirection() ? values.startBorder()
-                                                     : values.endBorder();
+    return styleForCellFlow.isLeftToRightDirection() ? values.startBorder
+                                                     : values.endBorder;
   }
-  return styleForCellFlow.isFlippedBlocksWritingMode() ? values.afterBorder()
-                                                       : values.beforeBorder();
+  return styleForCellFlow.isFlippedBlocksWritingMode() ? values.afterBorder
+                                                       : values.beforeBorder;
 }
 
 static const CollapsedBorderValue& collapsedRightBorder(
     const ComputedStyle& styleForCellFlow,
     const LayoutTableCell::CollapsedBorderValues& values) {
   if (styleForCellFlow.isHorizontalWritingMode()) {
-    return styleForCellFlow.isLeftToRightDirection() ? values.endBorder()
-                                                     : values.startBorder();
+    return styleForCellFlow.isLeftToRightDirection() ? values.endBorder
+                                                     : values.startBorder;
   }
-  return styleForCellFlow.isFlippedBlocksWritingMode() ? values.beforeBorder()
-                                                       : values.afterBorder();
+  return styleForCellFlow.isFlippedBlocksWritingMode() ? values.beforeBorder
+                                                       : values.afterBorder;
 }
 
 static const CollapsedBorderValue& collapsedTopBorder(
     const ComputedStyle& styleForCellFlow,
     const LayoutTableCell::CollapsedBorderValues& values) {
   if (styleForCellFlow.isHorizontalWritingMode())
-    return values.beforeBorder();
-  return styleForCellFlow.isLeftToRightDirection() ? values.startBorder()
-                                                   : values.endBorder();
+    return values.beforeBorder;
+  return styleForCellFlow.isLeftToRightDirection() ? values.startBorder
+                                                   : values.endBorder;
 }
 
 static const CollapsedBorderValue& collapsedBottomBorder(
     const ComputedStyle& styleForCellFlow,
     const LayoutTableCell::CollapsedBorderValues& values) {
   if (styleForCellFlow.isHorizontalWritingMode())
-    return values.afterBorder();
-  return styleForCellFlow.isLeftToRightDirection() ? values.endBorder()
-                                                   : values.startBorder();
+    return values.afterBorder;
+  return styleForCellFlow.isLeftToRightDirection() ? values.endBorder
+                                                   : values.startBorder;
 }
 
 void TableCellPainter::paint(const PaintInfo& paintInfo,
@@ -68,15 +68,6 @@
   return style;
 }
 
-const DisplayItemClient& TableCellPainter::displayItemClientForBorders() const {
-  // TODO(wkorman): We may need to handle PaintInvalidationDelayedFull.
-  // http://crbug.com/657186
-  return m_layoutTableCell.usesTableAsAdditionalDisplayItemClient()
-             ? static_cast<const DisplayItemClient&>(
-                   *m_layoutTableCell.collapsedBorderValues())
-             : m_layoutTableCell;
-}
-
 void TableCellPainter::paintCollapsedBorders(
     const PaintInfo& paintInfo,
     const LayoutPoint& paintOffset,
@@ -104,18 +95,6 @@
   const CollapsedBorderValue& bottomBorderValue =
       collapsedBottomBorder(styleForCellFlow, *values);
 
-  int displayItemType = DisplayItem::kTableCollapsedBorderBase;
-  if (topBorderValue.shouldPaint(currentBorderValue))
-    displayItemType |= DisplayItem::TableCollapsedBorderTop;
-  if (bottomBorderValue.shouldPaint(currentBorderValue))
-    displayItemType |= DisplayItem::TableCollapsedBorderBottom;
-  if (leftBorderValue.shouldPaint(currentBorderValue))
-    displayItemType |= DisplayItem::TableCollapsedBorderLeft;
-  if (rightBorderValue.shouldPaint(currentBorderValue))
-    displayItemType |= DisplayItem::TableCollapsedBorderRight;
-  if (displayItemType == DisplayItem::kTableCollapsedBorderBase)
-    return;
-
   int topWidth = topBorderValue.width();
   int bottomWidth = bottomBorderValue.width();
   int leftWidth = leftBorderValue.width();
@@ -131,41 +110,32 @@
       paintRect.height() + topWidth / 2 + (bottomWidth + 1) / 2);
 
   GraphicsContext& graphicsContext = paintInfo.context;
-  const DisplayItemClient& client = displayItemClientForBorders();
-  if (DrawingRecorder::useCachedDrawingIfPossible(
-          graphicsContext, client,
-          static_cast<DisplayItem::Type>(displayItemType)))
-    return;
-
-  DrawingRecorder recorder(graphicsContext, client,
-                           static_cast<DisplayItem::Type>(displayItemType),
-                           borderRect);
   Color cellColor = m_layoutTableCell.resolveColor(CSSPropertyColor);
 
   // We never paint diagonals at the joins.  We simply let the border with the
   // highest precedence paint on top of borders with lower precedence.
-  if (displayItemType & DisplayItem::TableCollapsedBorderTop) {
+  if (topBorderValue.shouldPaint(currentBorderValue)) {
     ObjectPainter::drawLineForBoxSide(
         graphicsContext, borderRect.x(), borderRect.y(), borderRect.maxX(),
         borderRect.y() + topWidth, BSTop,
         topBorderValue.color().resolve(cellColor),
         collapsedBorderStyle(topBorderValue.style()), 0, 0, true);
   }
-  if (displayItemType & DisplayItem::TableCollapsedBorderBottom) {
+  if (bottomBorderValue.shouldPaint(currentBorderValue)) {
     ObjectPainter::drawLineForBoxSide(
         graphicsContext, borderRect.x(), borderRect.maxY() - bottomWidth,
         borderRect.maxX(), borderRect.maxY(), BSBottom,
         bottomBorderValue.color().resolve(cellColor),
         collapsedBorderStyle(bottomBorderValue.style()), 0, 0, true);
   }
-  if (displayItemType & DisplayItem::TableCollapsedBorderLeft) {
+  if (leftBorderValue.shouldPaint(currentBorderValue)) {
     ObjectPainter::drawLineForBoxSide(
         graphicsContext, borderRect.x(), borderRect.y(),
         borderRect.x() + leftWidth, borderRect.maxY(), BSLeft,
         leftBorderValue.color().resolve(cellColor),
         collapsedBorderStyle(leftBorderValue.style()), 0, 0, true);
   }
-  if (displayItemType & DisplayItem::TableCollapsedBorderRight) {
+  if (rightBorderValue.shouldPaint(currentBorderValue)) {
     ObjectPainter::drawLineForBoxSide(
         graphicsContext, borderRect.maxX() - rightWidth, borderRect.y(),
         borderRect.maxX(), borderRect.maxY(), BSRight,
diff --git a/third_party/WebKit/Source/core/paint/TableCellPainter.h b/third_party/WebKit/Source/core/paint/TableCellPainter.h
index a87c4b59..749915c 100644
--- a/third_party/WebKit/Source/core/paint/TableCellPainter.h
+++ b/third_party/WebKit/Source/core/paint/TableCellPainter.h
@@ -39,7 +39,6 @@
   void paintMask(const PaintInfo&, const LayoutPoint& paintOffset);
 
  private:
-  const DisplayItemClient& displayItemClientForBorders() const;
   LayoutRect paintRectNotIncludingVisualOverflow(
       const LayoutPoint& paintOffset);
   void paintBackground(const PaintInfo&,
diff --git a/third_party/WebKit/Source/core/paint/TableCellPainterTest.cpp b/third_party/WebKit/Source/core/paint/TableCellPainterTest.cpp
index 1cd5830..adce65d 100644
--- a/third_party/WebKit/Source/core/paint/TableCellPainterTest.cpp
+++ b/third_party/WebKit/Source/core/paint/TableCellPainterTest.cpp
@@ -170,11 +170,12 @@
       "100px solid yellow; background: green; }"
       "  table { margin: 100px; border-collapse: collapse; }"
       "</style>"
-      "<table>"
+      "<table id='table'>"
       "  <tr><td id='cell'></td></tr>"
       "</table>");
 
   LayoutView& layoutView = *document().layoutView();
+  LayoutObject& table = *getLayoutObjectByElementId("table");
   LayoutObject& cell = *getLayoutObjectByElementId("cell");
 
   rootPaintController().invalidateAll();
@@ -188,7 +189,7 @@
       rootPaintController().getDisplayItemList(), 4,
       TestDisplayItem(layoutView, DisplayItem::kDocumentBackground),
       TestDisplayItem(cell, DisplayItem::kBoxDecorationBackground),
-      TestDisplayItem(cell, DisplayItem::kTableCollapsedBorderLast),
+      TestDisplayItem(table, DisplayItem::kTableCollapsedBorders),
       TestDisplayItem(cell, DisplayItem::paintPhaseToDrawingType(
                                 PaintPhaseSelfOutlineOnly)));
 }
diff --git a/third_party/WebKit/Source/core/paint/TablePainter.cpp b/third_party/WebKit/Source/core/paint/TablePainter.cpp
index 16f910484..65a069c 100644
--- a/third_party/WebKit/Source/core/paint/TablePainter.cpp
+++ b/third_party/WebKit/Source/core/paint/TablePainter.cpp
@@ -44,31 +44,61 @@
       }
     }
 
-    if (m_layoutTable.collapseBorders() &&
-        shouldPaintDescendantBlockBackgrounds(paintPhase) &&
-        m_layoutTable.style()->visibility() == EVisibility::Visible) {
-      // Using our cached sorted styles, we then do individual passes,
-      // painting each style of border from lowest precedence to highest
-      // precedence.
-      LayoutTable::CollapsedBorderValues collapsedBorders =
-          m_layoutTable.collapsedBorders();
-      size_t count = collapsedBorders.size();
-      for (size_t i = 0; i < count; ++i) {
-        for (LayoutTableSection* section = m_layoutTable.bottomSection();
-             section; section = m_layoutTable.sectionAbove(section)) {
-          LayoutPoint childPoint =
-              m_layoutTable.flipForWritingModeForChild(section, paintOffset);
-          TableSectionPainter(*section).paintCollapsedBorders(
-              paintInfoForDescendants, childPoint, collapsedBorders[i]);
-        }
-      }
-    }
+    if (shouldPaintDescendantBlockBackgrounds(paintPhase))
+      paintCollapsedBorders(paintInfoForDescendants, paintOffset);
   }
 
   if (shouldPaintSelfOutline(paintPhase))
     ObjectPainter(m_layoutTable).paintOutline(paintInfo, paintOffset);
 }
 
+void TablePainter::paintCollapsedBorders(const PaintInfo& paintInfo,
+                                         const LayoutPoint& paintOffset) {
+  if (!m_layoutTable.hasCollapsedBorders() ||
+      m_layoutTable.style()->visibility() != EVisibility::Visible)
+    return;
+
+  LayoutTable::CollapsedBordersInfo& collapsedBorders =
+      m_layoutTable.getCollapsedBordersInfo();
+
+  // Normally we don't clip individual display items by paint dirty rect
+  // (aka interest rect), to keep display items independent with paint dirty
+  // rect so we can just depend on paint invalidation status to repaint them.
+  // However, the collapsed border display item may be too big to contain all
+  // collapsed borders in a huge table, so we clip it to paint dirty rect.
+  // We need to invalidate the display item if the previous paint is clipped
+  // and the paint dirty rect changed.
+  if (collapsedBorders.lastPaintResult != FullyPainted &&
+      collapsedBorders.lastPaintRect != paintInfo.cullRect())
+    m_layoutTable.setDisplayItemsUncached();
+
+  if (DrawingRecorder::useCachedDrawingIfPossible(
+          paintInfo.context, m_layoutTable,
+          DisplayItem::kTableCollapsedBorders))
+    return;
+
+  DrawingRecorder recorder(
+      paintInfo.context, m_layoutTable, DisplayItem::kTableCollapsedBorders,
+      FloatRect(LayoutRect(paintOffset, m_layoutTable.size())));
+
+  // Using our cached sorted styles, we then do individual passes, painting
+  // each style of border from lowest precedence to highest precedence.
+  PaintResult paintResult = FullyPainted;
+  for (const auto& borderValue : collapsedBorders.values) {
+    for (LayoutTableSection* section = m_layoutTable.bottomSection(); section;
+         section = m_layoutTable.sectionAbove(section)) {
+      LayoutPoint childPoint =
+          m_layoutTable.flipForWritingModeForChild(section, paintOffset);
+      if (TableSectionPainter(*section).paintCollapsedBorders(
+              paintInfo, childPoint, borderValue) ==
+          MayBeClippedByPaintDirtyRect)
+        paintResult = MayBeClippedByPaintDirtyRect;
+    }
+  }
+  collapsedBorders.lastPaintResult = paintResult;
+  collapsedBorders.lastPaintRect = paintInfo.cullRect();
+}
+
 void TablePainter::paintBoxDecorationBackground(
     const PaintInfo& paintInfo,
     const LayoutPoint& paintOffset) {
diff --git a/third_party/WebKit/Source/core/paint/TablePainter.h b/third_party/WebKit/Source/core/paint/TablePainter.h
index d3de8e0e..ac57899 100644
--- a/third_party/WebKit/Source/core/paint/TablePainter.h
+++ b/third_party/WebKit/Source/core/paint/TablePainter.h
@@ -24,6 +24,8 @@
   void paintMask(const PaintInfo&, const LayoutPoint&);
 
  private:
+  void paintCollapsedBorders(const PaintInfo&, const LayoutPoint&);
+
   const LayoutTable& m_layoutTable;
 };
 
diff --git a/third_party/WebKit/Source/core/paint/TablePainterTest.cpp b/third_party/WebKit/Source/core/paint/TablePainterTest.cpp
new file mode 100644
index 0000000..7ebcdff1
--- /dev/null
+++ b/third_party/WebKit/Source/core/paint/TablePainterTest.cpp
@@ -0,0 +1,75 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/paint/PaintControllerPaintTest.h"
+#include "platform/graphics/paint/DrawingDisplayItem.h"
+
+namespace blink {
+
+using TablePainterTest = PaintControllerPaintTest;
+
+TEST_F(TablePainterTest, CollapsedBorderInterestRectChange) {
+  setBodyInnerHTML(
+      "<style>"
+      "  table { border-collapse: collapse; position: absolute; }"
+      "  td { width: 100px; height: 100px; border: 2px solid blue; }"
+      "</style>"
+      "<table id='table'>"
+      "  <tr><td></td><td></td><td></td><td></td></tr>"
+      "  <tr><td></td><td></td><td></td><td></td></tr>"
+      "  <tr><td></td><td></td><td></td><td></td></tr>"
+      "  <tr><td></td><td></td><td></td><td></td></tr>"
+      "</table>");
+
+  PaintLayer& htmlLayer =
+      *toLayoutBoxModelObject(document().documentElement()->layoutObject())
+           ->layer();
+  LayoutObject& table = *getLayoutObjectByElementId("table");
+
+  rootPaintController().invalidateAll();
+  document().view()->updateAllLifecyclePhasesExceptPaint();
+  IntRect interestRect(300, 300, 300, 300);
+  paint(&interestRect);
+
+  EXPECT_DISPLAY_LIST(
+      rootPaintController().getDisplayItemList(), 4,
+      TestDisplayItem(layoutView(), documentBackgroundType),
+      TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
+      TestDisplayItem(table, DisplayItem::kTableCollapsedBorders),
+      TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence));
+  // Painted collapsed borders of the central 4 cells, each 4 operations.
+  EXPECT_EQ(16, static_cast<const DrawingDisplayItem&>(
+                    rootPaintController().getDisplayItemList()[2])
+                    .picture()
+                    ->approximateOpCount());
+
+  // Should repaint collapsed borders if the previous paint didn't fully paint
+  // and interest rect changes.
+  document().view()->updateAllLifecyclePhasesExceptPaint();
+  interestRect = IntRect(0, 0, 1000, 1000);
+  EXPECT_TRUE(paintWithoutCommit(&interestRect));
+  EXPECT_EQ(1, numCachedNewItems());
+  commit();
+  EXPECT_DISPLAY_LIST(
+      rootPaintController().getDisplayItemList(), 4,
+      TestDisplayItem(layoutView(), documentBackgroundType),
+      TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
+      TestDisplayItem(table, DisplayItem::kTableCollapsedBorders),
+      TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence));
+  // Painted collapsed borders of all 16 cells, each 4 operations.
+  EXPECT_EQ(64, static_cast<const DrawingDisplayItem&>(
+                    rootPaintController().getDisplayItemList()[2])
+                    .picture()
+                    ->approximateOpCount());
+
+  // Should not repaint collapsed borders if the previous paint fully painted
+  // and interest rect changes.
+  document().view()->updateAllLifecyclePhasesExceptPaint();
+  interestRect = IntRect(0, 0, 400, 400);
+  EXPECT_TRUE(paintWithoutCommit(&interestRect));
+  EXPECT_EQ(4, numCachedNewItems());
+  commit();
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/TableSectionPainter.cpp b/third_party/WebKit/Source/core/paint/TableSectionPainter.cpp
index 685ba682..44e1d61 100644
--- a/third_party/WebKit/Source/core/paint/TableSectionPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/TableSectionPainter.cpp
@@ -149,24 +149,27 @@
   return elem1->absoluteColumnIndex() < elem2->absoluteColumnIndex();
 }
 
-void TableSectionPainter::paintCollapsedBorders(
+PaintResult TableSectionPainter::paintCollapsedBorders(
     const PaintInfo& paintInfo,
     const LayoutPoint& paintOffset,
     const CollapsedBorderValue& currentBorderValue) {
-  paintCollapsedSectionBorders(paintInfo, paintOffset, currentBorderValue);
+  PaintResult result =
+      paintCollapsedSectionBorders(paintInfo, paintOffset, currentBorderValue);
   LayoutTable* table = m_layoutTableSection.table();
-  if (table->header() == m_layoutTableSection)
+  if (table->header() == m_layoutTableSection) {
     paintRepeatingHeaderGroup(paintInfo, paintOffset, currentBorderValue,
                               PaintCollapsedBorders);
+  }
+  return result;
 }
 
-void TableSectionPainter::paintCollapsedSectionBorders(
+PaintResult TableSectionPainter::paintCollapsedSectionBorders(
     const PaintInfo& paintInfo,
     const LayoutPoint& paintOffset,
     const CollapsedBorderValue& currentBorderValue) {
   if (!m_layoutTableSection.numRows() ||
       !m_layoutTableSection.table()->effectiveColumns().size())
-    return;
+    return FullyPainted;
 
   LayoutPoint adjustedPaintOffset =
       paintOffset + m_layoutTableSection.location();
@@ -185,7 +188,7 @@
       m_layoutTableSection.dirtiedEffectiveColumns(tableAlignedRect);
 
   if (dirtiedColumns.start() >= dirtiedColumns.end())
-    return;
+    return MayBeClippedByPaintDirtyRect;
 
   // Collapsed borders are painted from the bottom right to the top left so that
   // precedence due to cell position is respected.
@@ -207,6 +210,11 @@
                                                     currentBorderValue);
     }
   }
+
+  if (dirtiedRows == m_layoutTableSection.fullTableRowSpan() &&
+      dirtiedColumns == m_layoutTableSection.fullTableEffectiveColumnSpan())
+    return FullyPainted;
+  return MayBeClippedByPaintDirtyRect;
 }
 
 void TableSectionPainter::paintObject(const PaintInfo& paintInfo,
diff --git a/third_party/WebKit/Source/core/paint/TableSectionPainter.h b/third_party/WebKit/Source/core/paint/TableSectionPainter.h
index d6ff986f..9e33c022 100644
--- a/third_party/WebKit/Source/core/paint/TableSectionPainter.h
+++ b/third_party/WebKit/Source/core/paint/TableSectionPainter.h
@@ -6,6 +6,7 @@
 #define TableSectionPainter_h
 
 #include "core/paint/PaintPhase.h"
+#include "core/paint/PaintResult.h"
 #include "core/style/ShadowData.h"
 #include "wtf/Allocator.h"
 
@@ -26,9 +27,10 @@
       : m_layoutTableSection(layoutTableSection) {}
 
   void paint(const PaintInfo&, const LayoutPoint&);
-  void paintCollapsedBorders(const PaintInfo&,
-                             const LayoutPoint&,
-                             const CollapsedBorderValue&);
+
+  PaintResult paintCollapsedBorders(const PaintInfo&,
+                                    const LayoutPoint&,
+                                    const CollapsedBorderValue&);
 
  private:
   void paintObject(const PaintInfo&, const LayoutPoint&);
@@ -56,9 +58,9 @@
                                  const CollapsedBorderValue& currentBorderValue,
                                  ItemToPaint);
   void paintSection(const PaintInfo&, const LayoutPoint&);
-  void paintCollapsedSectionBorders(const PaintInfo&,
-                                    const LayoutPoint&,
-                                    const CollapsedBorderValue&);
+  PaintResult paintCollapsedSectionBorders(const PaintInfo&,
+                                           const LayoutPoint&,
+                                           const CollapsedBorderValue&);
 
   const LayoutTableSection& m_layoutTableSection;
 };
diff --git a/third_party/WebKit/Source/core/svg/graphics/SVGImage.cpp b/third_party/WebKit/Source/core/svg/graphics/SVGImage.cpp
index 04e9ec2..3b2c121 100644
--- a/third_party/WebKit/Source/core/svg/graphics/SVGImage.cpp
+++ b/third_party/WebKit/Source/core/svg/graphics/SVGImage.cpp
@@ -62,7 +62,9 @@
 namespace blink {
 
 SVGImage::SVGImage(ImageObserver* observer)
-    : Image(observer), m_hasPendingTimelineRewind(false) {}
+    : Image(observer),
+      m_paintController(PaintController::create()),
+      m_hasPendingTimelineRewind(false) {}
 
 SVGImage::~SVGImage() {
   if (m_page) {
@@ -369,8 +371,8 @@
   // time=0.) The reason we do this here and not in resetAnimation() is to
   // avoid setting timers from the latter.
   flushPendingTimelineRewind();
-
-  SkPictureBuilder imagePicture(dstRect);
+  SkPictureBuilder imagePicture(dstRect, nullptr, nullptr,
+                                m_paintController.get());
   {
     ClipRecorder clipRecorder(imagePicture.context(), imagePicture,
                               DisplayItem::kClipNodeImage,
diff --git a/third_party/WebKit/Source/core/svg/graphics/SVGImage.h b/third_party/WebKit/Source/core/svg/graphics/SVGImage.h
index 737bf326..77657e2 100644
--- a/third_party/WebKit/Source/core/svg/graphics/SVGImage.h
+++ b/third_party/WebKit/Source/core/svg/graphics/SVGImage.h
@@ -38,6 +38,7 @@
 
 class Document;
 class Page;
+class PaintController;
 class LayoutReplaced;
 class SVGImageChromeClient;
 class SVGImageForContainer;
@@ -158,6 +159,7 @@
 
   Persistent<SVGImageChromeClient> m_chromeClient;
   Persistent<Page> m_page;
+  std::unique_ptr<PaintController> m_paintController;
 
   // When an SVG image has no intrinsic size, the size depends on the default
   // object size, which in turn depends on the container. One SVGImage may
diff --git a/third_party/WebKit/Source/core/timing/Performance.cpp b/third_party/WebKit/Source/core/timing/Performance.cpp
index 2bf1572d..e8402d5 100644
--- a/third_party/WebKit/Source/core/timing/Performance.cpp
+++ b/third_party/WebKit/Source/core/timing/Performance.cpp
@@ -35,11 +35,20 @@
 #include "bindings/core/v8/V8ObjectBuilder.h"
 #include "core/dom/Document.h"
 #include "core/frame/LocalFrame.h"
-#include "core/frame/PerformanceMonitor.h"
+#include "core/frame/UseCounter.h"
 #include "core/loader/DocumentLoader.h"
 #include "core/origin_trials/OriginTrials.h"
 #include "core/timing/PerformanceTiming.h"
 
+static const double kLongTaskThreshold = 0.05;
+
+static const char kUnknownAttribution[] = "unknown";
+static const char kAmbugiousAttribution[] = "multiple-contexts";
+static const char kSameOriginAttribution[] = "same-origin";
+static const char kAncestorAttribution[] = "cross-origin-ancestor";
+static const char kDescendantAttribution[] = "cross-origin-descendant";
+static const char kCrossOriginAttribution[] = "cross-origin-unreachable";
+
 namespace blink {
 
 static double toTimeOrigin(LocalFrame* frame) {
@@ -62,11 +71,11 @@
 
 Performance::~Performance() {
   if (frame())
-    PerformanceMonitor::performanceObserverRemoved(this);
+    frame()->performanceMonitor()->unsubscribeAll(this);
 }
 
 void Performance::frameDestroyed() {
-  PerformanceMonitor::performanceObserverRemoved(this);
+  frame()->performanceMonitor()->unsubscribeAll(this);
   DOMWindowProperty::frameDestroyed();
 }
 
@@ -99,13 +108,14 @@
   if (!frame()->document() ||
       !OriginTrials::longTaskObserverEnabled(frame()->document()))
     return;
-  LocalFrame* localRoot = frame()->localFrameRoot();
-  DCHECK(localRoot);
 
-  if (hasObserverFor(PerformanceEntry::LongTask))
-    PerformanceMonitor::performanceObserverAdded(this);
-  else
-    PerformanceMonitor::performanceObserverRemoved(this);
+  if (hasObserverFor(PerformanceEntry::LongTask)) {
+    UseCounter::count(frame()->localFrameRoot(), UseCounter::LongTaskObserver);
+    frame()->performanceMonitor()->subscribe(PerformanceMonitor::kLongTask,
+                                             kLongTaskThreshold, this);
+  } else {
+    frame()->performanceMonitor()->unsubscribeAll(this);
+  }
 }
 
 ScriptValue Performance::toJSONForBinding(ScriptState* scriptState) const {
@@ -120,6 +130,72 @@
   visitor->trace(m_timing);
   DOMWindowProperty::trace(visitor);
   PerformanceBase::trace(visitor);
+  PerformanceMonitor::Client::trace(visitor);
+}
+
+static bool canAccessOrigin(Frame* frame1, Frame* frame2) {
+  SecurityOrigin* securityOrigin1 =
+      frame1->securityContext()->getSecurityOrigin();
+  SecurityOrigin* securityOrigin2 =
+      frame2->securityContext()->getSecurityOrigin();
+  return securityOrigin1->canAccess(securityOrigin2);
+}
+
+/**
+ * Report sanitized name based on cross-origin policy.
+ * See detailed Security doc here: http://bit.ly/2duD3F7
+ */
+// static
+std::pair<String, DOMWindow*> Performance::sanitizedAttribution(
+    const HeapHashSet<Member<Frame>>& frames,
+    Frame* observerFrame) {
+  if (frames.size() == 0) {
+    // Unable to attribute as no script was involved.
+    return std::make_pair(kUnknownAttribution, nullptr);
+  }
+  if (frames.size() > 1) {
+    // Unable to attribute, multiple script execution contents were involved.
+    return std::make_pair(kAmbugiousAttribution, nullptr);
+  }
+  // Exactly one culprit location, attribute based on origin boundary.
+  DCHECK_EQ(1u, frames.size());
+  Frame* culpritFrame = *frames.begin();
+  DCHECK(culpritFrame);
+  if (canAccessOrigin(observerFrame, culpritFrame)) {
+    // From accessible frames or same origin, return culprit location URL.
+    return std::make_pair(kSameOriginAttribution, culpritFrame->domWindow());
+  }
+  // For cross-origin, if the culprit is the descendant or ancestor of
+  // observer then indicate the *closest* cross-origin frame between
+  // the observer and the culprit, in the corresponding direction.
+  if (culpritFrame->tree().isDescendantOf(observerFrame)) {
+    // If the culprit is a descendant of the observer, then walk up the tree
+    // from culprit to observer, and report the *last* cross-origin (from
+    // observer) frame.  If no intermediate cross-origin frame is found, then
+    // report the culprit directly.
+    Frame* lastCrossOriginFrame = culpritFrame;
+    for (Frame* frame = culpritFrame; frame != observerFrame;
+         frame = frame->tree().parent()) {
+      if (!canAccessOrigin(observerFrame, frame)) {
+        lastCrossOriginFrame = frame;
+      }
+    }
+    return std::make_pair(kDescendantAttribution,
+                          lastCrossOriginFrame->domWindow());
+  }
+  if (observerFrame->tree().isDescendantOf(culpritFrame)) {
+    return std::make_pair(kAncestorAttribution, nullptr);
+  }
+  return std::make_pair(kCrossOriginAttribution, nullptr);
+}
+
+void Performance::reportLongTask(
+    double startTime,
+    double endTime,
+    const HeapHashSet<Member<Frame>>& contextFrames) {
+  std::pair<String, DOMWindow*> attribution =
+      Performance::sanitizedAttribution(contextFrames, frame());
+  addLongTaskTiming(startTime, endTime, attribution.first, attribution.second);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/timing/Performance.h b/third_party/WebKit/Source/core/timing/Performance.h
index ea5579d..95bc1fb0 100644
--- a/third_party/WebKit/Source/core/timing/Performance.h
+++ b/third_party/WebKit/Source/core/timing/Performance.h
@@ -34,6 +34,7 @@
 
 #include "core/CoreExport.h"
 #include "core/frame/DOMWindowProperty.h"
+#include "core/frame/PerformanceMonitor.h"
 #include "core/timing/MemoryInfo.h"
 #include "core/timing/PerformanceBase.h"
 #include "core/timing/PerformanceNavigation.h"
@@ -45,7 +46,8 @@
 class ScriptValue;
 
 class CORE_EXPORT Performance final : public PerformanceBase,
-                                      public DOMWindowProperty {
+                                      public DOMWindowProperty,
+                                      public PerformanceMonitor::Client {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(Performance);
   friend class PerformanceTest;
@@ -74,6 +76,15 @@
   // DOMWindowProperty overrides.
   void frameDestroyed() override;
 
+  static std::pair<String, DOMWindow*> sanitizedAttribution(
+      const HeapHashSet<Member<Frame>>& frames,
+      Frame* observerFrame);
+
+  // PerformanceMonitor::Client implementation.
+  void reportLongTask(double startTime,
+                      double endTime,
+                      const HeapHashSet<Member<Frame>>& contextFrames) override;
+
   mutable Member<PerformanceNavigation> m_navigation;
   mutable Member<PerformanceTiming> m_timing;
 };
diff --git a/third_party/WebKit/Source/core/timing/PerformanceTest.cpp b/third_party/WebKit/Source/core/timing/PerformanceTest.cpp
index 8a7bc32..5f2807cc 100644
--- a/third_party/WebKit/Source/core/timing/PerformanceTest.cpp
+++ b/third_party/WebKit/Source/core/timing/PerformanceTest.cpp
@@ -16,6 +16,11 @@
     m_pageHolder = DummyPageHolder::create(IntSize(800, 600));
     m_pageHolder->document().setURL(KURL(KURL(), "https://example.com"));
     m_performance = Performance::create(&m_pageHolder->frame());
+
+    // Create another dummy page holder and pretend this is the iframe.
+    m_anotherPageHolder = DummyPageHolder::create(IntSize(400, 300));
+    m_anotherPageHolder->document().setURL(
+        KURL(KURL(), "https://iframed.com/bar"));
   }
 
   bool observingLongTasks() {
@@ -33,8 +38,20 @@
     m_performance->m_observerFilterOptions = PerformanceEntry::Invalid;
   }
 
+  LocalFrame* frame() const { return m_pageHolder->document().frame(); }
+
+  LocalFrame* anotherFrame() const {
+    return m_anotherPageHolder->document().frame();
+  }
+
+  String sanitizedAttribution(const HeapHashSet<Member<Frame>>& frames,
+                              Frame* observerFrame) {
+    return Performance::sanitizedAttribution(frames, observerFrame).first;
+  }
+
   Persistent<Performance> m_performance;
   std::unique_ptr<DummyPageHolder> m_pageHolder;
+  std::unique_ptr<DummyPageHolder> m_anotherPageHolder;
 };
 
 TEST_F(PerformanceTest, LongTaskObserverInstrumentation) {
@@ -51,4 +68,29 @@
   m_performance->updateLongTaskInstrumentation();
   EXPECT_FALSE(observingLongTasks());
 }
+
+TEST_F(PerformanceTest, SanitizedLongTaskName) {
+  HeapHashSet<Member<Frame>> frameContexts;
+  // Unable to attribute, when no execution contents are available.
+  EXPECT_EQ("unknown", sanitizedAttribution(frameContexts, frame()));
+
+  // Attribute for same context (and same origin).
+  frameContexts.add(frame());
+  EXPECT_EQ("same-origin", sanitizedAttribution(frameContexts, frame()));
+
+  // Unable to attribute, when multiple script execution contents are involved.
+  frameContexts.add(anotherFrame());
+  EXPECT_EQ("multiple-contexts", sanitizedAttribution(frameContexts, frame()));
+}
+
+TEST_F(PerformanceTest, SanitizedLongTaskName_CrossOrigin) {
+  HeapHashSet<Member<Frame>> frameContexts;
+  // Unable to attribute, when no execution contents are available.
+  EXPECT_EQ("unknown", sanitizedAttribution(frameContexts, frame()));
+
+  // Attribute for same context (and same origin).
+  frameContexts.add(anotherFrame());
+  EXPECT_EQ("cross-origin-unreachable",
+            sanitizedAttribution(frameContexts, frame()));
+}
 }
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/securityIcons.png b/third_party/WebKit/Source/devtools/front_end/Images/securityIcons.png
index 28bc596..5271256 100644
--- a/third_party/WebKit/Source/devtools/front_end/Images/securityIcons.png
+++ b/third_party/WebKit/Source/devtools/front_end/Images/securityIcons.png
Binary files differ
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/securityIcons_2x.png b/third_party/WebKit/Source/devtools/front_end/Images/securityIcons_2x.png
index 6c5c20f..d67c76dc 100644
--- a/third_party/WebKit/Source/devtools/front_end/Images/securityIcons_2x.png
+++ b/third_party/WebKit/Source/devtools/front_end/Images/securityIcons_2x.png
Binary files differ
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/src/optimize_png.hashes b/third_party/WebKit/Source/devtools/front_end/Images/src/optimize_png.hashes
index b0c90518..cbaeb7e 100644
--- a/third_party/WebKit/Source/devtools/front_end/Images/src/optimize_png.hashes
+++ b/third_party/WebKit/Source/devtools/front_end/Images/src/optimize_png.hashes
@@ -1,5 +1,5 @@
 {
-    "securityIcons.svg": "6772b6645ebb3048b0ef8cbb02bbdde4",
+    "securityIcons.svg": "27676f7c1f1542659c7c49a8052259dc",
     "resourceGlyphs.svg": "8e1947b1fa4aac49cbc081f85f44d412",
     "breakpointConditional.svg": "4cf90210b2af2ed84db2f60b07bcde28",
     "errorWave.svg": "e183fa242a22ed4784a92f6becbc2c45",
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/src/securityIcons.svg b/third_party/WebKit/Source/devtools/front_end/Images/src/securityIcons.svg
index a8f063b..0b8f777 100644
--- a/third_party/WebKit/Source/devtools/front_end/Images/src/securityIcons.svg
+++ b/third_party/WebKit/Source/devtools/front_end/Images/src/securityIcons.svg
@@ -1,97 +1,278 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg width="160px" height="96px" viewBox="0 0 160 96" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <!-- Generator: Sketch 39.1 (31720) - http://www.bohemiancoding.com/sketch -->
-    <title>Non-Touch-Summary-Icons</title>
-    <desc>Created with Sketch.</desc>
-    <defs></defs>
-    <g id="Page" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="Non-Touch-Summary-Icons" transform="translate(-128.000000, -192.000000)">
-            <g id="Info-Grey" transform="translate(176.000000, 272.000000)" fill="#000000">
-                <path d="M2,3.73208732 C2,2.77548191 2.77427769,2 3.72629963,2 L13.2737004,2 C14.2271093,2 15,2.76890868 15,3.73208732 L15,12.2679127 C15,13.2245181 14.2257223,14 13.2737004,14 L3.72629963,14 C2.77289067,14 2,13.2310913 2,12.2679127 L2,3.73208732 Z M8,6 L9,6 L9,5 L8,5 L8,6 L8,6 Z M8,11 L9,11 L9,7 L8,7 L8,11 L8,11 Z" id="Icon"></path>
-            </g>
-            <g id="Unkonwn-Grey" transform="translate(192.000000, 272.000000)">
-                <g id="Icon-HTTP">
-                    <polygon id="Layout" points="0 0 16 0 16 16 0 16"></polygon>
-                </g>
-                <g id="ic_help_black_24dp" transform="translate(2.000000, 2.000000)" fill="#000000">
-                    <g id="Group">
-                        <path d="M0,1.73208732 C0,0.775481909 0.774277687,0 1.72629963,0 L11.2737004,0 C12.2271093,0 13,0.768908679 13,1.73208732 L13,10.2679127 C13,11.2245181 12.2257223,12 11.2737004,12 L1.72629963,12 C0.77289067,12 0,11.2310913 0,10.2679127 L0,1.73208732 Z M7,9 L6,9 L6,8 L7,8 L7,9 Z M8.035,5.125 L7.585,5.585 C7.225,5.95 7,6.25 7,7 L6.69726562,7 L6.3104248,7 L6,7 L6,6.75 C6,6.2 6.225,5.7 6.585,5.335 L7.205,4.705 C7.39,4.525 7.5,4.275 7.5,4 C7.5,3.45 7.05,3 6.5,3 C5.95,3 5.5,3.45 5.5,4 L4.5,4 C4.5,2.895 5.395,2 6.5,2 C7.605,2 8.5,2.895 8.5,4 C8.5,4.44 8.32,4.84 8.035,5.125 Z" id="Combined-Shape"></path>
-                    </g>
-                </g>
-            </g>
-            <g id="Icon" transform="translate(128.000000, 272.000000)">
-                <g id="Security-Icon-Green-Simple">
-                    <rect id="Layout" opacity="0.2" x="0" y="0" width="16" height="16"></rect>
-                    <rect id="Green" fill="#000000" x="4" y="4" width="8" height="8" rx="1"></rect>
-                </g>
-            </g>
-            <g id="Icon" transform="translate(144.000000, 272.000000)">
-                <g id="Security-Icon-Green-Simple">
-                    <rect id="Layout" opacity="0.2" x="0" y="0" width="16" height="16"></rect>
-                    <rect id="Green" fill="#000000" x="4" y="4" width="8" height="8" rx="4"></rect>
-                </g>
-            </g>
-            <g id="Icon" transform="translate(160.000000, 272.000000)">
-                <g id="Security-Icon-Green-Simple">
-                    <rect id="Layout" opacity="0.2" x="0" y="0" width="16" height="16"></rect>
-                    <polygon id="Red" fill="#000000" points="8 3 13 12 3 12"></polygon>
-                </g>
-            </g>
-            <g id="Icon" transform="translate(160.000000, 256.000000)">
-                <rect id="Layout" opacity="0.2" x="0" y="0" width="16" height="16"></rect>
-                <path d="M0.5,14 L15.5,14 L8,1 L0.5,14 L0.5,14 Z M9,12 L7,12 L7,10 L9,10 L9,12 L9,12 Z M9,9 L7,9 L7,6 L9,6 L9,9 L9,9 Z" fill="#000000"></path>
-            </g>
-            <g id="Icon" transform="translate(144.000000, 256.000000)">
-                <polygon id="Layout" points="0 0 16 0 16 16 0 16"></polygon>
-                <path d="M8,1 C4.136,1 1,4.136 1,8 C1,11.864 4.136,15 8,15 C11.864,15 15,11.864 15,8 C15,4.136 11.864,1 8,1 L8,1 Z M2.5,8 C2.5,4.968125 4.968125,2.5 8,2.5 C11.031875,2.5 13.5,4.968125 13.5,8 C13.5,11.031875 11.031875,13.5 8,13.5 C4.968125,13.5 2.5,11.031875 2.5,8 Z M9,12 L9,7 L7,7 L7,12 L9,12 Z M7,6 L9,6 L9,4 L7,4 L7,6 L7,6 Z" fill="#000000"></path>
-            </g>
-            <g id="Icon" transform="translate(128.000000, 256.000000)">
-                <polygon id="Layout" points="0 0 16 0 16 16 0 16"></polygon>
-                <path d="M10.5,6.49999992 L10.5,5.49999992 C10.5,4.11999512 9.38000488,3 8,3 C6.61999512,3.01017207 5.5,4.11999512 5.5,5.49999992 L5.5,6.49999992 C5.5,6.49999992 5.55000305,6.5 5,6.49999992 C4.44999695,6.49999983 4,6.95015331 4,7.49633781 L4,12.5 C4,13.0500031 4.45001221,13.5 5,13.5 C5.54998779,13.5 10.4500122,13.5 11,13.5 C11.5499878,13.5 12,13.0500031 12,12.5 C12,11.9499969 12,8.05000305 12,7.5 C12,6.97227107 11.5500031,6.5 11,6.49999992 C10.4499969,6.5 10.5,6.49999992 10.5,6.49999992 Z M6.5,6.51011411 L6.5,5.5 C6.5,4.66999817 7.16999817,4 8,4 C8.83000183,4 9.5,4.66999817 9.5,5.5 L9.5,6.51011411 L6.5,6.51011411 Z" fill="#000000"></path>
-            </g>
-            <g id="Icon" transform="translate(227.000000, 227.000000)" fill="#000000">
-                <path d="M0,3.46603816 C0,1.55179814 1.54855537,0 3.46603816,0 L22.5339618,0 C24.4482019,0 26,1.54855537 26,3.46603816 L26,22.5339618 C26,24.4482019 24.4514446,26 22.5339618,26 L3.46603816,26 C1.55179814,26 0,24.4514446 0,22.5339618 L0,3.46603816 Z M12,9 L14,9 L14,7 L12,7 L12,9 L12,9 Z M12,19 L14,19 L14,11 L12,11 L12,19 L12,19 Z"></path>
-            </g>
-            <g id="Icon" transform="translate(256.000000, 224.000000)">
-                <g id="Icon-HTTP">
-                    <polygon id="Layout" points="0 0 32 0 32 32 0 32"></polygon>
-                </g>
-                <g id="ic_help_black_24dp" transform="translate(3.000000, 3.000000)" fill="#000000">
-                    <g id="Group">
-                        <path d="M0,3.46603816 C0,1.55179814 1.54855537,0 3.46603816,0 L22.5339618,0 C24.4482019,0 26,1.54855537 26,3.46603816 L26,22.5339618 C26,24.4482019 24.4514446,26 22.5339618,26 L3.46603816,26 C1.55179814,26 0,24.4514446 0,22.5339618 L0,3.46603816 Z M14,19 L12,19 L12,17 L14,17 L14,19 Z M16.07,11.25 L15.17,12.17 C14.45,12.9 14,13.5 14,15 L13.3945312,15 L12.6208496,15 L12,15 L12,14.5 C12,13.4 12.45,12.4 13.17,11.67 L14.41,10.41 C14.78,10.05 15,9.55 15,9 C15,7.9 14.1,7 13,7 C11.9,7 11,7.9 11,9 L9,9 C9,6.79 10.79,5 13,5 C15.21,5 17,6.79 17,9 C17,9.88 16.64,10.68 16.07,11.25 Z" id="Combined-Shape"></path>
-                    </g>
-                </g>
-            </g>
-            <g id="Icon" transform="translate(128.000000, 224.000000)">
-                <g id="Security-Icon-Green-Simple">
-                    <rect id="Layout" opacity="0.2" x="0" y="0" width="32" height="32"></rect>
-                    <rect id="Green" fill="#000000" x="8" y="8" width="16" height="16" rx="2"></rect>
-                </g>
-            </g>
-            <g id="Icon" transform="translate(160.000000, 224.000000)">
-                <g id="Security-Icon-Green-Simple">
-                    <rect id="Layout" opacity="0.2" x="0" y="0" width="32" height="32"></rect>
-                    <rect id="Green" fill="#000000" x="8" y="8" width="16" height="16" rx="8"></rect>
-                </g>
-            </g>
-            <g id="Icon" transform="translate(192.000000, 224.000000)">
-                <g id="Security-Icon-Green-Simple">
-                    <rect id="Layout" opacity="0.2" x="0" y="0" width="32" height="32"></rect>
-                    <polygon id="Red" fill="#000000" points="16 6 26 24 6 24"></polygon>
-                </g>
-            </g>
-            <g id="Icon" transform="translate(192.000000, 192.000000)">
-                <rect id="Layout" opacity="0.2" x="0" y="0" width="32" height="32"></rect>
-                <path d="M4,27 L29,27 L16.5,5 L4,27 L4,27 Z M18,24 L15,24 L15,21 L18,21 L18,24 L18,24 Z M18,19 L15,19 L15,13 L18,13 L18,19 L18,19 Z" fill="#000000"></path>
-            </g>
-            <g id="Icon" transform="translate(160.000000, 192.000000)">
-                <polygon id="Layout" points="0 0 32 0 32 32 0 32"></polygon>
-                <path d="M16.5,3 C9.048,3 3,9.048 3,16.5 C3,23.952 9.048,30 16.5,30 C23.952,30 30,23.952 30,16.5 C30,9.048 23.952,3 16.5,3 L16.5,3 Z M6,16.5 C6,10.711875 10.711875,6 16.5,6 C22.288125,6 27,10.711875 27,16.5 C27,22.288125 22.288125,27 16.5,27 C10.711875,27 6,22.288125 6,16.5 Z M15,23 L18,23 L18,15 L15,15 L15,23 L15,23 Z M15,13 L18,13 L18,10 L15,10 L15,13 L15,13 Z" fill="#000000"></path>
-            </g>
-            <g id="Icon" transform="translate(128.000000, 192.000000)">
-                <polygon id="Layout" points="0 0 32 0 32 32 0 32"></polygon>
-                <path d="M21,12.9999998 L21,10.9999998 C21,8.23999023 18.7600098,6 16,6 C13.2399902,6.02034414 11,8.23999023 11,10.9999998 L11,12.9999998 C11,12.9999998 11.1000061,13 10,12.9999998 C8.8999939,12.9999997 8,13.9003066 8,14.9926756 L8,25 C8,26.1000061 8.90002441,27 10,27 C11.0999756,27 20.9000244,27 22,27 C23.0999756,27 24,26.1000061 24,25 C24,23.8999939 24,16.1000061 24,15 C24,13.9445421 23.1000061,13 22,12.9999998 C20.8999939,13 21,12.9999998 21,12.9999998 Z M13,13.0202282 L13,11 C13,9.33999634 14.3399963,8 16,8 C17.6600037,8 19,9.33999634 19,11 L19,13.0202282 L13,13.0202282 Z" fill="#000000"></path>
-            </g>
-        </g>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="80"
+   height="32"
+   viewBox="0 0 80 32"
+   version="1.1"
+   id="svg6301"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="securityIcons.svg">
+  <metadata
+     id="metadata6418">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1525"
+     inkscape:window-height="1092"
+     id="namedview6416"
+     showgrid="false"
+     inkscape:zoom="17.55"
+     inkscape:cx="77.609293"
+     inkscape:cy="-1.8798347"
+     inkscape:window-x="615"
+     inkscape:window-y="171"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="Page"
+     showborder="true" />
+  <!-- Generator: Sketch 39.1 (31720) - http://www.bohemiancoding.com/sketch -->
+  <title
+     id="title6303">Non-Touch-Summary-Icons</title>
+  <desc
+     id="desc6305">Created with Sketch.</desc>
+  <defs
+     id="defs6307" />
+  <g
+     id="Page"
+     style="fill:none;stroke:none"
+     transform="translate(0,-64)">
+    <g
+       style="fill:#000000"
+       transform="translate(48,80)"
+       id="Info-Grey">
+      <path
+         inkscape:connector-curvature="0"
+         id="Icon"
+         d="M 2,3.7320873 C 2,2.7754819 2.7742777,2 3.7262996,2 L 13.2737,2 C 14.227109,2 15,2.7689087 15,3.7320873 l 0,8.5358257 C 15,13.224518 14.225722,14 13.2737,14 L 3.7262996,14 C 2.7728907,14 2,13.231091 2,12.267913 L 2,3.7320873 z M 8,6 9,6 9,5 8,5 8,6 8,6 z m 0,5 1,0 0,-4 -1,0 0,4 0,0 z" />
     </g>
-</svg>
\ No newline at end of file
+    <g
+       id="Icon-HTTP"
+       transform="translate(64,80)">
+      <polygon
+         id="Layout"
+         points="0,16 0,0 16,0 16,16 " />
+    </g>
+    <g
+       id="ic_help_black_24dp"
+       transform="translate(66,82)"
+       style="fill:#000000">
+      <g
+         id="Group">
+        <path
+           d="M 67.71875 18 C 66.766728 18 66 18.762145 66 19.71875 L 66 28.28125 C 66 29.244428 66.765341 30 67.71875 30 L 77.28125 30 C 78.233272 30 79 29.237855 79 28.28125 L 79 19.71875 C 79 18.755571 78.234659 18 77.28125 18 L 67.71875 18 z M 72.5 20 C 73.605 20 74.5 20.895 74.5 22 C 74.5 22.44 74.31625 22.84 74.03125 23.125 L 73.59375 23.59375 C 73.23375 23.95875 73 24.25 73 25 L 72.6875 25 L 72.3125 25 L 72 25 L 72 24.75 C 72 24.2 72.23375 23.70875 72.59375 23.34375 L 73.21875 22.71875 C 73.40375 22.53875 73.5 22.275 73.5 22 C 73.5 21.45 73.05 21 72.5 21 C 71.95 21 71.5 21.45 71.5 22 L 70.5 22 C 70.5 20.895 71.395 20 72.5 20 z M 72 26 L 73 26 L 73 27 L 72 27 L 72 26 z "
+           transform="translate(-66,-18)"
+           id="path3614" />
+      </g>
+    </g>
+    <g
+       transform="translate(0,80)"
+       id="g6319">
+      <g
+         id="Security-Icon-Green-Simple">
+        <rect
+           style="opacity:0.2"
+           height="16"
+           width="16"
+           y="0"
+           x="0"
+           id="rect6322" />
+        <rect
+           style="fill:#000000"
+           rx="1"
+           height="8"
+           width="8"
+           y="4"
+           x="4"
+           id="Green" />
+      </g>
+    </g>
+    <g
+       transform="translate(16,80)"
+       id="g6325">
+      <g
+         id="g6327">
+        <rect
+           style="opacity:0.2"
+           height="16"
+           width="16"
+           y="0"
+           x="0"
+           id="rect6329" />
+        <rect
+           style="fill:#000000"
+           rx="4"
+           height="8"
+           width="8"
+           y="4"
+           x="4"
+           id="rect6331" />
+      </g>
+    </g>
+    <g
+       transform="translate(32,80)"
+       id="g6333">
+      <g
+         id="g6335">
+        <rect
+           style="opacity:0.2"
+           height="16"
+           width="16"
+           y="0"
+           x="0"
+           id="rect6337" />
+        <polygon
+           style="fill:#000000"
+           points="3,12 8,3 13,12 "
+           id="Red" />
+      </g>
+    </g>
+    <g
+       transform="translate(32,64)"
+       id="g6340">
+      <rect
+         style="opacity:0.2"
+         height="16"
+         width="16"
+         y="0"
+         x="0"
+         id="rect6342" />
+      <path
+         style="fill:#000000"
+         inkscape:connector-curvature="0"
+         id="path6344"
+         d="m 0.5,14 15,0 L 8,1 0.5,14 l 0,0 z m 8.5,-2 -2,0 0,-2 2,0 0,2 0,0 z M 9,9 7,9 7,6 9,6 9,9 9,9 z" />
+    </g>
+    <g
+       transform="translate(16,64)"
+       id="g6346">
+      <polygon
+         points="16,0 16,16 0,16 0,0 "
+         id="polygon6348" />
+      <path
+         style="fill:#000000"
+         inkscape:connector-curvature="0"
+         id="path6350"
+         d="M 8,1 C 4.136,1 1,4.136 1,8 c 0,3.864 3.136,7 7,7 3.864,0 7,-3.136 7,-7 C 15,4.136 11.864,1 8,1 L 8,1 z M 2.5,8 C 2.5,4.968125 4.968125,2.5 8,2.5 c 3.031875,0 5.5,2.468125 5.5,5.5 0,3.031875 -2.468125,5.5 -5.5,5.5 C 4.968125,13.5 2.5,11.031875 2.5,8 z M 9,12 9,7 7,7 7,12 9,12 z M 7,6 9,6 9,4 7,4 7,6 7,6 z" />
+    </g>
+    <g
+       transform="translate(0,64)"
+       id="g6352">
+      <polygon
+         points="16,0 16,16 0,16 0,0 "
+         id="polygon6354" />
+      <path
+         style="fill:#000000"
+         inkscape:connector-curvature="0"
+         id="path6356"
+         d="m 10.5,6.4999999 0,-1 C 10.5,4.1199951 9.3800049,3 8,3 6.6199951,3.0101721 5.5,4.1199951 5.5,5.4999999 l 0,1 c 0,0 0.050003,1e-7 -0.5,0 -0.5500031,-10e-8 -1,0.4501534 -1,0.9963379 L 4,12.5 c 0,0.550003 0.4500122,1 1,1 0.5499878,0 5.450012,0 6,0 0.549988,0 1,-0.449997 1,-1 0,-0.550003 0,-4.4499969 0,-5 0,-0.5277289 -0.449997,-1 -1,-1.0000001 -0.550003,1e-7 -0.5,0 -0.5,0 z M 6.5,6.5101141 6.5,5.5 C 6.5,4.6699982 7.1699982,4 8,4 8.8300018,4 9.5,4.6699982 9.5,5.5 l 0,1.0101141 -3,0 z" />
+    </g>
+    <g
+       style="fill:#000000"
+       transform="translate(99,35)"
+       id="g6358" />
+    <g
+       transform="translate(128,32)"
+       id="g6362">
+      <g
+         id="g6364">
+        <polygon
+           points="32,0 32,32 0,32 0,0 "
+           id="polygon6366" />
+      </g>
+      <g
+         style="fill:#000000"
+         transform="translate(3,3)"
+         id="g6368">
+        <g
+           id="g6370" />
+      </g>
+    </g>
+    <g
+       transform="translate(0,32)"
+       id="g6374">
+      <g
+         id="g6376">
+        <rect
+           style="opacity:0.2"
+           height="32"
+           width="32"
+           y="0"
+           x="0"
+           id="rect6378" />
+      </g>
+    </g>
+    <g
+       transform="translate(32,32)"
+       id="g6382">
+      <g
+         id="g6384">
+        <rect
+           style="opacity:0.2"
+           height="32"
+           width="32"
+           y="0"
+           x="0"
+           id="rect6386" />
+      </g>
+    </g>
+    <g
+       transform="translate(64,32)"
+       id="g6390">
+      <g
+         id="g6392">
+        <rect
+           style="opacity:0.2"
+           height="32"
+           width="32"
+           y="0"
+           x="0"
+           id="rect6394" />
+      </g>
+    </g>
+    <g
+       transform="translate(64,0)"
+       id="g6398">
+      <rect
+         style="opacity:0.2"
+         height="32"
+         width="32"
+         y="0"
+         x="0"
+         id="rect6400" />
+    </g>
+    <g
+       transform="translate(32,0)"
+       id="g6404">
+      <polygon
+         points="32,0 32,32 0,32 0,0 "
+         id="polygon6406" />
+    </g>
+    <g
+       id="g6410">
+      <polygon
+         points="32,0 32,32 0,32 0,0 "
+         id="polygon6412" />
+    </g>
+  </g>
+</svg>
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/src/svg2png.hashes b/third_party/WebKit/Source/devtools/front_end/Images/src/svg2png.hashes
index bca7b050..d58afd2 100644
--- a/third_party/WebKit/Source/devtools/front_end/Images/src/svg2png.hashes
+++ b/third_party/WebKit/Source/devtools/front_end/Images/src/svg2png.hashes
@@ -1,4 +1,5 @@
 {
+    "securityIcons.svg": "27676f7c1f1542659c7c49a8052259dc",
     "resourceGlyphs.svg": "8e1947b1fa4aac49cbc081f85f44d412",
     "breakpointConditional.svg": "4cf90210b2af2ed84db2f60b07bcde28",
     "errorWave.svg": "e183fa242a22ed4784a92f6becbc2c45",
@@ -7,4 +8,4 @@
     "toolbarButtonGlyphs.svg": "4f6393dd90967d814a6eed60d51b3530",
     "breakpoint.svg": "69cd92d807259c022791112809b97799",
     "search.svg": "fc990dd3836aec510d7ca1f36c2a3142"
-}
+}
\ No newline at end of file
diff --git a/third_party/WebKit/Source/devtools/front_end/components/EventListenersUtils.js b/third_party/WebKit/Source/devtools/front_end/components/EventListenersUtils.js
index 6b9aeba..7392c49a 100644
--- a/third_party/WebKit/Source/devtools/front_end/components/EventListenersUtils.js
+++ b/third_party/WebKit/Source/devtools/front_end/components/EventListenersUtils.js
@@ -4,7 +4,7 @@
 /** @typedef {{eventListeners:!Array<!WebInspector.EventListener>, internalHandlers:?WebInspector.RemoteArray}} */
 WebInspector.FrameworkEventListenersObject;
 
-/** @typedef {{type: string, useCapture: boolean, passive: boolean, handler: function()}} */
+/** @typedef {{type: string, useCapture: boolean, passive: boolean, once: boolean, handler: function()}} */
 WebInspector.EventListenerObjectInInspectedPage;
 
 /**
@@ -73,6 +73,8 @@
       var useCapture;
       /** @type {boolean} */
       var passive;
+      /** @type {boolean} */
+      var once;
       /** @type {?WebInspector.RemoteObject} */
       var handler = null;
       /** @type {?WebInspector.RemoteObject} */
@@ -89,19 +91,20 @@
       /**
        * @suppressReceiverCheck
        * @this {WebInspector.EventListenerObjectInInspectedPage}
-       * @return {!{type:string, useCapture:boolean, passive:boolean}}
+       * @return {!{type:string, useCapture:boolean, passive:boolean, once:boolean}}
        */
       function truncatePageEventListener() {
-        return {type: this.type, useCapture: this.useCapture, passive: this.passive};
+          return {type: this.type, useCapture: this.useCapture, passive: this.passive, once: this.once};
       }
 
       /**
-       * @param {!{type:string, useCapture: boolean, passive: boolean}} truncatedListener
+       * @param {!{type:string, useCapture: boolean, passive: boolean, once: boolean}} truncatedListener
        */
       function storeTruncatedListener(truncatedListener) {
         type = truncatedListener.type;
         useCapture = truncatedListener.useCapture;
         passive = truncatedListener.passive;
+        once = truncatedListener.once;
       }
 
       promises.push(listenerObject.callFunctionPromise(handlerFunction)
@@ -178,7 +181,7 @@
         if (!location)
           throw new Error('Empty event listener\'s location');
         return new WebInspector.EventListener(
-            handler._target, object, type, useCapture, passive, handler, originalHandler, location,
+            handler._target, object, type, useCapture, passive, once, handler, originalHandler, location,
             removeFunctionObject, 'frameworkUser');
       }
     }
@@ -267,6 +270,7 @@
               "handler": function(),
               "useCapture": true,
               "passive": false,
+              "once": false,
               "type": "change",
               "remove": function(type, handler, useCapture, passive)
             },
@@ -356,6 +360,9 @@
         var passive = eventListener.passive;
         if (typeof passive !== 'boolean')
           errorString += 'event listener\'s passive isn\'t boolean or undefined, ';
+        var once = eventListener.once;
+        if (typeof once !== 'boolean')
+          errorString += 'event listener\'s once isn\'t boolean or undefined, ';
         var handler = eventListener.handler;
         if (!handler || (typeof handler !== 'function'))
           errorString += 'event listener\'s handler isn\'t a function or empty, ';
@@ -363,7 +370,7 @@
         if (remove && (typeof remove !== 'function'))
           errorString += 'event listener\'s remove isn\'t a function, ';
         if (!errorString) {
-          return {type: type, useCapture: useCapture, passive: passive, handler: handler, remove: remove};
+          return {type: type, useCapture: useCapture, passive: passive, once: once, handler: handler, remove: remove};
         } else {
           errorLines.push(errorString.substr(0, errorString.length - 2));
           return null;
@@ -428,6 +435,7 @@
                 handler: frameworkListener.handler || frameworkListener,
                 useCapture: true,
                 passive: false,
+                once: false,
                 type: type
               };
               listener.remove = jQueryRemove.bind(node, frameworkListener.selector);
@@ -446,7 +454,7 @@
           var events = entryEvents[type];
           for (var key in events) {
             if (typeof events[key] === 'function') {
-              var listener = {handler: events[key], useCapture: true, passive: false, type: type};
+              var listener = {handler: events[key], useCapture: true, passive: false, once: false, type: type};
               // We don't support removing for old version < 1.4 of jQuery because it doesn't provide API for getting "selector".
               eventListeners.push(listener);
             }
diff --git a/third_party/WebKit/Source/devtools/front_end/components/EventListenersView.js b/third_party/WebKit/Source/devtools/front_end/components/EventListenersView.js
index c8793d6..79c87d22 100644
--- a/third_party/WebKit/Source/devtools/front_end/components/EventListenersView.js
+++ b/third_party/WebKit/Source/devtools/front_end/components/EventListenersView.js
@@ -270,6 +270,7 @@
     var runtimeModel = eventListener.target().runtimeModel;
     properties.push(runtimeModel.createRemotePropertyFromPrimitiveValue('useCapture', eventListener.useCapture()));
     properties.push(runtimeModel.createRemotePropertyFromPrimitiveValue('passive', eventListener.passive()));
+    properties.push(runtimeModel.createRemotePropertyFromPrimitiveValue('once', eventListener.once()));
     if (typeof eventListener.handler() !== 'undefined')
       properties.push(new WebInspector.RemoteObjectProperty('handler', eventListener.handler()));
     WebInspector.ObjectPropertyTreeElement.populateWithProperties(this, properties, [], true, null);
diff --git a/third_party/WebKit/Source/devtools/front_end/components/Linkifier.js b/third_party/WebKit/Source/devtools/front_end/components/Linkifier.js
index bd515c8..c04bfa9 100644
--- a/third_party/WebKit/Source/devtools/front_end/components/Linkifier.js
+++ b/third_party/WebKit/Source/devtools/front_end/components/Linkifier.js
@@ -43,7 +43,7 @@
     this._anchorsByTarget = new Map();
     /** @type {!Map<!WebInspector.Target, !WebInspector.LiveLocationPool>} */
     this._locationPoolByTarget = new Map();
-    this._useLinkDecorator = true;
+    this._useLinkDecorator = !!useLinkDecorator;
     WebInspector.Linkifier._instances.add(this);
     WebInspector.targetManager.observeTargets(this);
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/main/Main.js b/third_party/WebKit/Source/devtools/front_end/main/Main.js
index ed8201d..23c08c33 100644
--- a/third_party/WebKit/Source/devtools/front_end/main/Main.js
+++ b/third_party/WebKit/Source/devtools/front_end/main/Main.js
@@ -104,7 +104,7 @@
     Runtime.experiments.register('inspectTooltip', 'Dark inspect element tooltip');
     Runtime.experiments.register('liveSASS', 'Live SASS');
     Runtime.experiments.register('nodeDebugging', 'Node debugging', true);
-    Runtime.experiments.register('persistence2', 'Persistence 2.0', true);
+    Runtime.experiments.register('persistence2', 'Persistence 2.0');
     Runtime.experiments.register('privateScriptInspection', 'Private script inspection');
     Runtime.experiments.register('requestBlocking', 'Request blocking', true);
     Runtime.experiments.register('resolveVariableNames', 'Resolve variable names');
@@ -119,6 +119,7 @@
     Runtime.experiments.register('timelineTracingJSProfile', 'Timeline tracing based JS profiler', true);
     Runtime.experiments.register('timelineV8RuntimeCallStats', 'V8 Runtime Call Stats on Timeline', true);
     Runtime.experiments.register('timelineRuleUsageRecording', 'Track CSS rules usage while recording Timeline.');
+    Runtime.experiments.register('timelinePerFrameTrack', 'Show track per frame on Timeline', true);
 
     Runtime.experiments.cleanUpStaleExperiments();
 
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/ConsoleModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/ConsoleModel.js
index e7906e5..652fbaa 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/ConsoleModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/ConsoleModel.js
@@ -50,8 +50,12 @@
     if (this._logAgent) {
       target.registerLogDispatcher(new WebInspector.LogDispatcher(this));
       this._logAgent.enable();
-      if (!InspectorFrontendHost.isUnderTest())
-        this._logAgent.setReportViolationsEnabled(true);
+      if (!InspectorFrontendHost.isUnderTest()) {
+        this._logAgent.startViolationsReport([
+            {name: 'longTask', threshold: 50 },
+            {name: 'longLayout', threshold: 30},
+            {name: 'blockedEvent', threshold: 100}]);
+      }
     }
   }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/RemoteObject.js b/third_party/WebKit/Source/devtools/front_end/sdk/RemoteObject.js
index 601dbdb6..16618e3e 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/RemoteObject.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/RemoteObject.js
@@ -551,7 +551,7 @@
        */
       function createEventListener(payload) {
         return new WebInspector.EventListener(
-            this._target, this, payload.type, payload.useCapture, payload.passive,
+            this._target, this, payload.type, payload.useCapture, payload.passive, payload.once,
             payload.handler ? this.target().runtimeModel.createRemoteObject(payload.handler) : null,
             payload.originalHandler ? this.target().runtimeModel.createRemoteObject(payload.originalHandler) : null,
             /** @type {!WebInspector.DebuggerModel.Location} */ (this._debuggerModel.createRawLocationByScriptId(
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/RuntimeModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/RuntimeModel.js
index e733e8a..50265c0 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/RuntimeModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/RuntimeModel.js
@@ -607,6 +607,7 @@
    * @param {string} type
    * @param {boolean} useCapture
    * @param {boolean} passive
+   * @param {boolean} once
    * @param {?WebInspector.RemoteObject} handler
    * @param {?WebInspector.RemoteObject} originalHandler
    * @param {!WebInspector.DebuggerModel.Location} location
@@ -619,6 +620,7 @@
       type,
       useCapture,
       passive,
+      once,
       handler,
       originalHandler,
       location,
@@ -629,6 +631,7 @@
     this._type = type;
     this._useCapture = useCapture;
     this._passive = passive;
+    this._once = once;
     this._handler = handler;
     this._originalHandler = originalHandler || handler;
     this._location = location;
@@ -660,6 +663,13 @@
   }
 
   /**
+   * @return {boolean}
+   */
+  once() {
+    return this._once;
+  }
+
+  /**
    * @return {?WebInspector.RemoteObject}
    */
   handler() {
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChart.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChart.js
index 65ffa4c5..23ce2d3 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChart.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChart.js
@@ -387,8 +387,17 @@
       this._appendAsyncEventsGroup(title, animations, this._interactionsHeaderLevel2);
     }
     var threads = this._model.virtualThreads();
-    this._appendThreadTimelineData(
-        WebInspector.UIString('Main'), this._model.mainThreadEvents(), this._model.mainThreadAsyncEvents(), true);
+    if (!Runtime.experiments.isEnabled('timelinePerFrameTrack')) {
+      this._appendThreadTimelineData(
+          WebInspector.UIString('Main'), this._model.mainThreadEvents(), this._model.mainThreadAsyncEvents(), true);
+    } else {
+      this._appendThreadTimelineData(
+          WebInspector.UIString('Page'), this._model.eventsForFrame(WebInspector.TimelineModel.PageFrame.mainFrameId), this._model.mainThreadAsyncEvents(), true);
+      for (var frame of this._model.rootFrames()) {
+        // Ignore top frame itself, since it should be part of page events.
+        frame.children.forEach(this._appendFrameEvents.bind(this, 0));
+      }
+    }
     var compositorThreads = threads.filter(thread => thread.name.startsWith('CompositorTileWorker'));
     var otherThreads = threads.filter(thread => !thread.name.startsWith('CompositorTileWorker'));
     if (compositorThreads.length) {
@@ -418,6 +427,19 @@
   }
 
   /**
+   * @param {number} level
+   * @param {!WebInspector.TimelineModel.PageFrame} frame
+   */
+  _appendFrameEvents(level, frame) {
+    var events = this._model.eventsForFrame(frame.id);
+    var clonedHeader = Object.assign({}, this._headerLevel1);
+    clonedHeader.nestingLevel = level;
+    this._appendSyncEvents(events, WebInspector.TimelineUIUtils.displayNameForFrame(frame),
+                           /** @type {!WebInspector.FlameChart.GroupStyle} */ (clonedHeader));
+    frame.children.forEach(this._appendFrameEvents.bind(this, level + 1));
+  }
+
+  /**
    * @param {string} threadTitle
    * @param {!Array<!WebInspector.TracingModel.Event>} syncEvents
    * @param {!Map<!WebInspector.TimelineModel.AsyncEventGroup, !Array<!WebInspector.TracingModel.AsyncEvent>>} asyncEvents
@@ -1054,7 +1076,7 @@
 
     context.fillStyle = 'hsla(0, 100%, 100%, 0.8)';
     context.fillRect(sendStart + 0.5, barY + 0.5, headersEnd - sendStart - 0.5, barHeight - 2);
-    context.fillStyle = 'white';
+    context.fillStyle = WebInspector.themeSupport.patchColor('white', WebInspector.ThemeSupport.ColorUsage.Background);
     context.fillRect(barX, barY - 0.5, sendStart - barX, barHeight);
     context.fillRect(finish, barY - 0.5, barX + barWidth - finish, barHeight);
 
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineTreeView.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineTreeView.js
index 25cfa43..9e43ae5b 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineTreeView.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineTreeView.js
@@ -156,7 +156,7 @@
   }
 
   /**
-   * @param {function(!WebInspector.TracingModel.Event, string):(string|symbol)=} eventIdCallback
+   * @param {function(!WebInspector.TracingModel.Event):(string|symbol)=} eventIdCallback
    * @return {!WebInspector.TimelineProfileTree.Node}
    */
   _buildTopDownTree(eventIdCallback) {
@@ -322,26 +322,25 @@
    * @return {!Element}
    */
   _createNameCell(columnId) {
-    var cell = this.createTD(columnId);
-    var container = cell.createChild('div', 'name-container');
-    var icon = container.createChild('div', 'activity-icon');
-    var name = container.createChild('div', 'activity-name');
-    var event = this._profileNode.event;
+    const cell = this.createTD(columnId);
+    const container = cell.createChild('div', 'name-container');
+    const icon = container.createChild('div', 'activity-icon');
+    const name = container.createChild('div', 'activity-name');
+    const event = this._profileNode.event;
     if (this._profileNode.isGroupNode()) {
-      var treeView = /** @type {!WebInspector.AggregatedTimelineTreeView} */ (this._treeView);
-      var info = treeView._displayInfoForGroupNode(this._profileNode);
+      const treeView = /** @type {!WebInspector.AggregatedTimelineTreeView} */ (this._treeView);
+      const info = treeView._displayInfoForGroupNode(this._profileNode);
       name.textContent = info.name;
       icon.style.backgroundColor = info.color;
     } else if (event) {
-      var data = event.args['data'];
-      var deoptReason = data && data['deoptReason'];
-      if (deoptReason)
+      const data = event.args['data'];
+      const deoptReason = data && data['deoptReason'];
+      if (deoptReason) {
         container.createChild('div', 'activity-warning').title =
             WebInspector.UIString('Not optimized: %s', deoptReason);
-      name.textContent = event.name === WebInspector.TimelineModel.RecordType.JSFrame ?
-          WebInspector.beautifyFunctionName(event.args['data']['functionName']) :
-          WebInspector.TimelineUIUtils.eventTitle(event);
-      var link = this._treeView._linkifyLocation(event);
+      }
+      name.textContent = WebInspector.TimelineUIUtils.eventTitle(event);
+      const link = this._treeView._linkifyLocation(event);
       if (link)
         container.createChild('div', 'activity-link').appendChild(link);
       icon.style.backgroundColor = WebInspector.TimelineUIUtils.eventColor(event);
@@ -510,11 +509,7 @@
         break;
       case WebInspector.AggregatedTimelineTreeView.GroupBy.Frame:
         var frame = this._model.pageFrameById(node.id);
-        var frameName;
-        if (frame && frame.url)
-          frameName = frame.url.startsWith('about:') && frame.name ? `"${frame.name}"` : frame.url;
-        else
-          frameName = WebInspector.UIString('Page');
+        var frameName = frame ? WebInspector.TimelineUIUtils.displayNameForFrame(frame, 80) : WebInspector.UIString('Page');
         return {
           name: frameName,
           color: color
@@ -545,17 +540,14 @@
       if (id === this._groupBySetting.get())
         this._groupByCombobox.select(option);
     }
-    addGroupingOption.call(this, WebInspector.UIString('No Grouping'), WebInspector.AggregatedTimelineTreeView.GroupBy.None);
-    addGroupingOption.call(
-        this, WebInspector.UIString('Group by Activity'), WebInspector.AggregatedTimelineTreeView.GroupBy.EventName);
-    addGroupingOption.call(
-        this, WebInspector.UIString('Group by Category'), WebInspector.AggregatedTimelineTreeView.GroupBy.Category);
-    addGroupingOption.call(
-        this, WebInspector.UIString('Group by Domain'), WebInspector.AggregatedTimelineTreeView.GroupBy.Domain);
-    addGroupingOption.call(
-        this, WebInspector.UIString('Group by Subdomain'), WebInspector.AggregatedTimelineTreeView.GroupBy.Subdomain);
-    addGroupingOption.call(this, WebInspector.UIString('Group by URL'), WebInspector.AggregatedTimelineTreeView.GroupBy.URL);
-    addGroupingOption.call(this, WebInspector.UIString('Group by Frame'), WebInspector.AggregatedTimelineTreeView.GroupBy.Frame);
+    const groupBy = WebInspector.AggregatedTimelineTreeView.GroupBy;
+    addGroupingOption.call(this, WebInspector.UIString('No Grouping'), groupBy.None);
+    addGroupingOption.call(this, WebInspector.UIString('Group by Activity'), groupBy.EventName);
+    addGroupingOption.call(this, WebInspector.UIString('Group by Category'), groupBy.Category);
+    addGroupingOption.call(this, WebInspector.UIString('Group by Domain'), groupBy.Domain);
+    addGroupingOption.call(this, WebInspector.UIString('Group by Subdomain'), groupBy.Subdomain);
+    addGroupingOption.call(this, WebInspector.UIString('Group by URL'), groupBy.URL);
+    addGroupingOption.call(this, WebInspector.UIString('Group by Frame'), groupBy.Frame);
     panelToolbar.appendToolbarItem(this._groupByCombobox);
   }
 
@@ -611,7 +603,7 @@
 
   /**
    * @param {!WebInspector.AggregatedTimelineTreeView.GroupBy} groupBy
-   * @return {function(!WebInspector.TracingModel.Event, string):string}
+   * @return {function(!WebInspector.TracingModel.Event):string}
    */
   _groupingFunction(groupBy) {
     /**
@@ -658,7 +650,7 @@
       case WebInspector.AggregatedTimelineTreeView.GroupBy.URL:
         return groupByURL;
       case WebInspector.AggregatedTimelineTreeView.GroupBy.Frame:
-        return (event, pageFrameId) => pageFrameId || '';
+        return event => WebInspector.TimelineData.forEvent(event).frameId;
       default:
         console.assert(false, `Unexpected aggregation setting: ${groupBy}`);
         return () => Symbol('uniqueGroupId');
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js
index fc6b986..ea68f739 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js
@@ -224,6 +224,22 @@
   }
 
   /**
+   * @param {!Protocol.Runtime.CallFrame} frame
+   * @return {string}
+   */
+  static frameDisplayName(frame) {
+    if (!WebInspector.TimelineJSProfileProcessor.isNativeRuntimeFrame(frame))
+      return WebInspector.beautifyFunctionName(frame.functionName);
+    const nativeGroup = WebInspector.TimelineJSProfileProcessor.nativeGroup(frame.functionName);
+    const groups = WebInspector.TimelineJSProfileProcessor.NativeGroups;
+    switch (nativeGroup) {
+      case groups.Compile: return WebInspector.UIString('Compile');
+      case groups.Parse: return WebInspector.UIString('Parse');
+    }
+    return frame.functionName;
+  }
+
+  /**
    * @param {!WebInspector.TracingModel.Event} traceEvent
    * @param {!RegExp} regExp
    * @return {boolean}
@@ -295,14 +311,17 @@
    * @return {string}
    */
   static eventTitle(event) {
-    var title = WebInspector.TimelineUIUtils.eventStyle(event).title;
+    const recordType = WebInspector.TimelineModel.RecordType;
+    const eventData = event.args['data'];
+    if (event.name === recordType.JSFrame)
+      return WebInspector.TimelineUIUtils.frameDisplayName(eventData);
+    const title = WebInspector.TimelineUIUtils.eventStyle(event).title;
     if (event.hasCategory(WebInspector.TimelineModel.Category.Console))
       return title;
-    if (event.name === WebInspector.TimelineModel.RecordType.TimeStamp)
-      return WebInspector.UIString('%s: %s', title, event.args['data']['message']);
-    if (event.name === WebInspector.TimelineModel.RecordType.Animation && event.args['data'] &&
-        event.args['data']['name'])
-      return WebInspector.UIString('%s: %s', title, event.args['data']['name']);
+    if (event.name === recordType.TimeStamp)
+      return WebInspector.UIString('%s: %s', title, eventData['message']);
+    if (event.name === recordType.Animation && eventData && eventData['name'])
+      return WebInspector.UIString('%s: %s', title, eventData['name']);
     return title;
   }
 
@@ -438,7 +457,7 @@
           detailsText = linkifyLocationAsText(eventData['scriptId'], eventData['lineNumber'], 0);
         break;
       case recordType.JSFrame:
-        detailsText = WebInspector.beautifyFunctionName(eventData['functionName']);
+        detailsText = WebInspector.TimelineUIUtils.frameDisplayName(eventData);
         break;
       case recordType.EventDispatch:
         detailsText = eventData ? eventData['type'] : null;
@@ -600,8 +619,8 @@
       case recordType.FunctionCall:
       case recordType.JSFrame:
         details = createElement('span');
-        details.createTextChild(WebInspector.beautifyFunctionName(eventData['functionName']));
-        var location = linkifyLocation(
+        details.createTextChild(WebInspector.TimelineUIUtils.frameDisplayName(eventData));
+        const location = linkifyLocation(
             eventData['scriptId'], eventData['url'], eventData['lineNumber'], eventData['columnNumber']);
         if (location) {
           details.createTextChild(' @ ');
@@ -1738,6 +1757,17 @@
     }
     return span;
   }
+
+  /**
+   * @param {!WebInspector.TimelineModel.PageFrame} frame
+   * @param {number=} trimAt
+   */
+  static displayNameForFrame(frame, trimAt) {
+    var url = frame.url;
+    if (!trimAt)
+      trimAt = 30;
+    return url.startsWith('about:') ? `"${frame.name.trimMiddle(trimAt)}"` : frame.url.trimEnd(trimAt);
+  }
 };
 
 /**
@@ -1815,7 +1845,7 @@
     if (topFrame && this._contentHelper.linkifier()) {
       title.createTextChild(WebInspector.UIString('. '));
       var stack = title.createChild('span', 'monospace');
-      stack.createChild('span').textContent = WebInspector.beautifyFunctionName(topFrame.functionName);
+      stack.createChild('span').textContent = WebInspector.TimelineUIUtils.frameDisplayName(topFrame);
       var link = this._contentHelper.linkifier().maybeLinkifyConsoleCallFrame(target, topFrame);
       if (link) {
         stack.createChild('span').textContent = ' @ ';
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineJSProfile.js b/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineJSProfile.js
index d99b9ee..9e0ee3ef 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineJSProfile.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineJSProfile.js
@@ -78,25 +78,6 @@
     const showRuntimeCallStats = Runtime.experiments.isEnabled('timelineV8RuntimeCallStats');
     const showNativeFunctions = WebInspector.moduleSetting('showNativeFunctionsInJSProfile').get();
 
-    const visibleV8RuntimeStatsItems = new Set([
-      'Compile',
-      'CompileCode',
-      'CompileCodeLazy',
-      'CompileDeserialize',
-      'CompileEval',
-      'CompileFullCode',
-      'CompileIgnition',
-      'CompilerDispatcher',
-      'CompileSerialize',
-      'DeoptimizeCode',
-      'OptimizeCode',
-      'ParseProgram',
-      'ParseFunction',
-      'RecompileConcurrent',
-      'RecompileSynchronous',
-      'ParseLazy'
-    ]);
-
     /**
      * @param {!WebInspector.TracingModel.Event} e
      */
@@ -146,6 +127,14 @@
     }
 
     /**
+     * @param {string} name
+     * @return {boolean}
+     */
+    function showNativeName(name) {
+      return showRuntimeCallStats && !!WebInspector.TimelineJSProfileProcessor.nativeGroup(name);
+    }
+
+    /**
      * @param {!Array<!Protocol.Runtime.CallFrame>} stack
      */
     function filterStackFrames(stack) {
@@ -158,7 +147,7 @@
         const isNativeFrame = url && url.startsWith('native ');
         if (!showNativeFunctions && isNativeFrame)
           continue;
-        if (url === 'native V8Runtime' && (!visibleV8RuntimeStatsItems.has(frame.functionName) || !showRuntimeCallStats))
+        if (WebInspector.TimelineJSProfileProcessor.isNativeRuntimeFrame(frame) && !showNativeName(frame.functionName))
           continue;
         if (isPreviousFrameNative && isNativeFrame)
           continue;
@@ -208,4 +197,48 @@
           events, onStartEvent, onEndEvent, onInstantEvent, firstTopLevelEvent.startTime);
     return jsFrameEvents;
   }
+
+  /**
+   * @param {!Protocol.Runtime.CallFrame} frame
+   * @return {boolean}
+   */
+  static isNativeRuntimeFrame(frame) {
+    return frame.url === 'native V8Runtime';
+  }
+
+  /**
+   * @param {string} nativeName
+   * @return {?WebInspector.TimelineJSProfileProcessor.NativeGroups}
+   */
+  static nativeGroup(nativeName) {
+    var map = WebInspector.TimelineJSProfileProcessor.nativeGroup._map;
+    if (!map) {
+      const nativeGroups = WebInspector.TimelineJSProfileProcessor.NativeGroups;
+      map = new Map([
+        ['Compile', nativeGroups.Compile],
+        ['CompileCode', nativeGroups.Compile],
+        ['CompileCodeLazy', nativeGroups.Compile],
+        ['CompileDeserialize', nativeGroups.Compile],
+        ['CompileEval', nativeGroups.Compile],
+        ['CompileFullCode', nativeGroups.Compile],
+        ['CompileIgnition', nativeGroups.Compile],
+        ['CompilerDispatcher', nativeGroups.Compile],
+        ['CompileSerialize', nativeGroups.Compile],
+        ['ParseProgram', nativeGroups.Parse],
+        ['ParseFunction', nativeGroups.Parse],
+        ['RecompileConcurrent', nativeGroups.Compile],
+        ['RecompileSynchronous', nativeGroups.Compile],
+        ['ParseLazy', nativeGroups.Parse]
+      ]);
+      /** @type {!Map<string, !WebInspector.TimelineJSProfileProcessor.NativeGroups>} */
+      WebInspector.TimelineJSProfileProcessor.nativeGroup._map = map;
+    }
+    return map.get(nativeName) || null;
+  }
+};
+
+/** @enum {string} */
+WebInspector.TimelineJSProfileProcessor.NativeGroups = {
+  'Compile': 'Compile',
+  'Parse': 'Parse'
 };
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineModel.js b/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineModel.js
index 9f208af1..e5ab90b 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineModel.js
@@ -277,18 +277,8 @@
     for (var event of metadataEvents) {
       if (event.name === WebInspector.TimelineModel.DevToolsMetadataEvent.TracingStartedInPage) {
         pageDevToolsMetadataEvents.push(event);
-        var frames = (event.args['data'] && event.args['data']['frames']) || [];
-        for (var frame of frames) {
-          var processId = event.thread.process().id();
-          var frameId = `${processId}.${frame.frame}`;
-          var frameData = {
-            url: frame['url'] || '',
-            name: frame['name'] || '',
-            processId: processId,
-            frameId: frame['frame']
-          };
-          this._pageFrames.set(frameId, frameData);
-        }
+        var frames = ((event.args['data'] && event.args['data']['frames']) || []);
+        frames.forEach(payload => this._addPageFrame(event, payload));
       } else if (event.name === WebInspector.TimelineModel.DevToolsMetadataEvent.TracingSessionIdForWorker) {
         workersDevToolsMetadataEvents.push(event);
       } else if (event.name === WebInspector.TimelineModel.DevToolsMetadataEvent.TracingStartedInBrowser) {
@@ -572,6 +562,7 @@
   _processThreadEvents(tracingModel, startTime, endTime, thread, isMainThread) {
     var events = this._injectJSFrameEvents(tracingModel, thread);
     var asyncEvents = thread.asyncEvents();
+    var groupByFrame = isMainThread && Runtime.experiments.isEnabled('timelinePerFrameTrack');
 
     var threadEvents;
     var threadAsyncEventsByGroup;
@@ -594,6 +585,19 @@
         break;
       if (!this._processEvent(event))
         continue;
+      if (groupByFrame) {
+        var frameId = WebInspector.TimelineData.forEvent(event).frameId;
+        var pageFrame = frameId && this._pageFrames.get(frameId);
+        var isMainFrame = !frameId || !pageFrame || !pageFrame.parent;
+        if (isMainFrame)
+          frameId = WebInspector.TimelineModel.PageFrame.mainFrameId;
+        var frameEvents = this._eventsByFrame.get(frameId);
+        if (!frameEvents) {
+          frameEvents = [];
+          this._eventsByFrame.set(frameId, frameEvents);
+        }
+        frameEvents.push(event);
+      }
       threadEvents.push(event);
       this._inspectedTargetEvents.push(event);
     }
@@ -657,7 +661,10 @@
         --timelineData.stackTrace[i].columnNumber;
       }
     }
-
+    var pageFrameId = WebInspector.TimelineModel.eventFrameId(event);
+    if (!pageFrameId && eventStack.length)
+      pageFrameId = WebInspector.TimelineData.forEvent(eventStack.peekLast()).frameId;
+    timelineData.frameId = pageFrameId || WebInspector.TimelineModel.PageFrame.mainFrameId;
     this._asyncEventTracker.processEvent(event);
     switch (event.name) {
       case recordTypes.ResourceSendRequest:
@@ -788,8 +795,8 @@
           break;
         this._paintImageEventByPixelRefId[event.args['LazyPixelRef']] = paintImageEvent;
         var paintImageData = WebInspector.TimelineData.forEvent(paintImageEvent);
-        event.backendNodeId = paintImageData.backendNodeId;
-        event.url = paintImageData.url;
+        timelineData.backendNodeId = paintImageData.backendNodeId;
+        timelineData.url = paintImageData.url;
         break;
 
       case recordTypes.MarkDOMContent:
@@ -802,13 +809,10 @@
       case recordTypes.CommitLoad:
         var frameId = WebInspector.TimelineModel.eventFrameId(event);
         var pageFrame = this._pageFrames.get(frameId);
-        if (pageFrame) {
-          pageFrame.url = eventData.url || '';
-          pageFrame.name = eventData.name || '';
-        } else {
-          var processId = event.thread.process().id();
-          this._pageFrames.set(frameId, {url: eventData.url || '', processId: processId, frameId: eventData.frame, name: eventData.name || ''});
-        }
+        if (pageFrame)
+          pageFrame.update(eventData.name || '', eventData.url || '');
+        else
+          this._addPageFrame(event, eventData);
         var page = eventData['page'];
         if (page && page !== this._currentPage)
           return false;
@@ -923,6 +927,19 @@
     }
   }
 
+  /**
+   * @param {!WebInspector.TracingModel.Event} event
+   * @param {!Object} payload
+   */
+  _addPageFrame(event, payload) {
+    var processId = event.thread.process().id();
+    var pageFrame = new WebInspector.TimelineModel.PageFrame(this.targetByEvent(event), processId, payload);
+    this._pageFrames.set(pageFrame.id, pageFrame);
+    var parent = payload['parent'] && this._pageFrames.get(`${processId}.${payload['parent']}`);
+    if (parent)
+      parent.addChild(pageFrame);
+  }
+
   reset() {
     this._virtualThreads = [];
     /** @type {!Array<!WebInspector.TracingModel.Event>} */
@@ -949,6 +966,8 @@
     this._workerIdByThread = new WeakMap();
     /** @type {!Map<string, !WebInspector.TimelineModel.PageFrame>} */
     this._pageFrames = new Map();
+    /** @type {!Map<string, !Array<!WebInspector.TracingModel.Event>>} */
+    this._eventsByFrame = new Map();
 
     this._minimumRecordTime = 0;
     this._maximumRecordTime = 0;
@@ -969,21 +988,21 @@
   }
 
   /**
-   * @return {!Array.<!WebInspector.TracingModel.Event>}
+   * @return {!Array<!WebInspector.TracingModel.Event>}
    */
   inspectedTargetEvents() {
     return this._inspectedTargetEvents;
   }
 
   /**
-   * @return {!Array.<!WebInspector.TracingModel.Event>}
+   * @return {!Array<!WebInspector.TracingModel.Event>}
    */
   mainThreadEvents() {
     return this._mainThreadEvents;
   }
 
   /**
-   * @param {!Array.<!WebInspector.TracingModel.Event>} events
+   * @param {!Array<!WebInspector.TracingModel.Event>} events
    */
   _setMainThreadEvents(events) {
     this._mainThreadEvents = events;
@@ -997,7 +1016,7 @@
   }
 
   /**
-   * @return {!Array.<!WebInspector.TimelineModel.VirtualThread>}
+   * @return {!Array<!WebInspector.TimelineModel.VirtualThread>}
    */
   virtualThreads() {
     return this._virtualThreads;
@@ -1032,6 +1051,13 @@
   }
 
   /**
+   * @return {!Array<!WebInspector.TimelineModel.PageFrame>}
+   */
+  rootFrames() {
+    return Array.from(this._pageFrames.values()).filter(frame => !frame.parent);
+  }
+
+  /**
    * @param {string} frameId
    * @return {?WebInspector.TimelineModel.PageFrame}
    */
@@ -1040,6 +1066,14 @@
   }
 
   /**
+   * @param {string} frameId
+   * @return {!Array<!WebInspector.TracingModel.Event>}
+   */
+  eventsForFrame(frameId) {
+    return this._eventsByFrame.get(frameId) || [];
+  }
+
+  /**
    * @return {!Array<!WebInspector.TimelineModel.NetworkRequest>}
    */
   networkRequests() {
@@ -1352,8 +1386,44 @@
 WebInspector.TimelineModel.MetadataEvents;
 
 
-/** @typedef {!{url: string, processId: number, frameId: string, name: string}} */
-WebInspector.TimelineModel.PageFrame;
+WebInspector.TimelineModel.PageFrame = class {
+  /**
+   * @param {?WebInspector.Target} target
+   * @param {number} pid
+   * @param {!Object} payload
+   */
+  constructor(target, pid, payload) {
+    this.frameId = payload['frame'];
+    this.url = payload['url'] || '';
+    this.name = payload['name'];
+    this.processId = pid;
+    this.children = [];
+    /** @type {?WebInspector.TimelineModel.PageFrame} */
+    this.parent = null;
+    this.id = `${this.processId}.${this.frameId}`;
+    this.ownerNode = target && payload['nodeId'] ? new WebInspector.DeferredDOMNode(target, payload['nodeId']) : null;
+  }
+
+  /**
+   * @param {string} name
+   * @param {string} url
+   */
+  update(name, url) {
+    this.name = name;
+    this.url = url;
+  }
+
+  /**
+   * @param {!WebInspector.TimelineModel.PageFrame} child
+   */
+  addChild(child) {
+    this.children.push(child);
+    child.parent = this;
+  }
+};
+
+WebInspector.TimelineModel.PageFrame.mainFrameId = '';
+
 
 /**
  * @unrestricted
@@ -1870,6 +1940,7 @@
     this.picture = null;
     /** @type {?WebInspector.TracingModel.Event} */
     this._initiator = null;
+    this.frameId = '';
     /** @type {number|undefined} */
     this.timeWaitingForMainThread;
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineProfileTree.js b/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineProfileTree.js
index c1a09e7..c05d882 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineProfileTree.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineProfileTree.js
@@ -1,6 +1,7 @@
 // Copyright 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+
 WebInspector.TimelineProfileTree = {};
 
 /**
@@ -39,7 +40,7 @@
  * @param {!Array<!WebInspector.TimelineModel.Filter>} filters
  * @param {number} startTime
  * @param {number} endTime
- * @param {function(!WebInspector.TracingModel.Event, string):string=} eventGroupIdCallback
+ * @param {function(!WebInspector.TracingModel.Event):string=} eventGroupIdCallback
  * @return {!WebInspector.TimelineProfileTree.Node}
  */
 WebInspector.TimelineProfileTree.buildTopDown = function(events, filters, startTime, endTime, eventGroupIdCallback) {
@@ -49,19 +50,16 @@
   root.totalTime = initialTime;
   root.selfTime = initialTime;
   root.children = /** @type {!Map<string, !WebInspector.TimelineProfileTree.Node>} */ (new Map());
-  var pageFrameIdStack = [];
   var parent = root;
 
   /**
    * @param {!WebInspector.TracingModel.Event} e
    */
   function onStartEvent(e) {
-    var pageFrameId = WebInspector.TimelineModel.eventFrameId(e) || pageFrameIdStack.peekLast();
-    pageFrameIdStack.push(pageFrameId);
     if (!WebInspector.TimelineModel.isVisible(filters, e))
       return;
     var time = e.endTime ? Math.min(endTime, e.endTime) - Math.max(startTime, e.startTime) : 0;
-    var groupId = eventGroupIdCallback ? eventGroupIdCallback(e, pageFrameId) : Symbol('uniqueGroupId');
+    var groupId = eventGroupIdCallback ? eventGroupIdCallback(e) : Symbol('uniqueGroupId');
     var id = eventGroupIdCallback ? WebInspector.TimelineProfileTree._eventId(e) : Symbol('uniqueEventId');
     if (typeof groupId === 'string' && typeof id === 'string')
       id += '/' + groupId;
@@ -94,7 +92,6 @@
    * @param {!WebInspector.TracingModel.Event} e
    */
   function onEndEvent(e) {
-    pageFrameIdStack.pop();
     if (!WebInspector.TimelineModel.isVisible(filters, e))
       return;
     parent = parent.parent;
@@ -217,14 +214,17 @@
  * @return {string}
  */
 WebInspector.TimelineProfileTree._eventId = function(event) {
-  if (event.name === WebInspector.TimelineModel.RecordType.JSFrame) {
-    var data = event.args['data'];
-    return 'f:' + data['functionName'] + '@' + (data['scriptId'] || data['url'] || '');
-  }
-  return event.name;
+  if (event.name !== WebInspector.TimelineModel.RecordType.JSFrame)
+    return event.name;
+  const frame = event.args['data'];
+  const location = frame['scriptId'] || frame['url'] || '';
+  const functionName = frame['functionName'];
+  const name = WebInspector.TimelineJSProfileProcessor.isNativeRuntimeFrame(frame)
+      ? WebInspector.TimelineJSProfileProcessor.nativeGroup(functionName) || functionName
+      : functionName;
+  return `f:${name}@${location}`;
 };
 
-
 /**
  * @unrestricted
  */
diff --git a/third_party/WebKit/Source/modules/mediasession/MediaImage.cpp b/third_party/WebKit/Source/modules/mediasession/MediaImage.cpp
index ebad13eb..c2e08e9f 100644
--- a/third_party/WebKit/Source/modules/mediasession/MediaImage.cpp
+++ b/third_party/WebKit/Source/modules/mediasession/MediaImage.cpp
@@ -5,7 +5,10 @@
 #include "modules/mediasession/MediaImage.h"
 
 #include "core/dom/ExecutionContext.h"
+#include "core/inspector/ConsoleMessage.h"
 #include "modules/mediasession/MediaImageInit.h"
+#include "platform/weborigin/KURL.h"
+#include "wtf/text/StringOperators.h"
 
 namespace blink {
 
@@ -17,6 +20,11 @@
 
 MediaImage::MediaImage(ExecutionContext* context, const MediaImageInit& image) {
   m_src = context->completeURL(image.src());
+  if (!KURL(ParsedURLString, m_src).isValid()) {
+    context->addConsoleMessage(
+        ConsoleMessage::create(JSMessageSource, WarningMessageLevel,
+                               "MediaImage src is invalid: " + image.src()));
+  }
   m_sizes = image.sizes();
   m_type = image.type();
 }
diff --git a/third_party/WebKit/Source/modules/mediasession/MediaMetadataSanitizer.cpp b/third_party/WebKit/Source/modules/mediasession/MediaMetadataSanitizer.cpp
index b3ab3d5..b2fc748 100644
--- a/third_party/WebKit/Source/modules/mediasession/MediaMetadataSanitizer.cpp
+++ b/third_party/WebKit/Source/modules/mediasession/MediaMetadataSanitizer.cpp
@@ -4,11 +4,14 @@
 
 #include "modules/mediasession/MediaMetadataSanitizer.h"
 
+#include "core/dom/ExecutionContext.h"
+#include "core/inspector/ConsoleMessage.h"
 #include "modules/mediasession/MediaImage.h"
 #include "modules/mediasession/MediaMetadata.h"
 #include "public/platform/WebIconSizesParser.h"
 #include "public/platform/WebSize.h"
 #include "url/url_constants.h"
+#include "wtf/text/StringOperators.h"
 
 namespace blink {
 
@@ -30,29 +33,40 @@
 // Maximum of sizes in a MediaImage.
 const size_t kMaxNumberOfImageSizes = 10;
 
-bool checkMediaImageSrcSanity(const KURL& src) {
+bool checkMediaImageSrcSanity(const KURL& src, ExecutionContext* context) {
+  // Console warning for invalid src is printed upon MediaImage creation.
   if (!src.isValid())
     return false;
+
   if (!src.protocolIs(url::kHttpScheme) && !src.protocolIs(url::kHttpsScheme) &&
       !src.protocolIs(url::kDataScheme)) {
+    context->addConsoleMessage(ConsoleMessage::create(
+        JSMessageSource, WarningMessageLevel,
+        "MediaImage src can only be of http/https/data scheme: " +
+            src.getString()));
     return false;
   }
   DCHECK(src.getString().is8Bit());
-  if (src.getString().length() > url::kMaxURLChars)
+  if (src.getString().length() > url::kMaxURLChars) {
+    context->addConsoleMessage(ConsoleMessage::create(
+        JSMessageSource, WarningMessageLevel,
+        "MediaImage src exceeds maximum URL length: " + src.getString()));
     return false;
+  }
   return true;
 }
 
 // Sanitize MediaImage and do mojo serialization. Returns null when
 // |image.src()| is bad.
 blink::mojom::blink::MediaImagePtr sanitizeMediaImageAndConvertToMojo(
-    const MediaImage* image) {
+    const MediaImage* image,
+    ExecutionContext* context) {
   DCHECK(image);
 
   blink::mojom::blink::MediaImagePtr mojoImage;
 
   KURL url = KURL(ParsedURLString, image->src());
-  if (!checkMediaImageSrcSanity(url))
+  if (!checkMediaImageSrcSanity(url, context))
     return mojoImage;
 
   mojoImage = blink::mojom::blink::MediaImage::New();
@@ -61,8 +75,13 @@
   for (const auto& webSize :
        WebIconSizesParser::parseIconSizes(image->sizes())) {
     mojoImage->sizes.append(webSize);
-    if (mojoImage->sizes.size() == kMaxNumberOfImageSizes)
+    if (mojoImage->sizes.size() == kMaxNumberOfImageSizes) {
+      context->addConsoleMessage(ConsoleMessage::create(
+          JSMessageSource, WarningMessageLevel,
+          "The number of MediaImage sizes exceeds the upper limit. "
+          "All remaining MediaImage will be ignored"));
       break;
+    }
   }
   return mojoImage;
 }
@@ -70,8 +89,8 @@
 }  // anonymous namespace
 
 blink::mojom::blink::MediaMetadataPtr
-MediaMetadataSanitizer::sanitizeAndConvertToMojo(
-    const MediaMetadata* metadata) {
+MediaMetadataSanitizer::sanitizeAndConvertToMojo(const MediaMetadata* metadata,
+                                                 ExecutionContext* context) {
   blink::mojom::blink::MediaMetadataPtr mojoMetadata;
   if (!metadata)
     return mojoMetadata;
@@ -84,11 +103,16 @@
 
   for (const auto image : metadata->artwork()) {
     blink::mojom::blink::MediaImagePtr mojoImage =
-        sanitizeMediaImageAndConvertToMojo(image.get());
+        sanitizeMediaImageAndConvertToMojo(image.get(), context);
     if (!mojoImage.is_null())
       mojoMetadata->artwork.append(std::move(mojoImage));
-    if (mojoMetadata->artwork.size() == kMaxNumberOfMediaImages)
+    if (mojoMetadata->artwork.size() == kMaxNumberOfMediaImages) {
+      context->addConsoleMessage(ConsoleMessage::create(
+          JSMessageSource, WarningMessageLevel,
+          "The number of MediaImage sizes exceeds the upper limit. "
+          "All remaining MediaImage will be ignored"));
       break;
+    }
   }
   return mojoMetadata;
 }
diff --git a/third_party/WebKit/Source/modules/mediasession/MediaMetadataSanitizer.h b/third_party/WebKit/Source/modules/mediasession/MediaMetadataSanitizer.h
index 35687b52..abbbf65 100644
--- a/third_party/WebKit/Source/modules/mediasession/MediaMetadataSanitizer.h
+++ b/third_party/WebKit/Source/modules/mediasession/MediaMetadataSanitizer.h
@@ -9,6 +9,7 @@
 
 namespace blink {
 
+class ExecutionContext;
 class MediaMetadata;
 
 class MediaMetadataSanitizer {
@@ -16,7 +17,8 @@
   // Produce the sanitized metadata, which will later be sent to the
   // MediaSession mojo service.
   static blink::mojom::blink::MediaMetadataPtr sanitizeAndConvertToMojo(
-      const MediaMetadata*);
+      const MediaMetadata*,
+      ExecutionContext*);
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/mediasession/MediaSession.cpp b/third_party/WebKit/Source/modules/mediasession/MediaSession.cpp
index ab3bfc3f..dc2bb5a 100644
--- a/third_party/WebKit/Source/modules/mediasession/MediaSession.cpp
+++ b/third_party/WebKit/Source/modules/mediasession/MediaSession.cpp
@@ -83,8 +83,8 @@
 void MediaSession::setMetadata(MediaMetadata* metadata) {
   if (mojom::blink::MediaSessionService* service =
           getService(m_scriptState.get())) {
-    service->SetMetadata(
-        MediaMetadataSanitizer::sanitizeAndConvertToMojo(metadata));
+    service->SetMetadata(MediaMetadataSanitizer::sanitizeAndConvertToMojo(
+        metadata, getExecutionContext()));
   }
 }
 
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequestUpdateEvent.cpp b/third_party/WebKit/Source/modules/payments/PaymentRequestUpdateEvent.cpp
index 34f1fb19..1b63d6e 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentRequestUpdateEvent.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequestUpdateEvent.cpp
@@ -116,6 +116,7 @@
     return;
   }
 
+  stopPropagation();
   stopImmediatePropagation();
   m_waitForUpdate = true;
   m_abortTimer.stop();
diff --git a/third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp b/third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp
index 037586d..eadf3eb0 100644
--- a/third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp
+++ b/third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp
@@ -115,8 +115,8 @@
   }
 
   const sk_sp<SkImage> image = blinkImage->imageForCurrentFrame();
-  DCHECK_EQ(img->naturalWidth(), image->width());
-  DCHECK_EQ(img->naturalHeight(), image->height());
+  DCHECK_EQ(img->naturalWidth(), static_cast<unsigned>(image->width()));
+  DCHECK_EQ(img->naturalHeight(), static_cast<unsigned>(image->height()));
 
   if (!image) {
     resolver->reject(DOMException::create(
diff --git a/third_party/WebKit/Source/modules/speech/SpeechRecognition.idl b/third_party/WebKit/Source/modules/speech/SpeechRecognition.idl
index 901dc13..e8fac10 100644
--- a/third_party/WebKit/Source/modules/speech/SpeechRecognition.idl
+++ b/third_party/WebKit/Source/modules/speech/SpeechRecognition.idl
@@ -23,6 +23,8 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+// https://dvcs.w3.org/hg/speech-api/raw-file/tip/webspeechapi.html#speechreco-section
+
 [
     ActiveScriptWrappable,
     Constructor,
@@ -30,17 +32,22 @@
     DependentLifetime,
     NoInterfaceObject,
 ] interface SpeechRecognition : EventTarget {
-    [LegacyInterfaceTypeChecking] attribute SpeechGrammarList grammars;
+    // recognition parameters
+    attribute SpeechGrammarList grammars;
     attribute DOMString lang;
     attribute boolean continuous;
     attribute boolean interimResults;
     attribute unsigned long maxAlternatives;
-    [RuntimeEnabled=MediaStreamSpeech, LegacyInterfaceTypeChecking] attribute MediaStreamTrack audioTrack;
 
+    // Speech Recognition with WebRTC. https://crbug.com/408940
+    [RuntimeEnabled=MediaStreamSpeech] attribute MediaStreamTrack? audioTrack;
+
+    // methods to drive the speech interaction
     [RaisesException] void start();
     [ImplementedAs=stopFunction] void stop();
     void abort();
 
+    // event methods
     attribute EventHandler onaudiostart;
     attribute EventHandler onsoundstart;
     attribute EventHandler onspeechstart;
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index 2c99df0..4aa2607 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -6,6 +6,7 @@
 import("//build/config/ui.gni")
 import("//testing/libfuzzer/fuzzer_test.gni")
 import("//testing/test.gni")
+import("//third_party/WebKit/public/public_features.gni")
 import("//third_party/WebKit/Source/build/scripts/scripts.gni")
 import("//third_party/WebKit/Source/config.gni")
 import("//third_party/WebKit/Source/platform/platform_generated.gni")
@@ -1103,6 +1104,8 @@
     "network/LinkHeader.h",
     "network/NetworkHints.cpp",
     "network/NetworkHints.h",
+    "network/NetworkInstrumentation.cpp",
+    "network/NetworkInstrumentation.h",
     "network/NetworkLog.h",
     "network/NetworkUtils.cpp",
     "network/NetworkUtils.h",
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
index e310f6c..23e4996 100644
--- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
+++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
@@ -122,7 +122,7 @@
 InstalledApp status=experimental
 IntersectionObserver status=stable
 LangAttributeAwareFormControlUI
-LayoutNG depends_on=LayoutNGInline
+LayoutNG
 LayoutNGInline
 LinkServiceWorker status=experimental, origin_trial_feature_name=ForeignFetch
 LongTaskObserver status=experimental, origin_trial_feature_name=LongTaskObserver
@@ -192,7 +192,7 @@
 ReloadwithoutSubResourceCacheRevalidation
 RemotePlayback status=experimental
 RenderingPipelineThrottling status=stable
-RenderingPipelineThrottlingLoadingIframes status=experimental
+RenderingPipelineThrottlingLoadingIframes status=stable
 RenderUnicodeControlCharacters status=stable
 ResizeObserver status=experimental
 // Handles frame scrolling via the root PaintLayer instead of the FrameView.
diff --git a/third_party/WebKit/Source/platform/fonts/Font.cpp b/third_party/WebKit/Source/platform/fonts/Font.cpp
index 37e0ad69..185d713 100644
--- a/third_party/WebKit/Source/platform/fonts/Font.cpp
+++ b/third_party/WebKit/Source/platform/fonts/Font.cpp
@@ -493,6 +493,10 @@
   return shaper.offsetForPosition(this, run, xFloat, includePartialGlyphs);
 }
 
+ShapeCache* Font::shapeCache() const {
+  return m_fontFallbackList->shapeCache(m_fontDescription);
+}
+
 bool Font::canShapeWordByWord() const {
   if (!m_shapeWordByWordComputed) {
     m_canShapeWordByWord = computeCanShapeWordByWord();
diff --git a/third_party/WebKit/Source/platform/fonts/Font.h b/third_party/WebKit/Source/platform/fonts/Font.h
index 3718c24..8d1db980 100644
--- a/third_party/WebKit/Source/platform/fonts/Font.h
+++ b/third_party/WebKit/Source/platform/fonts/Font.h
@@ -52,6 +52,7 @@
 class FontData;
 class FontSelector;
 class GlyphBuffer;
+class ShapeCache;
 class TextRun;
 struct TextRunPaintInfo;
 
@@ -156,6 +157,8 @@
   const SimpleFontData* primaryFont() const;
   const FontData* fontDataAt(unsigned) const;
 
+  ShapeCache* shapeCache() const;
+
   // Whether the font supports shaping word by word instead of shaping the
   // full run in one go. Allows better caching for fonts where space cannot
   // participate in kerning and/or ligatures.
diff --git a/third_party/WebKit/Source/platform/graphics/CompositorFilterOperations.cpp b/third_party/WebKit/Source/platform/graphics/CompositorFilterOperations.cpp
index b968f4d..b40dd532 100644
--- a/third_party/WebKit/Source/platform/graphics/CompositorFilterOperations.cpp
+++ b/third_party/WebKit/Source/platform/graphics/CompositorFilterOperations.cpp
@@ -92,4 +92,9 @@
   return m_filterOperations.IsEmpty();
 }
 
+bool CompositorFilterOperations::operator==(
+    const CompositorFilterOperations& o) const {
+  return m_filterOperations == o.m_filterOperations;
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/CompositorFilterOperations.h b/third_party/WebKit/Source/platform/graphics/CompositorFilterOperations.h
index 34014d7..57a70952 100644
--- a/third_party/WebKit/Source/platform/graphics/CompositorFilterOperations.h
+++ b/third_party/WebKit/Source/platform/graphics/CompositorFilterOperations.h
@@ -40,6 +40,11 @@
   void clear();
   bool isEmpty() const;
 
+  bool operator==(const CompositorFilterOperations&) const;
+  bool operator!=(const CompositorFilterOperations& o) const {
+    return !(*this == o);
+  }
+
  private:
   cc::FilterOperations m_filterOperations;
 };
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ClipPaintPropertyNode.h b/third_party/WebKit/Source/platform/graphics/paint/ClipPaintPropertyNode.h
index 71012d4..5f34e60c 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ClipPaintPropertyNode.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/ClipPaintPropertyNode.h
@@ -54,6 +54,23 @@
   const ClipPaintPropertyNode* parent() const { return m_parent.get(); }
   bool isRoot() const { return !m_parent; }
 
+#if DCHECK_IS_ON()
+  // The clone function is used by FindPropertiesNeedingUpdate.h for recording
+  // a clip node before it has been updated, to later detect changes.
+  PassRefPtr<ClipPaintPropertyNode> clone() const {
+    return adoptRef(
+        new ClipPaintPropertyNode(m_parent, m_localTransformSpace, m_clipRect));
+  }
+
+  // The equality operator is used by FindPropertiesNeedingUpdate.h for checking
+  // if a clip node has changed.
+  bool operator==(const ClipPaintPropertyNode& o) const {
+    return m_parent == o.m_parent &&
+           m_localTransformSpace == o.m_localTransformSpace &&
+           m_clipRect == o.m_clipRect;
+  }
+#endif
+
  private:
   ClipPaintPropertyNode(
       PassRefPtr<const ClipPaintPropertyNode> parent,
diff --git a/third_party/WebKit/Source/platform/graphics/paint/CullRect.h b/third_party/WebKit/Source/platform/graphics/paint/CullRect.h
index 20ca2ad..48aa42f 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/CullRect.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/CullRect.h
@@ -24,6 +24,7 @@
   DISALLOW_NEW();
 
  public:
+  CullRect() {}
   explicit CullRect(const IntRect& rect) : m_rect(rect) {}
   CullRect(const CullRect&, const IntPoint& offset);
   CullRect(const CullRect&, const IntSize& offset);
@@ -41,6 +42,8 @@
  private:
   IntRect m_rect;
 
+  friend bool operator==(const CullRect&, const CullRect&);
+
   // TODO(chrishtr): temporary while we implement CullRect everywhere.
   friend class FramePainter;
   friend class GridPainter;
@@ -53,6 +56,13 @@
   friend class WebPluginContainerImpl;
 };
 
+inline bool operator==(const CullRect& a, const CullRect& b) {
+  return a.m_rect == b.m_rect;
+}
+inline bool operator!=(const CullRect& a, const CullRect& b) {
+  return !(a == b);
+}
+
 }  // namespace blink
 
 #endif  // CullRect_h
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp
index ecaa5b61..f37ca39 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp
@@ -68,23 +68,6 @@
     return "Unknown"
 
 static WTF::String specialDrawingTypeAsDebugString(DisplayItem::Type type) {
-  if (type >= DisplayItem::kTableCollapsedBorderUnalignedBase) {
-    if (type <= DisplayItem::kTableCollapsedBorderBase)
-      return "TableCollapsedBorderAlignment";
-    if (type <= DisplayItem::kTableCollapsedBorderLast) {
-      StringBuilder sb;
-      sb.append("TableCollapsedBorder");
-      if (type & DisplayItem::TableCollapsedBorderTop)
-        sb.append("Top");
-      if (type & DisplayItem::TableCollapsedBorderRight)
-        sb.append("Right");
-      if (type & DisplayItem::TableCollapsedBorderBottom)
-        sb.append("Bottom");
-      if (type & DisplayItem::TableCollapsedBorderLeft)
-        sb.append("Left");
-      return sb.toString();
-    }
-  }
   switch (type) {
     DEBUG_STRING_CASE(BoxDecorationBackground);
     DEBUG_STRING_CASE(Caret);
@@ -126,6 +109,7 @@
     DEBUG_STRING_CASE(TableCellBackgroundFromColumn);
     DEBUG_STRING_CASE(TableCellBackgroundFromSection);
     DEBUG_STRING_CASE(TableCellBackgroundFromRow);
+    DEBUG_STRING_CASE(TableCollapsedBorders);
     DEBUG_STRING_CASE(TableSectionBoxShadowInset);
     DEBUG_STRING_CASE(TableSectionBoxShadowNormal);
     DEBUG_STRING_CASE(TableRowBoxShadowInset);
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h
index 69d0457..591a3e7 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h
@@ -110,15 +110,7 @@
     kTableCellBackgroundFromColumn,
     kTableCellBackgroundFromSection,
     kTableCellBackgroundFromRow,
-    // Table collapsed borders can be painted together (e.g., left & top) but
-    // there are at most 4 phases of collapsed border painting for a single
-    // cell. To disambiguate these phases of collapsed border painting, a mask
-    // is used. TableCollapsedBorderBase can be larger than
-    // TableCollapsedBorderUnalignedBase to ensure the base lower bits are 0's.
-    kTableCollapsedBorderUnalignedBase,
-    kTableCollapsedBorderBase =
-        (((kTableCollapsedBorderUnalignedBase - 1) >> 4) + 1) << 4,
-    kTableCollapsedBorderLast = kTableCollapsedBorderBase + 0x0f,
+    kTableCollapsedBorders,
     kTableSectionBoxShadowInset,
     kTableSectionBoxShadowNormal,
     kTableRowBoxShadowInset,
@@ -202,19 +194,6 @@
     kTypeLast = kUninitializedType
   };
 
-  static_assert(kTableCollapsedBorderBase >= kTableCollapsedBorderUnalignedBase,
-                "TableCollapsedBorder types overlap with other types");
-  static_assert((kTableCollapsedBorderBase & 0xf) == 0,
-                "The lowest 4 bits of TableCollapsedBorderBase should be zero");
-  // Bits or'ed onto TableCollapsedBorderBase to generate a real table collapsed
-  // border type.
-  enum TableCollapsedBorderSides {
-    TableCollapsedBorderTop = 1 << 0,
-    TableCollapsedBorderRight = 1 << 1,
-    TableCollapsedBorderBottom = 1 << 2,
-    TableCollapsedBorderLeft = 1 << 3,
-  };
-
   DisplayItem(const DisplayItemClient& client, Type type, size_t derivedSize)
       : m_client(&client),
         m_type(type),
diff --git a/third_party/WebKit/Source/platform/graphics/paint/EffectPaintPropertyNode.h b/third_party/WebKit/Source/platform/graphics/paint/EffectPaintPropertyNode.h
index 374281f2..e2d6099ee 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/EffectPaintPropertyNode.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/EffectPaintPropertyNode.h
@@ -64,6 +64,24 @@
   const EffectPaintPropertyNode* parent() const { return m_parent.get(); }
   bool isRoot() const { return !m_parent; }
 
+#if DCHECK_IS_ON()
+  // The clone function is used by FindPropertiesNeedingUpdate.h for recording
+  // an effect node before it has been updated, to later detect changes.
+  PassRefPtr<EffectPaintPropertyNode> clone() const {
+    return adoptRef(new EffectPaintPropertyNode(
+        m_parent, m_localTransformSpace, m_outputClip, m_filter, m_opacity));
+  }
+
+  // The equality operator is used by FindPropertiesNeedingUpdate.h for checking
+  // if an effect node has changed.
+  bool operator==(const EffectPaintPropertyNode& o) const {
+    return m_parent == o.m_parent &&
+           m_localTransformSpace == o.m_localTransformSpace &&
+           m_outputClip == o.m_outputClip && m_filter == o.m_filter &&
+           m_opacity == o.m_opacity;
+  }
+#endif
+
  private:
   EffectPaintPropertyNode(
       PassRefPtr<const EffectPaintPropertyNode> parent,
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ScrollPaintPropertyNode.h b/third_party/WebKit/Source/platform/graphics/paint/ScrollPaintPropertyNode.h
index 3f075cc..18779c95 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ScrollPaintPropertyNode.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/ScrollPaintPropertyNode.h
@@ -91,6 +91,32 @@
   }
   void clearMainThreadScrollingReasons() { m_mainThreadScrollingReasons = 0; }
 
+#if DCHECK_IS_ON()
+  // The clone function is used by FindPropertiesNeedingUpdate.h for recording
+  // a scroll node before it has been updated, to later detect changes.
+  PassRefPtr<ScrollPaintPropertyNode> clone() const {
+    RefPtr<ScrollPaintPropertyNode> cloned =
+        adoptRef(new ScrollPaintPropertyNode(
+            m_parent, m_scrollOffsetTranslation, m_clip, m_bounds,
+            m_userScrollableHorizontal, m_userScrollableVertical));
+    cloned->addMainThreadScrollingReasons(m_mainThreadScrollingReasons);
+    return cloned;
+  }
+
+  // The equality operator is used by FindPropertiesNeedingUpdate.h for checking
+  // if a scroll node has changed.
+  bool operator==(const ScrollPaintPropertyNode& o) const {
+    // TODO(pdr): Check main thread scrolling reason equality as well. We do
+    // not yet mark nodes as needing a paint property update on main thread
+    // scrolling reason changes. See: See: https://crbug.com/664672.
+    return m_parent == o.m_parent &&
+           m_scrollOffsetTranslation == o.m_scrollOffsetTranslation &&
+           m_clip == o.m_clip && m_bounds == o.m_bounds &&
+           m_userScrollableHorizontal == o.m_userScrollableHorizontal &&
+           m_userScrollableVertical == o.m_userScrollableVertical;
+  }
+#endif
+
  private:
   ScrollPaintPropertyNode(
       PassRefPtr<ScrollPaintPropertyNode> parent,
diff --git a/third_party/WebKit/Source/platform/graphics/paint/SkPictureBuilder.cpp b/third_party/WebKit/Source/platform/graphics/paint/SkPictureBuilder.cpp
index 90716fa..cdc23e9b 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/SkPictureBuilder.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/SkPictureBuilder.cpp
@@ -13,14 +13,19 @@
 
 SkPictureBuilder::SkPictureBuilder(const FloatRect& bounds,
                                    SkMetaData* metaData,
-                                   GraphicsContext* containingContext)
-    : m_bounds(bounds) {
+                                   GraphicsContext* containingContext,
+                                   PaintController* paintController)
+    : m_paintController(nullptr), m_bounds(bounds) {
   GraphicsContext::DisabledMode disabledMode = GraphicsContext::NothingDisabled;
   if (containingContext && containingContext->contextDisabled())
     disabledMode = GraphicsContext::FullyDisabled;
 
-  m_paintController = PaintController::create();
-  m_paintController->beginSkippingCache();
+  if (paintController) {
+    m_paintController = paintController;
+  } else {
+    m_paintControllerPtr = PaintController::create();
+    m_paintController = m_paintControllerPtr.get();
+  }
   m_context = wrapUnique(
       new GraphicsContext(*m_paintController, disabledMode, metaData));
 
@@ -34,7 +39,6 @@
 
 sk_sp<SkPicture> SkPictureBuilder::endRecording() {
   m_context->beginRecording(m_bounds);
-  m_paintController->endSkippingCache();
   m_paintController->commitNewDisplayItems();
   m_paintController->paintArtifact().replay(*m_context);
   return m_context->endRecording();
diff --git a/third_party/WebKit/Source/platform/graphics/paint/SkPictureBuilder.h b/third_party/WebKit/Source/platform/graphics/paint/SkPictureBuilder.h
index 73fbd898..a6b3d4c 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/SkPictureBuilder.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/SkPictureBuilder.h
@@ -31,9 +31,14 @@
   // builder's internal canvas. If |containingContext| is specified, the device
   // scale factor, printing, and disabled state are propagated to the builder's
   // internal context.
+  // If a PaintController is passed, it is used as the PaintController for
+  // painting the picture (and hence we can use its cache). Otherwise, a new
+  // PaintController is used for the duration of the picture building, which
+  // therefore has no caching.
   SkPictureBuilder(const FloatRect& bounds,
                    SkMetaData* = nullptr,
-                   GraphicsContext* containingContext = nullptr);
+                   GraphicsContext* containingContext = nullptr,
+                   PaintController* = nullptr);
   ~SkPictureBuilder();
 
   GraphicsContext& context() { return *m_context; }
@@ -47,7 +52,8 @@
   LayoutRect visualRect() const final { return LayoutRect(); }
 
  private:
-  std::unique_ptr<PaintController> m_paintController;
+  PaintController* m_paintController;
+  std::unique_ptr<PaintController> m_paintControllerPtr;
   std::unique_ptr<GraphicsContext> m_context;
   FloatRect m_bounds;
 };
diff --git a/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.h b/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.h
index 0aca67e..712761a 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.h
@@ -71,6 +71,25 @@
   unsigned renderingContextID() const { return m_renderingContextID; }
   bool hasRenderingContext() const { return m_renderingContextID; }
 
+#if DCHECK_IS_ON()
+  // The clone function is used by FindPropertiesNeedingUpdate.h for recording
+  // a transform node before it has been updated, to later detect changes.
+  PassRefPtr<TransformPaintPropertyNode> clone() const {
+    return adoptRef(new TransformPaintPropertyNode(m_matrix, m_origin, m_parent,
+                                                   m_flattensInheritedTransform,
+                                                   m_renderingContextID));
+  }
+
+  // The equality operator is used by FindPropertiesNeedingUpdate.h for checking
+  // if a transform node has changed.
+  bool operator==(const TransformPaintPropertyNode& o) const {
+    return m_matrix == o.m_matrix && m_origin == o.m_origin &&
+           m_parent == o.m_parent &&
+           m_flattensInheritedTransform == o.m_flattensInheritedTransform &&
+           m_renderingContextID == o.m_renderingContextID;
+  }
+#endif
+
  private:
   TransformPaintPropertyNode(
       const TransformationMatrix& matrix,
diff --git a/third_party/WebKit/Source/platform/network/NetworkInstrumentation.cpp b/third_party/WebKit/Source/platform/network/NetworkInstrumentation.cpp
new file mode 100644
index 0000000..bb2a7d23
--- /dev/null
+++ b/third_party/WebKit/Source/platform/network/NetworkInstrumentation.cpp
@@ -0,0 +1,75 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "platform/network/NetworkInstrumentation.h"
+
+#include "base/trace_event/trace_event.h"
+#include "platform/network/ResourceLoadPriority.h"
+#include "platform/network/ResourceRequest.h"
+#include "platform/tracing/TracedValue.h"
+
+namespace network_instrumentation {
+
+using network_instrumentation::RequestOutcome;
+using blink::TracedValue;
+
+const char kBlinkResourceID[] = "BlinkResourceID";
+const char kResourceLoadTitle[] = "ResourceLoad";
+const char kResourcePrioritySetTitle[] = "ResourcePrioritySet";
+const char kNetInstrumentationCategory[] = TRACE_DISABLED_BY_DEFAULT("network");
+
+const char* RequestOutcomeToString(RequestOutcome outcome) {
+  switch (outcome) {
+    case RequestOutcome::Success:
+      return "Success";
+    case RequestOutcome::Fail:
+      return "Fail";
+    default:
+      NOTREACHED();
+      // We need to return something to avoid compiler warning.
+      return "This should never happen";
+  }
+}
+
+ScopedResourceLoadTracker::ScopedResourceLoadTracker(
+    unsigned long resourceID,
+    const blink::ResourceRequest& request)
+    : m_resourceLoadContinuesBeyondScope(false), m_resourceID(resourceID) {
+  std::unique_ptr<TracedValue> beginData = TracedValue::create();
+  beginData->setString("url", request.url().getString());
+  TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
+      kNetInstrumentationCategory, kResourceLoadTitle,
+      TRACE_ID_WITH_SCOPE(kBlinkResourceID, TRACE_ID_LOCAL(resourceID)),
+      "beginData", std::move(beginData));
+}
+
+ScopedResourceLoadTracker::~ScopedResourceLoadTracker() {
+  if (!m_resourceLoadContinuesBeyondScope)
+    endResourceLoad(m_resourceID, RequestOutcome::Fail);
+}
+
+void ScopedResourceLoadTracker::resourceLoadContinuesBeyondScope() {
+  m_resourceLoadContinuesBeyondScope = true;
+}
+
+void resourcePrioritySet(unsigned long resourceID,
+                         blink::ResourceLoadPriority priority) {
+  std::unique_ptr<TracedValue> data = TracedValue::create();
+  data->setInteger("priority", priority);
+  TRACE_EVENT_NESTABLE_ASYNC_INSTANT1(
+      kNetInstrumentationCategory, kResourcePrioritySetTitle,
+      TRACE_ID_WITH_SCOPE(kBlinkResourceID, TRACE_ID_LOCAL(resourceID)), "data",
+      std::move(data));
+}
+
+void endResourceLoad(unsigned long resourceID, RequestOutcome outcome) {
+  std::unique_ptr<TracedValue> endData = TracedValue::create();
+  endData->setString("outcome", RequestOutcomeToString(outcome));
+  TRACE_EVENT_NESTABLE_ASYNC_END1(
+      kNetInstrumentationCategory, kResourceLoadTitle,
+      TRACE_ID_WITH_SCOPE(kBlinkResourceID, TRACE_ID_LOCAL(resourceID)),
+      "endData", std::move(endData));
+}
+
+}  // namespace network_instrumentation
diff --git a/third_party/WebKit/Source/platform/network/NetworkInstrumentation.h b/third_party/WebKit/Source/platform/network/NetworkInstrumentation.h
new file mode 100644
index 0000000..e5c07ea0
--- /dev/null
+++ b/third_party/WebKit/Source/platform/network/NetworkInstrumentation.h
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NetworkInstrumentation_h
+#define NetworkInstrumentation_h
+
+#include "platform/PlatformExport.h"
+#include "platform/network/ResourceLoadPriority.h"
+
+namespace blink {
+class ResourceRequest;
+}  // namespace blink
+
+namespace network_instrumentation {
+
+enum RequestOutcome { Success, Fail };
+
+class PLATFORM_EXPORT ScopedResourceLoadTracker {
+ public:
+  ScopedResourceLoadTracker(unsigned long resourceID,
+                            const blink::ResourceRequest&);
+  ~ScopedResourceLoadTracker();
+  void resourceLoadContinuesBeyondScope();
+
+ private:
+  // If this variable is false, close resource load slice at end of scope.
+  bool m_resourceLoadContinuesBeyondScope;
+
+  const unsigned long m_resourceID;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedResourceLoadTracker);
+};
+
+void PLATFORM_EXPORT resourcePrioritySet(unsigned long resourceID,
+                                         blink::ResourceLoadPriority);
+
+void PLATFORM_EXPORT endResourceLoad(unsigned long resourceID, RequestOutcome);
+
+}  // namespace network_instrumentation
+
+#endif  // NetworkInstrumentation_h
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollableArea.cpp b/third_party/WebKit/Source/platform/scroll/ScrollableArea.cpp
index 838c8e3..5ee4df68 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollableArea.cpp
+++ b/third_party/WebKit/Source/platform/scroll/ScrollableArea.cpp
@@ -389,11 +389,9 @@
 }
 
 bool ScrollableArea::hasOverlayScrollbars() const {
-  Scrollbar* vScrollbar = verticalScrollbar();
-  if (vScrollbar && vScrollbar->isOverlayScrollbar())
-    return true;
-  Scrollbar* hScrollbar = horizontalScrollbar();
-  return hScrollbar && hScrollbar->isOverlayScrollbar();
+  return (horizontalScrollbar() &&
+          horizontalScrollbar()->isOverlayScrollbar()) ||
+         (verticalScrollbar() && verticalScrollbar()->isOverlayScrollbar());
 }
 
 void ScrollableArea::setScrollbarOverlayColorTheme(
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollableArea.h b/third_party/WebKit/Source/platform/scroll/ScrollableArea.h
index eb2188d9..930a1ce 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollableArea.h
+++ b/third_party/WebKit/Source/platform/scroll/ScrollableArea.h
@@ -109,10 +109,11 @@
   void mouseEnteredContentArea() const;
   void mouseExitedContentArea() const;
   void mouseMovedInContentArea() const;
-  void mouseEnteredScrollbar(Scrollbar&);
-  void mouseExitedScrollbar(Scrollbar&);
+  virtual void mouseEnteredScrollbar(Scrollbar&);
+  virtual void mouseExitedScrollbar(Scrollbar&);
   void mouseCapturedScrollbar();
   void mouseReleasedScrollbar();
+
   void contentAreaDidShow() const;
   void contentAreaDidHide() const;
 
@@ -123,10 +124,10 @@
 
   virtual void contentsResized();
 
-  bool hasOverlayScrollbars() const;
+  virtual bool hasOverlayScrollbars() const;
   void setScrollbarOverlayColorTheme(ScrollbarOverlayColorTheme);
   void recalculateScrollbarOverlayColorTheme(Color);
-  ScrollbarOverlayColorTheme getScrollbarOverlayColorTheme() const {
+  virtual ScrollbarOverlayColorTheme getScrollbarOverlayColorTheme() const {
     return static_cast<ScrollbarOverlayColorTheme>(
         m_scrollbarOverlayColorTheme);
   }
@@ -167,10 +168,10 @@
 
   virtual bool isActive() const = 0;
   virtual int scrollSize(ScrollbarOrientation) const = 0;
-  void setScrollbarNeedsPaintInvalidation(ScrollbarOrientation);
+  virtual void setScrollbarNeedsPaintInvalidation(ScrollbarOrientation);
   virtual bool isScrollCornerVisible() const = 0;
   virtual IntRect scrollCornerRect() const = 0;
-  void setScrollCornerNeedsPaintInvalidation();
+  virtual void setScrollCornerNeedsPaintInvalidation();
   virtual void getTickmarks(Vector<IntRect>&) const {}
 
   // Convert points and rects between the scrollbar and its containing Widget.
@@ -200,6 +201,12 @@
   virtual Scrollbar* horizontalScrollbar() const { return nullptr; }
   virtual Scrollbar* verticalScrollbar() const { return nullptr; }
 
+  // TODO(crbug.com/661236): This method should be moved to ScrollbarManager
+  // which will update the scrollbars directly for all subclasses.
+  // Called to update the scrollbars to accurately reflect the state of the
+  // view.
+  virtual void updateScrollbars(){};
+
   // scrollPosition is the location of the top/left of the scroll viewport in
   // the coordinate system defined by the top/left of the overflow rect.
   // scrollOffset is the offset of the scroll viewport from its position when
@@ -221,6 +228,9 @@
   virtual ScrollOffset maximumScrollOffset() const {
     return ScrollOffset(maximumScrollOffsetInt());
   }
+  virtual ScrollOffset scrollAnimatorDesiredTargetOffset() const {
+    return scrollAnimator().desiredTargetOffset();
+  }
 
   virtual IntRect visibleContentRect(
       IncludeScrollbarsInRect = ExcludeScrollbars) const;
@@ -231,6 +241,11 @@
 
   virtual bool shouldSuspendScrollAnimations() const { return true; }
   virtual void scrollbarStyleChanged() {}
+  // Called when scrollbar hides/shows for overlay scrollbars. This callback
+  // shouldn't do any significant work as it can be called unexpectadly often
+  // on Mac. This happens because painting code has to set alpha to 1, paint,
+  // then reset to alpha, causing spurrious "visibilityChanged" calls.
+  virtual void scrollbarVisibilityChanged() {}
   virtual bool scrollbarsCanBeActive() const = 0;
 
   // Returns the bounding box of this scrollable area, in the coordinate system
@@ -392,12 +407,6 @@
   }
   void showOverlayScrollbars();
 
-  // Called when scrollbar hides/shows for overlay scrollbars. This callback
-  // shouldn't do any significant work as it can be called unexpectadly often
-  // on Mac. This happens because painting code has to set alpha to 1, paint,
-  // then reset to alpha, causing spurrious "visibilityChanged" calls.
-  virtual void scrollbarVisibilityChanged() {}
-
  private:
   void programmaticScrollHelper(const ScrollOffset&, ScrollBehavior);
   void userScrollHelper(const ScrollOffset&, ScrollBehavior);
diff --git a/third_party/WebKit/Source/platform/scroll/Scrollbar.cpp b/third_party/WebKit/Source/platform/scroll/Scrollbar.cpp
index 516eed5..daf1e3d 100644
--- a/third_party/WebKit/Source/platform/scroll/Scrollbar.cpp
+++ b/third_party/WebKit/Source/platform/scroll/Scrollbar.cpp
@@ -253,8 +253,7 @@
     if (m_draggingDocument)
       delta = pos - m_documentDragPos;
     m_draggingDocument = true;
-    ScrollOffset currentPosition =
-        m_scrollableArea->scrollAnimator().currentOffset();
+    ScrollOffset currentPosition = m_scrollableArea->scrollOffset();
     float destinationPosition =
         (m_orientation == HorizontalScrollbar ? currentPosition.width()
                                               : currentPosition.height()) +
@@ -605,11 +604,11 @@
     return 0;
 
   if (m_orientation == HorizontalScrollbar) {
-    return m_scrollableArea->scrollAnimator().desiredTargetOffset().width() -
+    return m_scrollableArea->scrollAnimatorDesiredTargetOffset().width() -
            m_scrollableArea->minimumScrollOffset().width();
   }
 
-  return m_scrollableArea->scrollAnimator().desiredTargetOffset().height() -
+  return m_scrollableArea->scrollAnimatorDesiredTargetOffset().height() -
          m_scrollableArea->minimumScrollOffset().height();
 }
 
diff --git a/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.cpp b/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.cpp
index 89aa5e9..ab648fe 100644
--- a/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.cpp
+++ b/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.cpp
@@ -27,14 +27,27 @@
 #include "platform/weborigin/SchemeRegistry.h"
 
 #include "wtf/ThreadSpecific.h"
-#include "wtf/Threading.h"
 #include "wtf/ThreadingPrimitives.h"
 #include "wtf/text/StringBuilder.h"
 
 namespace blink {
 
+static Mutex& mutex() {
+  // The first call to this should be made before or during blink
+  // initialization to avoid racy static local initialization.
+  DEFINE_STATIC_LOCAL(Mutex, m, ());
+  return m;
+}
+
+// Defines static local variable after making sure that a lock is held.
+// (We can't use DEFINE_STATIC_LOCAL for this because it asserts thread
+// safety, which is externally guaranteed by the local mutex() lock)
+#define DEFINE_STATIC_LOCAL_WITH_LOCK(type, name, arguments) \
+  ASSERT(mutex().locked());                                  \
+  static type& name = *new type arguments
+
 static URLSchemesSet& localURLSchemes() {
-  DEFINE_STATIC_LOCAL(URLSchemesSet, localSchemes, ());
+  DEFINE_STATIC_LOCAL_WITH_LOCK(URLSchemesSet, localSchemes, ());
 
   if (localSchemes.isEmpty())
     localSchemes.add("file");
@@ -43,55 +56,58 @@
 }
 
 static URLSchemesSet& displayIsolatedURLSchemes() {
-  DEFINE_STATIC_LOCAL(URLSchemesSet, displayIsolatedSchemes, ());
+  DEFINE_STATIC_LOCAL_WITH_LOCK(URLSchemesSet, displayIsolatedSchemes, ());
   return displayIsolatedSchemes;
 }
 
 static URLSchemesSet& secureSchemes() {
-  DEFINE_STATIC_LOCAL(URLSchemesSet, secureSchemes,
-                      ({
-                          "https", "about", "data", "wss",
-                      }));
+  DEFINE_STATIC_LOCAL_WITH_LOCK(URLSchemesSet, secureSchemes,
+                                ({
+                                    "https", "about", "data", "wss",
+                                }));
   return secureSchemes;
 }
 
 static URLSchemesSet& schemesWithUniqueOrigins() {
-  DEFINE_STATIC_LOCAL(URLSchemesSet, schemesWithUniqueOrigins,
-                      ({
-                          "about", "javascript", "data",
-                      }));
+  DEFINE_STATIC_LOCAL_WITH_LOCK(URLSchemesSet, schemesWithUniqueOrigins,
+                                ({
+                                    "about", "javascript", "data",
+                                }));
   return schemesWithUniqueOrigins;
 }
 
 static URLSchemesSet& emptyDocumentSchemes() {
-  DEFINE_STATIC_LOCAL(URLSchemesSet, emptyDocumentSchemes, ({
-                                                               "about",
-                                                           }));
+  DEFINE_STATIC_LOCAL_WITH_LOCK(URLSchemesSet, emptyDocumentSchemes,
+                                ({
+                                    "about",
+                                }));
   return emptyDocumentSchemes;
 }
 
 static HashSet<String>& schemesForbiddenFromDomainRelaxation() {
-  DEFINE_STATIC_LOCAL(HashSet<String>, schemes, ());
+  DEFINE_STATIC_LOCAL_WITH_LOCK(HashSet<String>, schemes, ());
   return schemes;
 }
 
 static URLSchemesSet& notAllowingJavascriptURLsSchemes() {
-  DEFINE_STATIC_LOCAL(URLSchemesSet, notAllowingJavascriptURLsSchemes, ());
+  DEFINE_STATIC_LOCAL_WITH_LOCK(URLSchemesSet, notAllowingJavascriptURLsSchemes,
+                                ());
   return notAllowingJavascriptURLsSchemes;
 }
 
 void SchemeRegistry::registerURLSchemeAsLocal(const String& scheme) {
-  DCHECK(WTF::isBeforeThreadCreated());
   DCHECK_EQ(scheme, scheme.lower());
+  MutexLocker locker(mutex());
   localURLSchemes().add(scheme);
 }
 
 const URLSchemesSet& SchemeRegistry::localSchemes() {
+  MutexLocker locker(mutex());
   return localURLSchemes();
 }
 
 static URLSchemesSet& CORSEnabledSchemes() {
-  DEFINE_STATIC_LOCAL(URLSchemesSet, CORSEnabledSchemes, ());
+  DEFINE_STATIC_LOCAL_WITH_LOCK(URLSchemesSet, CORSEnabledSchemes, ());
 
   if (CORSEnabledSchemes.isEmpty()) {
     CORSEnabledSchemes.add("http");
@@ -103,7 +119,7 @@
 }
 
 static URLSchemesSet& serviceWorkerSchemes() {
-  DEFINE_STATIC_LOCAL(URLSchemesSet, serviceWorkerSchemes, ());
+  DEFINE_STATIC_LOCAL_WITH_LOCK(URLSchemesSet, serviceWorkerSchemes, ());
 
   if (serviceWorkerSchemes.isEmpty()) {
     // HTTP is required because http://localhost is considered secure.
@@ -117,7 +133,7 @@
 }
 
 static URLSchemesSet& fetchAPISchemes() {
-  DEFINE_STATIC_LOCAL(URLSchemesSet, fetchAPISchemes, ());
+  DEFINE_STATIC_LOCAL_WITH_LOCK(URLSchemesSet, fetchAPISchemes, ());
 
   if (fetchAPISchemes.isEmpty()) {
     fetchAPISchemes.add("http");
@@ -128,23 +144,26 @@
 }
 
 static URLSchemesSet& firstPartyWhenTopLevelSchemes() {
-  DEFINE_STATIC_LOCAL(URLSchemesSet, firstPartyWhenTopLevelSchemes, ());
+  DEFINE_STATIC_LOCAL_WITH_LOCK(URLSchemesSet, firstPartyWhenTopLevelSchemes,
+                                ());
   return firstPartyWhenTopLevelSchemes;
 }
 
 static URLSchemesMap<SchemeRegistry::PolicyAreas>&
 ContentSecurityPolicyBypassingSchemes() {
-  DEFINE_STATIC_LOCAL(URLSchemesMap<SchemeRegistry::PolicyAreas>, schemes, ());
+  DEFINE_STATIC_LOCAL_WITH_LOCK(URLSchemesMap<SchemeRegistry::PolicyAreas>,
+                                schemes, ());
   return schemes;
 }
 
 static URLSchemesSet& secureContextBypassingSchemes() {
-  DEFINE_STATIC_LOCAL(URLSchemesSet, secureContextBypassingSchemes, ());
+  DEFINE_STATIC_LOCAL_WITH_LOCK(URLSchemesSet, secureContextBypassingSchemes,
+                                ());
   return secureContextBypassingSchemes;
 }
 
 static URLSchemesSet& allowedInReferrerSchemes() {
-  DEFINE_STATIC_LOCAL(URLSchemesSet, allowedInReferrerSchemes, ());
+  DEFINE_STATIC_LOCAL_WITH_LOCK(URLSchemesSet, allowedInReferrerSchemes, ());
 
   if (allowedInReferrerSchemes.isEmpty()) {
     allowedInReferrerSchemes.add("http");
@@ -154,35 +173,22 @@
   return allowedInReferrerSchemes;
 }
 
-// All new maps should be added here. Must be called before we create other
-// threads to avoid racy static local initialization.
 void SchemeRegistry::initialize() {
-  localURLSchemes();
-  displayIsolatedURLSchemes();
-  secureSchemes();
-  schemesWithUniqueOrigins();
-  emptyDocumentSchemes();
-  schemesForbiddenFromDomainRelaxation();
-  notAllowingJavascriptURLsSchemes();
-  CORSEnabledSchemes();
-  serviceWorkerSchemes();
-  fetchAPISchemes();
-  firstPartyWhenTopLevelSchemes();
-  ContentSecurityPolicyBypassingSchemes();
-  secureContextBypassingSchemes();
-  allowedInReferrerSchemes();
+  // Instantiate the mutex object.
+  mutex();
 }
 
 bool SchemeRegistry::shouldTreatURLSchemeAsLocal(const String& scheme) {
   DCHECK_EQ(scheme, scheme.lower());
   if (scheme.isEmpty())
     return false;
+  MutexLocker locker(mutex());
   return localURLSchemes().contains(scheme);
 }
 
 void SchemeRegistry::registerURLSchemeAsNoAccess(const String& scheme) {
-  DCHECK(WTF::isBeforeThreadCreated());
   DCHECK_EQ(scheme, scheme.lower());
+  MutexLocker locker(mutex());
   schemesWithUniqueOrigins().add(scheme);
 }
 
@@ -190,12 +196,13 @@
   DCHECK_EQ(scheme, scheme.lower());
   if (scheme.isEmpty())
     return false;
+  MutexLocker locker(mutex());
   return schemesWithUniqueOrigins().contains(scheme);
 }
 
 void SchemeRegistry::registerURLSchemeAsDisplayIsolated(const String& scheme) {
-  DCHECK(WTF::isBeforeThreadCreated());
   DCHECK_EQ(scheme, scheme.lower());
+  MutexLocker locker(mutex());
   displayIsolatedURLSchemes().add(scheme);
 }
 
@@ -204,6 +211,7 @@
   DCHECK_EQ(scheme, scheme.lower());
   if (scheme.isEmpty())
     return false;
+  MutexLocker locker(mutex());
   return displayIsolatedURLSchemes().contains(scheme);
 }
 
@@ -214,8 +222,8 @@
 }
 
 void SchemeRegistry::registerURLSchemeAsSecure(const String& scheme) {
-  DCHECK(WTF::isBeforeThreadCreated());
   DCHECK_EQ(scheme, scheme.lower());
+  MutexLocker locker(mutex());
   secureSchemes().add(scheme);
 }
 
@@ -223,12 +231,13 @@
   DCHECK_EQ(scheme, scheme.lower());
   if (scheme.isEmpty())
     return false;
+  MutexLocker locker(mutex());
   return secureSchemes().contains(scheme);
 }
 
 void SchemeRegistry::registerURLSchemeAsEmptyDocument(const String& scheme) {
-  DCHECK(WTF::isBeforeThreadCreated());
   DCHECK_EQ(scheme, scheme.lower());
+  MutexLocker locker(mutex());
   emptyDocumentSchemes().add(scheme);
 }
 
@@ -236,17 +245,18 @@
   DCHECK_EQ(scheme, scheme.lower());
   if (scheme.isEmpty())
     return false;
+  MutexLocker locker(mutex());
   return emptyDocumentSchemes().contains(scheme);
 }
 
 void SchemeRegistry::setDomainRelaxationForbiddenForURLScheme(
     bool forbidden,
     const String& scheme) {
-  DCHECK(WTF::isBeforeThreadCreated());
   DCHECK_EQ(scheme, scheme.lower());
   if (scheme.isEmpty())
     return;
 
+  MutexLocker locker(mutex());
   if (forbidden)
     schemesForbiddenFromDomainRelaxation().add(scheme);
   else
@@ -258,6 +268,7 @@
   DCHECK_EQ(scheme, scheme.lower());
   if (scheme.isEmpty())
     return false;
+  MutexLocker locker(mutex());
   return schemesForbiddenFromDomainRelaxation().contains(scheme);
 }
 
@@ -268,8 +279,8 @@
 
 void SchemeRegistry::registerURLSchemeAsNotAllowingJavascriptURLs(
     const String& scheme) {
-  DCHECK(WTF::isBeforeThreadCreated());
   DCHECK_EQ(scheme, scheme.lower());
+  MutexLocker locker(mutex());
   notAllowingJavascriptURLsSchemes().add(scheme);
 }
 
@@ -278,12 +289,13 @@
   DCHECK_EQ(scheme, scheme.lower());
   if (scheme.isEmpty())
     return false;
+  MutexLocker locker(mutex());
   return notAllowingJavascriptURLsSchemes().contains(scheme);
 }
 
 void SchemeRegistry::registerURLSchemeAsCORSEnabled(const String& scheme) {
-  DCHECK(WTF::isBeforeThreadCreated());
   DCHECK_EQ(scheme, scheme.lower());
+  MutexLocker locker(mutex());
   CORSEnabledSchemes().add(scheme);
 }
 
@@ -291,13 +303,19 @@
   DCHECK_EQ(scheme, scheme.lower());
   if (scheme.isEmpty())
     return false;
+  MutexLocker locker(mutex());
   return CORSEnabledSchemes().contains(scheme);
 }
 
 String SchemeRegistry::listOfCORSEnabledURLSchemes() {
   StringBuilder builder;
   bool addSeparator = false;
-  for (const auto& scheme : CORSEnabledSchemes()) {
+  URLSchemesSet schemes;
+  {
+    MutexLocker locker(mutex());
+    schemes = CORSEnabledSchemes();
+  }
+  for (const auto& scheme : schemes) {
     if (addSeparator)
       builder.append(", ");
     else
@@ -314,8 +332,8 @@
 
 void SchemeRegistry::registerURLSchemeAsAllowingServiceWorkers(
     const String& scheme) {
-  DCHECK(WTF::isBeforeThreadCreated());
   DCHECK_EQ(scheme, scheme.lower());
+  MutexLocker locker(mutex());
   serviceWorkerSchemes().add(scheme);
 }
 
@@ -324,13 +342,14 @@
   DCHECK_EQ(scheme, scheme.lower());
   if (scheme.isEmpty())
     return false;
+  MutexLocker locker(mutex());
   return serviceWorkerSchemes().contains(scheme);
 }
 
 void SchemeRegistry::registerURLSchemeAsSupportingFetchAPI(
     const String& scheme) {
-  DCHECK(WTF::isBeforeThreadCreated());
   DCHECK_EQ(scheme, scheme.lower());
+  MutexLocker locker(mutex());
   fetchAPISchemes().add(scheme);
 }
 
@@ -339,20 +358,21 @@
   DCHECK_EQ(scheme, scheme.lower());
   if (scheme.isEmpty())
     return false;
+  MutexLocker locker(mutex());
   return fetchAPISchemes().contains(scheme);
 }
 
 void SchemeRegistry::registerURLSchemeAsFirstPartyWhenTopLevel(
     const String& scheme) {
-  DCHECK(WTF::isBeforeThreadCreated());
   DCHECK_EQ(scheme, scheme.lower());
+  MutexLocker locker(mutex());
   firstPartyWhenTopLevelSchemes().add(scheme);
 }
 
 void SchemeRegistry::removeURLSchemeAsFirstPartyWhenTopLevel(
     const String& scheme) {
-  DCHECK(WTF::isBeforeThreadCreated());
   DCHECK_EQ(scheme, scheme.lower());
+  MutexLocker locker(mutex());
   firstPartyWhenTopLevelSchemes().remove(scheme);
 }
 
@@ -361,18 +381,19 @@
   DCHECK_EQ(scheme, scheme.lower());
   if (scheme.isEmpty())
     return false;
+  MutexLocker locker(mutex());
   return firstPartyWhenTopLevelSchemes().contains(scheme);
 }
 
 void SchemeRegistry::registerURLSchemeAsAllowedForReferrer(
     const String& scheme) {
-  DCHECK(WTF::isBeforeThreadCreated());
   DCHECK_EQ(scheme, scheme.lower());
+  MutexLocker locker(mutex());
   allowedInReferrerSchemes().add(scheme);
 }
 
 void SchemeRegistry::removeURLSchemeAsAllowedForReferrer(const String& scheme) {
-  DCHECK(WTF::isBeforeThreadCreated());
+  MutexLocker locker(mutex());
   allowedInReferrerSchemes().remove(scheme);
 }
 
@@ -381,21 +402,22 @@
   DCHECK_EQ(scheme, scheme.lower());
   if (scheme.isEmpty())
     return false;
+  MutexLocker locker(mutex());
   return allowedInReferrerSchemes().contains(scheme);
 }
 
 void SchemeRegistry::registerURLSchemeAsBypassingContentSecurityPolicy(
     const String& scheme,
     PolicyAreas policyAreas) {
-  DCHECK(WTF::isBeforeThreadCreated());
   DCHECK_EQ(scheme, scheme.lower());
+  MutexLocker locker(mutex());
   ContentSecurityPolicyBypassingSchemes().add(scheme, policyAreas);
 }
 
 void SchemeRegistry::removeURLSchemeRegisteredAsBypassingContentSecurityPolicy(
     const String& scheme) {
-  DCHECK(WTF::isBeforeThreadCreated());
   DCHECK_EQ(scheme, scheme.lower());
+  MutexLocker locker(mutex());
   ContentSecurityPolicyBypassingSchemes().remove(scheme);
 }
 
@@ -408,14 +430,15 @@
 
   // get() returns 0 (PolicyAreaNone) if there is no entry in the map.
   // Thus by default, schemes do not bypass CSP.
+  MutexLocker locker(mutex());
   return (ContentSecurityPolicyBypassingSchemes().get(scheme) & policyAreas) ==
          policyAreas;
 }
 
 void SchemeRegistry::registerURLSchemeBypassingSecureContextCheck(
     const String& scheme) {
-  DCHECK(WTF::isBeforeThreadCreated());
   DCHECK_EQ(scheme, scheme.lower());
+  MutexLocker locker(mutex());
   secureContextBypassingSchemes().add(scheme.lower());
 }
 
@@ -423,6 +446,7 @@
     const String& scheme) {
   if (scheme.isEmpty())
     return false;
+  MutexLocker locker(mutex());
   return secureContextBypassingSchemes().contains(scheme.lower());
 }
 
diff --git a/third_party/WebKit/Source/web/BUILD.gn b/third_party/WebKit/Source/web/BUILD.gn
index cc44f39..c4b8460 100644
--- a/third_party/WebKit/Source/web/BUILD.gn
+++ b/third_party/WebKit/Source/web/BUILD.gn
@@ -178,6 +178,7 @@
     "WebFrameSerializer.cpp",
     "WebFrameSerializerImpl.cpp",
     "WebFrameSerializerImpl.h",
+    "WebFrameWidgetBase.cpp",
     "WebFrameWidgetBase.h",
     "WebFrameWidgetImpl.cpp",
     "WebFrameWidgetImpl.h",
diff --git a/third_party/WebKit/Source/web/ChromeClientImpl.cpp b/third_party/WebKit/Source/web/ChromeClientImpl.cpp
index e9644cb..d8a4928 100644
--- a/third_party/WebKit/Source/web/ChromeClientImpl.cpp
+++ b/third_party/WebKit/Source/web/ChromeClientImpl.cpp
@@ -631,18 +631,25 @@
     return;
 
   WebURL url;
-  // Find out if the mouse is over a link, and if so, let our UI know...
-  if (result.isLiveLink() && !result.absoluteLinkURL().getString().isEmpty()) {
-    url = result.absoluteLinkURL();
-  } else if (result.innerNode() && (isHTMLObjectElement(*result.innerNode()) ||
-                                    isHTMLEmbedElement(*result.innerNode()))) {
-    LayoutObject* object = result.innerNode()->layoutObject();
-    if (object && object->isLayoutPart()) {
-      Widget* widget = toLayoutPart(object)->widget();
-      if (widget && widget->isPluginContainer()) {
-        WebPluginContainerImpl* plugin = toWebPluginContainerImpl(widget);
-        url = plugin->plugin()->linkAtPosition(
-            result.roundedPointInInnerNodeFrame());
+
+  // Ignore URL if hitTest include scrollbar since we might have both a
+  // scrollbar and an element in the case of overlay scrollbars.
+  if (!result.scrollbar()) {
+    // Find out if the mouse is over a link, and if so, let our UI know...
+    if (result.isLiveLink() &&
+        !result.absoluteLinkURL().getString().isEmpty()) {
+      url = result.absoluteLinkURL();
+    } else if (result.innerNode() &&
+               (isHTMLObjectElement(*result.innerNode()) ||
+                isHTMLEmbedElement(*result.innerNode()))) {
+      LayoutObject* object = result.innerNode()->layoutObject();
+      if (object && object->isLayoutPart()) {
+        Widget* widget = toLayoutPart(object)->widget();
+        if (widget && widget->isPluginContainer()) {
+          WebPluginContainerImpl* plugin = toWebPluginContainerImpl(widget);
+          url = plugin->plugin()->linkAtPosition(
+              result.roundedPointInInnerNodeFrame());
+        }
       }
     }
   }
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetBase.cpp b/third_party/WebKit/Source/web/WebFrameWidgetBase.cpp
new file mode 100644
index 0000000..91a6560
--- /dev/null
+++ b/third_party/WebKit/Source/web/WebFrameWidgetBase.cpp
@@ -0,0 +1,136 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "web/WebFrameWidgetBase.h"
+
+#include "core/frame/FrameHost.h"
+#include "core/frame/VisualViewport.h"
+#include "core/page/DragActions.h"
+#include "core/page/DragController.h"
+#include "core/page/DragData.h"
+#include "core/page/DragSession.h"
+#include "core/page/Page.h"
+#include "public/web/WebAutofillClient.h"
+#include "public/web/WebDocument.h"
+#include "web/WebLocalFrameImpl.h"
+#include "web/WebViewImpl.h"
+
+namespace blink {
+
+WebDragOperation WebFrameWidgetBase::dragTargetDragEnter(
+    const WebDragData& webDragData,
+    const WebPoint& pointInViewport,
+    const WebPoint& screenPoint,
+    WebDragOperationsMask operationsAllowed,
+    int modifiers) {
+  DCHECK(!m_currentDragData);
+
+  m_currentDragData = DataObject::create(webDragData);
+  m_operationsAllowed = operationsAllowed;
+
+  return dragTargetDragEnterOrOver(pointInViewport, screenPoint, DragEnter,
+                                   modifiers);
+}
+
+WebDragOperation WebFrameWidgetBase::dragTargetDragOver(
+    const WebPoint& pointInViewport,
+    const WebPoint& screenPoint,
+    WebDragOperationsMask operationsAllowed,
+    int modifiers) {
+  m_operationsAllowed = operationsAllowed;
+
+  return dragTargetDragEnterOrOver(pointInViewport, screenPoint, DragOver,
+                                   modifiers);
+}
+
+void WebFrameWidgetBase::dragTargetDragLeave() {
+  DCHECK(m_currentDragData);
+
+  DragData dragData(m_currentDragData.get(), IntPoint(), IntPoint(),
+                    static_cast<DragOperation>(m_operationsAllowed));
+
+  page()->dragController().dragExited(&dragData);
+
+  // FIXME: why is the drag scroll timer not stopped here?
+
+  m_dragOperation = WebDragOperationNone;
+  m_currentDragData = nullptr;
+}
+
+void WebFrameWidgetBase::dragTargetDrop(const WebDragData& webDragData,
+                                        const WebPoint& pointInViewport,
+                                        const WebPoint& screenPoint,
+                                        int modifiers) {
+  WebPoint pointInRootFrame(viewportToRootFrame(pointInViewport));
+
+  DCHECK(m_currentDragData);
+  m_currentDragData = DataObject::create(webDragData);
+  WebViewImpl::UserGestureNotifier notifier(view());
+
+  // If this webview transitions from the "drop accepting" state to the "not
+  // accepting" state, then our IPC message reply indicating that may be in-
+  // flight, or else delayed by javascript processing in this webview.  If a
+  // drop happens before our IPC reply has reached the browser process, then
+  // the browser forwards the drop to this webview.  So only allow a drop to
+  // proceed if our webview m_dragOperation state is not DragOperationNone.
+
+  if (m_dragOperation == WebDragOperationNone) {
+    // IPC RACE CONDITION: do not allow this drop.
+    dragTargetDragLeave();
+    return;
+  }
+
+  m_currentDragData->setModifiers(modifiers);
+  DragData dragData(m_currentDragData.get(), pointInRootFrame, screenPoint,
+                    static_cast<DragOperation>(m_operationsAllowed));
+
+  page()->dragController().performDrag(&dragData);
+
+  m_dragOperation = WebDragOperationNone;
+  m_currentDragData = nullptr;
+}
+
+WebDragOperation WebFrameWidgetBase::dragTargetDragEnterOrOver(
+    const WebPoint& pointInViewport,
+    const WebPoint& screenPoint,
+    DragAction dragAction,
+    int modifiers) {
+  DCHECK(m_currentDragData);
+
+  WebPoint pointInRootFrame(viewportToRootFrame(pointInViewport));
+
+  m_currentDragData->setModifiers(modifiers);
+  DragData dragData(m_currentDragData.get(), pointInRootFrame, screenPoint,
+                    static_cast<DragOperation>(m_operationsAllowed));
+
+  DragSession dragSession;
+  dragSession = page()->dragController().dragEnteredOrUpdated(&dragData);
+
+  DragOperation dropEffect = dragSession.operation;
+
+  // Mask the drop effect operation against the drag source's allowed
+  // operations.
+  if (!(dropEffect & dragData.draggingSourceOperationMask()))
+    dropEffect = DragOperationNone;
+
+  m_dragOperation = static_cast<WebDragOperation>(dropEffect);
+
+  return m_dragOperation;
+}
+
+WebPoint WebFrameWidgetBase::viewportToRootFrame(
+    const WebPoint& pointInViewport) const {
+  return page()->frameHost().visualViewport().viewportToRootFrame(
+      pointInViewport);
+}
+
+WebViewImpl* WebFrameWidgetBase::view() const {
+  return static_cast<WebLocalFrameImpl*>(localRoot())->viewImpl();
+}
+
+Page* WebFrameWidgetBase::page() const {
+  return view()->page();
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetBase.h b/third_party/WebKit/Source/web/WebFrameWidgetBase.h
index 69698fe..9ac9110 100644
--- a/third_party/WebKit/Source/web/WebFrameWidgetBase.h
+++ b/third_party/WebKit/Source/web/WebFrameWidgetBase.h
@@ -5,6 +5,8 @@
 #ifndef WebFrameWidgetBase_h
 #define WebFrameWidgetBase_h
 
+#include "core/clipboard/DataObject.h"
+#include "public/platform/WebDragData.h"
 #include "public/web/WebFrameWidget.h"
 #include "wtf/Assertions.h"
 
@@ -12,8 +14,11 @@
 
 class CompositorAnimationTimeline;
 class CompositorProxyClient;
+class DragController;
 class GraphicsLayer;
 class WebLayer;
+class WebLocalFrameImpl;
+class WebViewImpl;
 class HitTestResult;
 struct WebPoint;
 
@@ -38,6 +43,55 @@
       CompositorAnimationTimeline*) = 0;
 
   virtual HitTestResult coreHitTestResultAt(const WebPoint&) = 0;
+
+  // WebFrameWidget implementation.
+  WebDragOperation dragTargetDragEnter(const WebDragData&,
+                                       const WebPoint& pointInViewport,
+                                       const WebPoint& screenPoint,
+                                       WebDragOperationsMask operationsAllowed,
+                                       int modifiers) override;
+  WebDragOperation dragTargetDragOver(const WebPoint& pointInViewport,
+                                      const WebPoint& screenPoint,
+                                      WebDragOperationsMask operationsAllowed,
+                                      int modifiers) override;
+  void dragTargetDragLeave() override;
+  void dragTargetDrop(const WebDragData&,
+                      const WebPoint& pointInViewport,
+                      const WebPoint& screenPoint,
+                      int modifiers) override;
+
+ protected:
+  enum DragAction { DragEnter, DragOver };
+
+  // Consolidate some common code between starting a drag over a target and
+  // updating a drag over a target. If we're starting a drag, |isEntering|
+  // should be true.
+  WebDragOperation dragTargetDragEnterOrOver(const WebPoint& pointInViewport,
+                                             const WebPoint& screenPoint,
+                                             DragAction,
+                                             int modifiers);
+
+  // Helper function to call VisualViewport::viewportToRootFrame().
+  WebPoint viewportToRootFrame(const WebPoint& pointInViewport) const;
+
+  WebViewImpl* view() const;
+
+  // Returns the page object associated with this widget. This may be null when
+  // the page is shutting down, but will be valid at all other times.
+  Page* page() const;
+
+  // A copy of the web drop data object we received from the browser.
+  Persistent<DataObject> m_currentDragData;
+
+  bool m_doingDragAndDrop = false;
+
+  // The available drag operations (copy, move link...) allowed by the source.
+  WebDragOperation m_operationsAllowed = WebDragOperationNone;
+
+  // The current drag operation as negotiated by the source and destination.
+  // When not equal to DragOperationNone, the drag data can be dropped onto the
+  // current drop target in this WebView (the drop target can accept the drop).
+  WebDragOperation m_dragOperation = WebDragOperationNone;
 };
 
 DEFINE_TYPE_CASTS(WebFrameWidgetBase, WebFrameWidget, widget, true, true);
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetImpl.h b/third_party/WebKit/Source/web/WebFrameWidgetImpl.h
index 1e132cb..5b50a371 100644
--- a/third_party/WebKit/Source/web/WebFrameWidgetImpl.h
+++ b/third_party/WebKit/Source/web/WebFrameWidgetImpl.h
@@ -118,7 +118,7 @@
   void applyReplacementRange(const WebRange&) override;
 
   // WebFrameWidget implementation.
-  WebLocalFrameImpl* localRoot() override { return m_localRoot; }
+  WebLocalFrameImpl* localRoot() const override { return m_localRoot; }
   void setVisibilityState(WebPageVisibilityState) override;
   bool isTransparent() const override;
   void setIsTransparent(bool) override;
@@ -151,10 +151,6 @@
 
   void setIgnoreInputEvents(bool newValue);
 
-  // Returns the page object associated with this widget. This may be null when
-  // the page is shutting down, but will be valid at all other times.
-  Page* page() const { return view()->page(); }
-
   // Event related methods:
   void mouseContextMenu(const WebMouseEvent&);
 
@@ -190,7 +186,6 @@
   WebInputEventResult handleKeyEvent(const WebKeyboardEvent&) override;
   WebInputEventResult handleCharEvent(const WebKeyboardEvent&) override;
 
-  WebViewImpl* view() const { return m_localRoot->viewImpl(); }
   InspectorOverlay* inspectorOverlay();
 
   // This method returns the focused frame belonging to this WebWidget, that
diff --git a/third_party/WebKit/Source/web/WebViewFrameWidget.cpp b/third_party/WebKit/Source/web/WebViewFrameWidget.cpp
index 91eeace2..1b24577 100644
--- a/third_party/WebKit/Source/web/WebViewFrameWidget.cpp
+++ b/third_party/WebKit/Source/web/WebViewFrameWidget.cpp
@@ -210,7 +210,7 @@
   m_webView->setBaseBackgroundColor(color);
 }
 
-WebLocalFrameImpl* WebViewFrameWidget::localRoot() {
+WebLocalFrameImpl* WebViewFrameWidget::localRoot() const {
   return m_webView->mainFrameImpl();
 }
 
diff --git a/third_party/WebKit/Source/web/WebViewFrameWidget.h b/third_party/WebKit/Source/web/WebViewFrameWidget.h
index fcf7e4f..a6b72542 100644
--- a/third_party/WebKit/Source/web/WebViewFrameWidget.h
+++ b/third_party/WebKit/Source/web/WebViewFrameWidget.h
@@ -92,7 +92,7 @@
   bool isTransparent() const override;
   void setIsTransparent(bool) override;
   void setBaseBackgroundColor(WebColor) override;
-  WebLocalFrameImpl* localRoot() override;
+  WebLocalFrameImpl* localRoot() const override;
   WebInputMethodControllerImpl* getActiveWebInputMethodController()
       const override;
 
diff --git a/third_party/WebKit/Source/web/WebViewImpl.cpp b/third_party/WebKit/Source/web/WebViewImpl.cpp
index c562cb52..0f858659 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.cpp
+++ b/third_party/WebKit/Source/web/WebViewImpl.cpp
@@ -241,6 +241,8 @@
 
 // Ensure that the WebDragOperation enum values stay in sync with the original
 // DragOperation constants.
+// TODO(paulmeyer): Move this into WebFrameWidgetBase once all drag-and-drop
+// functions are out of WebViewImpl. See crbug.com/647249.
 #define STATIC_ASSERT_ENUM(a, b)                            \
   static_assert(static_cast<int>(a) == static_cast<int>(b), \
                 "mismatching enum : " #a)
@@ -257,34 +259,6 @@
 
 namespace {
 
-class UserGestureNotifier {
- public:
-  // If a UserGestureIndicator is created for a user gesture since the last
-  // page load and *userGestureObserved is false, the UserGestureNotifier
-  // will notify the client and set *userGestureObserved to true.
-  UserGestureNotifier(WebLocalFrameImpl*, bool* userGestureObserved);
-  ~UserGestureNotifier();
-
- private:
-  Persistent<WebLocalFrameImpl> m_frame;
-  bool* const m_userGestureObserved;
-};
-
-UserGestureNotifier::UserGestureNotifier(WebLocalFrameImpl* frame,
-                                         bool* userGestureObserved)
-    : m_frame(frame), m_userGestureObserved(userGestureObserved) {
-  DCHECK(m_userGestureObserved);
-}
-
-UserGestureNotifier::~UserGestureNotifier() {
-  if (!*m_userGestureObserved &&
-      m_frame->frame()->document()->hasReceivedUserGesture()) {
-    *m_userGestureObserved = true;
-    if (m_frame && m_frame->autofillClient())
-      m_frame->autofillClient()->firstUserGestureObserved();
-  }
-}
-
 class EmptyEventListener final : public EventListener {
  public:
   static EmptyEventListener* create() { return new EmptyEventListener(); }
@@ -410,8 +384,6 @@
       m_compositorDeviceScaleFactorOverride(0),
       m_suppressNextKeypressEvent(false),
       m_imeAcceptEvents(true),
-      m_operationsAllowed(WebDragOperationNone),
-      m_dragOperation(WebDragOperationNone),
       m_devToolsEmulator(nullptr),
       m_isTransparent(false),
       m_tabsToLinks(false),
@@ -483,6 +455,21 @@
   DCHECK(m_linkHighlights.isEmpty());
 }
 
+WebViewImpl::UserGestureNotifier::UserGestureNotifier(WebViewImpl* view)
+    : m_frame(view->mainFrameImpl()),
+      m_userGestureObserved(&view->m_userGestureObserved) {
+  DCHECK(m_userGestureObserved);
+}
+
+WebViewImpl::UserGestureNotifier::~UserGestureNotifier() {
+  if (!*m_userGestureObserved &&
+      m_frame->frame()->document()->hasReceivedUserGesture()) {
+    *m_userGestureObserved = true;
+    if (m_frame && m_frame->autofillClient())
+      m_frame->autofillClient()->firstUserGestureObserved();
+  }
+}
+
 WebDevToolsAgentImpl* WebViewImpl::mainFrameDevToolsAgentImpl() {
   WebLocalFrameImpl* mainFrame = mainFrameImpl();
   return mainFrame ? mainFrame->devToolsAgentImpl() : nullptr;
@@ -2138,7 +2125,7 @@
     return WebInputEventResult::NotHandled;
 
   WebAutofillClient* autofillClient = mainFrameImpl()->autofillClient();
-  UserGestureNotifier notifier(mainFrameImpl(), &m_userGestureObserved);
+  UserGestureNotifier notifier(this);
   // On the first input event since page load, |notifier| instructs the
   // autofill client to unblock values of password input fields of any forms
   // on the page. There is a single input event, GestureTap, which can both
@@ -3437,81 +3424,6 @@
   }
 }
 
-WebDragOperation WebViewImpl::dragTargetDragEnter(
-    const WebDragData& webDragData,
-    const WebPoint& pointInViewport,
-    const WebPoint& screenPoint,
-    WebDragOperationsMask operationsAllowed,
-    int modifiers) {
-  DCHECK(!m_currentDragData);
-
-  m_currentDragData = DataObject::create(webDragData);
-  m_operationsAllowed = operationsAllowed;
-
-  return dragTargetDragEnterOrOver(pointInViewport, screenPoint, DragEnter,
-                                   modifiers);
-}
-
-WebDragOperation WebViewImpl::dragTargetDragOver(
-    const WebPoint& pointInViewport,
-    const WebPoint& screenPoint,
-    WebDragOperationsMask operationsAllowed,
-    int modifiers) {
-  m_operationsAllowed = operationsAllowed;
-
-  return dragTargetDragEnterOrOver(pointInViewport, screenPoint, DragOver,
-                                   modifiers);
-}
-
-void WebViewImpl::dragTargetDragLeave() {
-  DCHECK(m_currentDragData);
-
-  DragData dragData(m_currentDragData.get(), IntPoint(), IntPoint(),
-                    static_cast<DragOperation>(m_operationsAllowed));
-
-  m_page->dragController().dragExited(&dragData);
-
-  // FIXME: why is the drag scroll timer not stopped here?
-
-  m_dragOperation = WebDragOperationNone;
-  m_currentDragData = nullptr;
-}
-
-void WebViewImpl::dragTargetDrop(const WebDragData& webDragData,
-                                 const WebPoint& pointInViewport,
-                                 const WebPoint& screenPoint,
-                                 int modifiers) {
-  WebPoint pointInRootFrame(
-      page()->frameHost().visualViewport().viewportToRootFrame(
-          pointInViewport));
-
-  DCHECK(m_currentDragData);
-  m_currentDragData = DataObject::create(webDragData);
-  UserGestureNotifier notifier(mainFrameImpl(), &m_userGestureObserved);
-
-  // If this webview transitions from the "drop accepting" state to the "not
-  // accepting" state, then our IPC message reply indicating that may be in-
-  // flight, or else delayed by javascript processing in this webview.  If a
-  // drop happens before our IPC reply has reached the browser process, then
-  // the browser forwards the drop to this webview.  So only allow a drop to
-  // proceed if our webview m_dragOperation state is not DragOperationNone.
-
-  if (m_dragOperation ==
-      WebDragOperationNone) {  // IPC RACE CONDITION: do not allow this drop.
-    dragTargetDragLeave();
-    return;
-  }
-
-  m_currentDragData->setModifiers(modifiers);
-  DragData dragData(m_currentDragData.get(), pointInRootFrame, screenPoint,
-                    static_cast<DragOperation>(m_operationsAllowed));
-
-  m_page->dragController().performDrag(&dragData);
-
-  m_dragOperation = WebDragOperationNone;
-  m_currentDragData = nullptr;
-}
-
 void WebViewImpl::spellingMarkers(WebVector<uint32_t>* markers) {
   Vector<uint32_t> result;
   for (Frame* frame = m_page->mainFrame(); frame;
@@ -3538,36 +3450,6 @@
   }
 }
 
-WebDragOperation WebViewImpl::dragTargetDragEnterOrOver(
-    const WebPoint& pointInViewport,
-    const WebPoint& screenPoint,
-    DragAction dragAction,
-    int modifiers) {
-  DCHECK(m_currentDragData);
-
-  WebPoint pointInRootFrame(
-      page()->frameHost().visualViewport().viewportToRootFrame(
-          pointInViewport));
-
-  m_currentDragData->setModifiers(modifiers);
-  DragData dragData(m_currentDragData.get(), pointInRootFrame, screenPoint,
-                    static_cast<DragOperation>(m_operationsAllowed));
-
-  DragSession dragSession;
-  dragSession = m_page->dragController().dragEnteredOrUpdated(&dragData);
-
-  DragOperation dropEffect = dragSession.operation;
-
-  // Mask the drop effect operation against the drag source's allowed
-  // operations.
-  if (!(dropEffect & dragData.draggingSourceOperationMask()))
-    dropEffect = DragOperationNone;
-
-  m_dragOperation = static_cast<WebDragOperation>(dropEffect);
-
-  return m_dragOperation;
-}
-
 void WebViewImpl::sendResizeEventAndRepaint() {
   // FIXME: This is wrong. The FrameView is responsible sending a resizeEvent
   // as part of layout. Layout is also responsible for sending invalidations
diff --git a/third_party/WebKit/Source/web/WebViewImpl.h b/third_party/WebKit/Source/web/WebViewImpl.h
index d6c0488..9a90f914 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.h
+++ b/third_party/WebKit/Source/web/WebViewImpl.h
@@ -107,6 +107,20 @@
   static WebViewImpl* create(WebViewClient*, WebPageVisibilityState);
   static HashSet<WebViewImpl*>& allInstances();
 
+  class UserGestureNotifier {
+   public:
+    // If a UserGestureIndicator is created for a user gesture since the last
+    // page load and the WebViewImpl's |m_userGestureObserved| is false, the
+    // UserGestureNotifier will notify the client and set
+    // |m_userGestureObserved| to true.
+    UserGestureNotifier(WebViewImpl*);
+    ~UserGestureNotifier();
+
+   private:
+    Persistent<WebLocalFrameImpl> m_frame;
+    bool* const m_userGestureObserved;
+  };
+
   // WebWidget methods:
   void close() override;
   WebSize size() override;
@@ -229,20 +243,6 @@
                          const WebPoint& screenPoint,
                          WebDragOperation) override;
   void dragSourceSystemDragEnded() override;
-  WebDragOperation dragTargetDragEnter(const WebDragData&,
-                                       const WebPoint& pointInViewport,
-                                       const WebPoint& screenPoint,
-                                       WebDragOperationsMask operationsAllowed,
-                                       int modifiers) override;
-  WebDragOperation dragTargetDragOver(const WebPoint& pointInViewport,
-                                      const WebPoint& screenPoint,
-                                      WebDragOperationsMask operationsAllowed,
-                                      int modifiers) override;
-  void dragTargetDragLeave() override;
-  void dragTargetDrop(const WebDragData&,
-                      const WebPoint& pointInViewport,
-                      const WebPoint& screenPoint,
-                      int modifiers) override;
   void spellingMarkers(WebVector<uint32_t>* markers) override;
   void removeSpellingMarkersUnderWords(
       const WebVector<WebString>& words) override;
@@ -545,8 +545,6 @@
   friend class WTF::RefCounted<WebViewImpl>;
   friend void setCurrentInputEventForTest(const WebInputEvent*);
 
-  enum DragAction { DragEnter, DragOver };
-
   explicit WebViewImpl(WebViewClient*, WebPageVisibilityState);
   ~WebViewImpl() override;
 
@@ -555,14 +553,6 @@
   HitTestResult hitTestResultForRootFramePos(const IntPoint&);
   HitTestResult hitTestResultForViewportPos(const IntPoint&);
 
-  // Consolidate some common code between starting a drag over a target and
-  // updating a drag over a target. If we're starting a drag, |isEntering|
-  // should be true.
-  WebDragOperation dragTargetDragEnterOrOver(const WebPoint& pointInViewport,
-                                             const WebPoint& screenPoint,
-                                             DragAction,
-                                             int modifiers);
-
   void configureAutoResizeMode();
 
   void initializeLayerTreeView();
@@ -641,9 +631,6 @@
   // is called.
   std::unique_ptr<WebSettingsImpl> m_webSettings;
 
-  // A copy of the web drop data object we received from the browser.
-  Persistent<DataObject> m_currentDragData;
-
   // Keeps track of the current zoom level. 0 means no zoom, positive numbers
   // mean zoom in, negative numbers mean zoom out.
   double m_zoomLevel;
@@ -688,14 +675,6 @@
   // Represents whether or not this object should process incoming IME events.
   bool m_imeAcceptEvents;
 
-  // The available drag operations (copy, move link...) allowed by the source.
-  WebDragOperation m_operationsAllowed;
-
-  // The current drag operation as negotiated by the source and destination.
-  // When not equal to DragOperationNone, the drag data can be dropped onto the
-  // current drop target in this WebView (the drop target can accept the drop).
-  WebDragOperation m_dragOperation;
-
   // The popup associated with an input/select element.
   RefPtr<WebPagePopupImpl> m_pagePopup;
 
diff --git a/third_party/WebKit/Source/web/tests/NGInlineLayoutTest.cpp b/third_party/WebKit/Source/web/tests/NGInlineLayoutTest.cpp
index e3af1956..623ba56 100644
--- a/third_party/WebKit/Source/web/tests/NGInlineLayoutTest.cpp
+++ b/third_party/WebKit/Source/web/tests/NGInlineLayoutTest.cpp
@@ -58,7 +58,7 @@
   EXPECT_EQ(expectedText, inlineBox->Text(0, 12));
 
   NGPhysicalFragmentBase* fragment;
-  layoutAlgorithm->Layout(&fragment);
+  layoutAlgorithm->Layout(nullptr, &fragment, nullptr);
 }
 
 TEST_F(NGInlineLayoutTest, BlockWithTextAndAtomicInline) {
@@ -87,7 +87,7 @@
   EXPECT_EQ(expectedText, inlineBox->Text(0, 8));
 
   NGPhysicalFragmentBase* fragment;
-  layoutAlgorithm->Layout(&fragment);
+  layoutAlgorithm->Layout(nullptr, &fragment, nullptr);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/web/tests/VisualViewportTest.cpp b/third_party/WebKit/Source/web/tests/VisualViewportTest.cpp
index 418e648..782a821 100644
--- a/third_party/WebKit/Source/web/tests/VisualViewportTest.cpp
+++ b/third_party/WebKit/Source/web/tests/VisualViewportTest.cpp
@@ -108,15 +108,9 @@
 
 namespace {
 
-typedef bool TestParamRootLayerScrolling;
-class VisualViewportTest
-    : public testing::Test,
-      public testing::WithParamInterface<TestParamRootLayerScrolling>,
-      private ScopedRootLayerScrollingForTest {
+class VisualViewportTest : public testing::Test {
  public:
-  VisualViewportTest()
-      : ScopedRootLayerScrollingForTest(GetParam()),
-        m_baseURL("http://www.test.com/") {}
+  VisualViewportTest() : m_baseURL("http://www.test.com/") {}
 
   void initializeWithDesktopSettings(
       void (*overrideSettingsFunc)(WebSettings*) = 0) {
@@ -191,11 +185,23 @@
   FrameTestHelpers::WebViewHelper m_helper;
 };
 
-INSTANTIATE_TEST_CASE_P(All, VisualViewportTest, ::testing::Bool());
+typedef bool TestParamRootLayerScrolling;
+class ParameterizedVisualViewportTest
+    : public testing::WithParamInterface<TestParamRootLayerScrolling>,
+      private ScopedRootLayerScrollingForTest,
+      public VisualViewportTest {
+ public:
+  ParameterizedVisualViewportTest()
+      : ScopedRootLayerScrollingForTest(GetParam()) {}
+};
+
+INSTANTIATE_TEST_CASE_P(All,
+                        ParameterizedVisualViewportTest,
+                        ::testing::Bool());
 
 // Test that resizing the VisualViewport works as expected and that resizing the
 // WebView resizes the VisualViewport.
-TEST_P(VisualViewportTest, TestResize) {
+TEST_P(ParameterizedVisualViewportTest, TestResize) {
   initializeWithDesktopSettings();
   webViewImpl()->resize(IntSize(320, 240));
 
@@ -225,7 +231,7 @@
 
 // Make sure that the visibleContentRect method acurately reflects the scale and
 // scroll location of the viewport with and without scrollbars.
-TEST_P(VisualViewportTest, TestVisibleContentRect) {
+TEST_P(ParameterizedVisualViewportTest, TestVisibleContentRect) {
   RuntimeEnabledFeatures::setOverlayScrollbarsEnabled(false);
   initializeWithDesktopSettings();
 
@@ -265,7 +271,8 @@
 // unchanged from the user's perspective (shrinking the FrameView will clamp
 // the VisualViewport so we need to counter scroll the FrameView to make it
 // appear to stay still). This caused bugs like crbug.com/453859.
-TEST_P(VisualViewportTest, TestResizeAtFullyScrolledPreservesViewportLocation) {
+TEST_P(ParameterizedVisualViewportTest,
+       TestResizeAtFullyScrolledPreservesViewportLocation) {
   initializeWithDesktopSettings();
   webViewImpl()->resize(IntSize(800, 600));
 
@@ -307,9 +314,45 @@
       frameView.getScrollableArea()->visibleContentRect().location());
 }
 
+// Test the main frame scrollbars take visual viewport into account.
+TEST_F(VisualViewportTest, VerifyScrollbarBehavior) {
+  initializeWithDesktopSettings();
+
+  registerMockedHttpURLLoad("200-by-800-viewport.html");
+  navigateTo(m_baseURL + "200-by-800-viewport.html");
+
+  webViewImpl()->resize(IntSize(300, 200));
+
+  ScrollableArea* scroller = frame()->view()->getScrollableArea();
+
+  // Initially, there is no horizontal scrollbar since the content fits.
+  EXPECT_FALSE(scroller->horizontalScrollbar());
+  EXPECT_TRUE(scroller->verticalScrollbar());
+
+  // Scroll layout viewport.
+  webViewImpl()->mainFrame()->setScrollOffset(WebSize(0, 200));
+  EXPECT_SIZE_EQ(ScrollOffset(0, 200), scroller->scrollOffset());
+
+  webViewImpl()->setPageScaleFactor(2.0);
+
+  // Pinch-zooming should add horizontal scrollbar.
+  EXPECT_TRUE(scroller->horizontalScrollbar());
+  EXPECT_TRUE(scroller->verticalScrollbar());
+
+  // Scroll visual viewport.
+  VisualViewport& visualViewport =
+      frame()->page()->frameHost().visualViewport();
+  visualViewport.setLocation(FloatPoint(100, 100));
+  EXPECT_FLOAT_SIZE_EQ(FloatSize(100, 100), visualViewport.scrollOffset());
+
+  // Scrollbar offset should take visual viewport into account.
+  EXPECT_FLOAT_EQ(100, scroller->horizontalScrollbar()->currentPos());
+  EXPECT_FLOAT_EQ(300, scroller->verticalScrollbar()->currentPos());
+}
+
 // Test that the VisualViewport works as expected in case of a scaled
 // and scrolled viewport - scroll down.
-TEST_P(VisualViewportTest, TestResizeAfterVerticalScroll) {
+TEST_P(ParameterizedVisualViewportTest, TestResizeAfterVerticalScroll) {
 /*
                  200                                 200
         |                   |               |                   |
@@ -379,7 +422,7 @@
 
 // Test that the VisualViewport works as expected in case if a scaled
 // and scrolled viewport - scroll right.
-TEST_P(VisualViewportTest, TestResizeAfterHorizontalScroll) {
+TEST_P(ParameterizedVisualViewportTest, TestResizeAfterHorizontalScroll) {
 /*
                  200                                 200
         ---------------o-----               ---------------o-----
@@ -444,7 +487,7 @@
 
 // Test that the container layer gets sized properly if the WebView is resized
 // prior to the VisualViewport being attached to the layer tree.
-TEST_P(VisualViewportTest, TestWebViewResizedBeforeAttachment) {
+TEST_P(ParameterizedVisualViewportTest, TestWebViewResizedBeforeAttachment) {
   initializeWithDesktopSettings();
   FrameView& frameView = *webViewImpl()->mainFrameImpl()->frameView();
   GraphicsLayer* rootGraphicsLayer =
@@ -469,7 +512,7 @@
 
 // Make sure that the visibleRect method acurately reflects the scale and scroll
 // location of the viewport.
-TEST_P(VisualViewportTest, TestVisibleRect) {
+TEST_P(ParameterizedVisualViewportTest, TestVisibleRect) {
   initializeWithDesktopSettings();
   webViewImpl()->resize(IntSize(320, 240));
 
@@ -520,7 +563,7 @@
 
 // Make sure that the visibleRectInDocument method acurately reflects the scale
 // and scroll location of the viewport relative to the document.
-TEST_P(VisualViewportTest, TestVisibleRectInDocument) {
+TEST_P(ParameterizedVisualViewportTest, TestVisibleRectInDocument) {
   initializeWithDesktopSettings();
   webViewImpl()->resize(IntSize(100, 400));
 
@@ -545,7 +588,8 @@
                        visualViewport.visibleRectInDocument());
 }
 
-TEST_P(VisualViewportTest, TestFractionalScrollOffsetIsNotOverwritten) {
+TEST_P(ParameterizedVisualViewportTest,
+       TestFractionalScrollOffsetIsNotOverwritten) {
   bool origFractionalOffsetsEnabled =
       RuntimeEnabledFeatures::fractionalScrollOffsetsEnabled();
   RuntimeEnabledFeatures::setFractionalScrollOffsetsEnabled(true);
@@ -571,7 +615,7 @@
 
 // Test that the viewport's scroll offset is always appropriately bounded such
 // that the visual viewport always stays within the bounds of the main frame.
-TEST_P(VisualViewportTest, TestOffsetClamping) {
+TEST_P(ParameterizedVisualViewportTest, TestOffsetClamping) {
   initializeWithDesktopSettings();
   webViewImpl()->resize(IntSize(320, 240));
 
@@ -627,7 +671,7 @@
 // Test that the viewport can be scrolled around only within the main frame in
 // the presence of viewport resizes, as would be the case if the on screen
 // keyboard came up.
-TEST_P(VisualViewportTest, TestOffsetClampingWithResize) {
+TEST_P(ParameterizedVisualViewportTest, TestOffsetClampingWithResize) {
   initializeWithDesktopSettings();
   webViewImpl()->resize(IntSize(320, 240));
 
@@ -696,7 +740,7 @@
 
 // Test that the viewport is scrollable but bounded appropriately within the
 // main frame when we apply both scaling and resizes.
-TEST_P(VisualViewportTest, TestOffsetClampingWithResizeAndScale) {
+TEST_P(ParameterizedVisualViewportTest, TestOffsetClampingWithResizeAndScale) {
   initializeWithDesktopSettings();
   webViewImpl()->resize(IntSize(320, 240));
 
@@ -751,7 +795,7 @@
 // viewport at minimum scale. If there's no explicit minimum scale set, the
 // FrameView should be set to the content width and height derived by the aspect
 // ratio.
-TEST_P(VisualViewportTest, TestFrameViewSizedToContent) {
+TEST_P(ParameterizedVisualViewportTest, TestFrameViewSizedToContent) {
   initializeWithAndroidSettings();
   webViewImpl()->resize(IntSize(320, 240));
 
@@ -771,7 +815,7 @@
 // The main FrameView's size should be set such that its the size of the visual
 // viewport at minimum scale. On Desktop, the minimum scale is set at 1 so make
 // sure the FrameView is sized to the viewport.
-TEST_P(VisualViewportTest, TestFrameViewSizedToMinimumScale) {
+TEST_P(ParameterizedVisualViewportTest, TestFrameViewSizedToMinimumScale) {
   initializeWithDesktopSettings();
   webViewImpl()->resize(IntSize(320, 240));
 
@@ -788,7 +832,8 @@
 
 // Test that attaching a new frame view resets the size of the inner viewport
 // scroll layer. crbug.com/423189.
-TEST_P(VisualViewportTest, TestAttachingNewFrameSetsInnerScrollLayerSize) {
+TEST_P(ParameterizedVisualViewportTest,
+       TestAttachingNewFrameSetsInnerScrollLayerSize) {
   initializeWithAndroidSettings();
   webViewImpl()->resize(IntSize(320, 240));
 
@@ -825,7 +870,8 @@
 // The main FrameView's size should be set such that its the size of the visual
 // viewport at minimum scale. Test that the FrameView is appropriately sized in
 // the presence of a viewport <meta> tag.
-TEST_P(VisualViewportTest, TestFrameViewSizedToViewportMetaMinimumScale) {
+TEST_P(ParameterizedVisualViewportTest,
+       TestFrameViewSizedToViewportMetaMinimumScale) {
   initializeWithAndroidSettings();
   webViewImpl()->resize(IntSize(320, 240));
 
@@ -841,7 +887,8 @@
 }
 
 // Test that the visual viewport still gets sized in AutoSize/AutoResize mode.
-TEST_P(VisualViewportTest, TestVisualViewportGetsSizeInAutoSizeMode) {
+TEST_P(ParameterizedVisualViewportTest,
+       TestVisualViewportGetsSizeInAutoSizeMode) {
   initializeWithDesktopSettings();
 
   EXPECT_SIZE_EQ(IntSize(0, 0), IntSize(webViewImpl()->size()));
@@ -859,7 +906,7 @@
 
 // Test that the text selection handle's position accounts for the visual
 // viewport.
-TEST_P(VisualViewportTest, TestTextSelectionHandles) {
+TEST_P(ParameterizedVisualViewportTest, TestTextSelectionHandles) {
   initializeWithDesktopSettings();
   webViewImpl()->resize(IntSize(500, 800));
 
@@ -894,7 +941,7 @@
 
 // Test that the HistoryItem for the page stores the visual viewport's offset
 // and scale.
-TEST_P(VisualViewportTest, TestSavedToHistoryItem) {
+TEST_P(ParameterizedVisualViewportTest, TestSavedToHistoryItem) {
   initializeWithDesktopSettings();
   webViewImpl()->resize(IntSize(200, 300));
   webViewImpl()->updateAllLifecyclePhases();
@@ -927,7 +974,7 @@
 }
 
 // Test restoring a HistoryItem properly restores the visual viewport's state.
-TEST_P(VisualViewportTest, TestRestoredFromHistoryItem) {
+TEST_P(ParameterizedVisualViewportTest, TestRestoredFromHistoryItem) {
   initializeWithDesktopSettings();
   webViewImpl()->resize(IntSize(200, 300));
 
@@ -955,7 +1002,7 @@
 // Test restoring a HistoryItem without the visual viewport offset falls back to
 // distributing the scroll offset between the main frame and the visual
 // viewport.
-TEST_P(VisualViewportTest, TestRestoredFromLegacyHistoryItem) {
+TEST_P(ParameterizedVisualViewportTest, TestRestoredFromLegacyHistoryItem) {
   initializeWithDesktopSettings();
   webViewImpl()->resize(IntSize(100, 150));
 
@@ -988,7 +1035,7 @@
 
 // Test that navigation to a new page with a different sized main frame doesn't
 // clobber the history item's main frame scroll offset. crbug.com/371867
-TEST_P(VisualViewportTest,
+TEST_P(ParameterizedVisualViewportTest,
        TestNavigateToSmallerFrameViewHistoryItemClobberBug) {
   initializeWithAndroidSettings();
   webViewImpl()->resize(IntSize(400, 400));
@@ -1026,7 +1073,7 @@
 
 // Test that the coordinates sent into moveRangeSelection are offset by the
 // visual viewport's location.
-TEST_P(VisualViewportTest,
+TEST_P(ParameterizedVisualViewportTest,
        DISABLED_TestWebFrameRangeAccountsForVisualViewportScroll) {
   initializeWithDesktopSettings();
   webViewImpl()->settings()->setDefaultFontSize(12);
@@ -1062,7 +1109,8 @@
 
 // Test that the scrollFocusedEditableElementIntoRect method works with the
 // visual viewport.
-TEST_P(VisualViewportTest, DISABLED_TestScrollFocusedEditableElementIntoRect) {
+TEST_P(ParameterizedVisualViewportTest,
+       DISABLED_TestScrollFocusedEditableElementIntoRect) {
   initializeWithDesktopSettings();
   webViewImpl()->resize(IntSize(500, 300));
 
@@ -1115,7 +1163,8 @@
 
 // Test that resizing the WebView causes ViewportConstrained objects to
 // relayout.
-TEST_P(VisualViewportTest, TestWebViewResizeCausesViewportConstrainedLayout) {
+TEST_P(ParameterizedVisualViewportTest,
+       TestWebViewResizeCausesViewportConstrainedLayout) {
   initializeWithDesktopSettings();
   webViewImpl()->resize(IntSize(500, 300));
 
@@ -1151,7 +1200,7 @@
 
 // Test that the context menu's location is correct in the presence of visual
 // viewport offset.
-TEST_P(VisualViewportTest, TestContextMenuShownInCorrectLocation) {
+TEST_P(ParameterizedVisualViewportTest, TestContextMenuShownInCorrectLocation) {
   initializeWithDesktopSettings();
   webViewImpl()->resize(IntSize(200, 300));
 
@@ -1206,7 +1255,7 @@
 }
 
 // Test that the client is notified if page scroll events.
-TEST_P(VisualViewportTest, TestClientNotifiedOfScrollEvents) {
+TEST_P(ParameterizedVisualViewportTest, TestClientNotifiedOfScrollEvents) {
   initializeWithAndroidSettings();
   webViewImpl()->resize(IntSize(200, 300));
 
@@ -1240,7 +1289,7 @@
 
 // Tests that calling scroll into view on a visible element doesn't cause
 // a scroll due to a fractional offset. Bug crbug.com/463356.
-TEST_P(VisualViewportTest, ScrollIntoViewFractionalOffset) {
+TEST_P(ParameterizedVisualViewportTest, ScrollIntoViewFractionalOffset) {
   initializeWithAndroidSettings();
 
   webViewImpl()->resize(IntSize(1000, 1000));
@@ -1318,7 +1367,7 @@
       frameView.contentsSize().height() - newHeight);
 }
 
-TEST_P(VisualViewportTest, TestBrowserControlsAdjustment) {
+TEST_P(ParameterizedVisualViewportTest, TestBrowserControlsAdjustment) {
   initializeWithAndroidSettings();
   webViewImpl()->resizeWithBrowserControls(IntSize(500, 450), 20, false);
 
@@ -1369,7 +1418,8 @@
                  frameView.layoutViewportScrollableArea()->scrollOffset());
 }
 
-TEST_P(VisualViewportTest, TestBrowserControlsAdjustmentWithScale) {
+TEST_P(ParameterizedVisualViewportTest,
+       TestBrowserControlsAdjustmentWithScale) {
   initializeWithAndroidSettings();
   webViewImpl()->resizeWithBrowserControls(IntSize(500, 450), 20, false);
 
@@ -1443,7 +1493,8 @@
 // Tests that a scroll all the way to the bottom of the page, while hiding the
 // browser controls doesn't cause a clamp in the viewport scroll offset when the
 // top controls initiated resize occurs.
-TEST_P(VisualViewportTest, TestBrowserControlsAdjustmentAndResize) {
+TEST_P(ParameterizedVisualViewportTest,
+       TestBrowserControlsAdjustmentAndResize) {
   int browserControlsHeight = 20;
   int visualViewportHeight = 450;
   int layoutViewportHeight = 900;
@@ -1514,7 +1565,8 @@
 // Tests that a scroll all the way to the bottom while showing the browser
 // controls doesn't cause a clamp to the viewport scroll offset when the browser
 // controls initiated resize occurs.
-TEST_P(VisualViewportTest, TestBrowserControlsShrinkAdjustmentAndResize) {
+TEST_P(ParameterizedVisualViewportTest,
+       TestBrowserControlsShrinkAdjustmentAndResize) {
   int browserControlsHeight = 20;
   int visualViewportHeight = 500;
   int layoutViewportHeight = 1000;
@@ -1589,7 +1641,8 @@
 
 // Tests that a resize due to browser controls hiding doesn't incorrectly clamp
 // the main frame's scroll offset. crbug.com/428193.
-TEST_P(VisualViewportTest, TestTopControlHidingResizeDoesntClampMainFrame) {
+TEST_P(ParameterizedVisualViewportTest,
+       TestTopControlHidingResizeDoesntClampMainFrame) {
   initializeWithAndroidSettings();
   webViewImpl()->resizeWithBrowserControls(webViewImpl()->size(), 500, false);
   webViewImpl()->applyViewportDeltas(WebFloatSize(), WebFloatSize(),
@@ -1623,7 +1676,7 @@
 
 // Tests that scrollbar layers are not attached to the inner viewport container
 // layer when hideScrollbars WebSetting is true.
-TEST_P(VisualViewportTest,
+TEST_P(ParameterizedVisualViewportTest,
        TestScrollbarsNotAttachedWhenHideScrollbarsSettingIsTrue) {
   initializeWithAndroidSettings(configureHiddenScrollbarsSettings);
   webViewImpl()->resize(IntSize(100, 150));
@@ -1637,7 +1690,7 @@
 
 // Tests that scrollbar layers are attached to the inner viewport container
 // layer when hideScrollbars WebSetting is false.
-TEST_P(VisualViewportTest,
+TEST_P(ParameterizedVisualViewportTest,
        TestScrollbarsAttachedWhenHideScrollbarsSettingIsFalse) {
   initializeWithAndroidSettings();
   webViewImpl()->resize(IntSize(100, 150));
@@ -1651,7 +1704,8 @@
 
 // Tests that the layout viewport's scroll layer bounds are updated in a
 // compositing change update. crbug.com/423188.
-TEST_P(VisualViewportTest, TestChangingContentSizeAffectsScrollBounds) {
+TEST_P(ParameterizedVisualViewportTest,
+       TestChangingContentSizeAffectsScrollBounds) {
   initializeWithAndroidSettings();
   webViewImpl()->resize(IntSize(100, 150));
 
@@ -1672,7 +1726,8 @@
 
 // Tests that resizing the visual viepwort keeps its bounds within the outer
 // viewport.
-TEST_P(VisualViewportTest, ResizeVisualViewportStaysWithinOuterViewport) {
+TEST_P(ParameterizedVisualViewportTest,
+       ResizeVisualViewportStaysWithinOuterViewport) {
   initializeWithDesktopSettings();
   webViewImpl()->resize(IntSize(100, 200));
 
@@ -1692,7 +1747,8 @@
   EXPECT_EQ(0, visualViewport.scrollOffset().height());
 }
 
-TEST_P(VisualViewportTest, ElementBoundsInViewportSpaceAccountsForViewport) {
+TEST_P(ParameterizedVisualViewportTest,
+       ElementBoundsInViewportSpaceAccountsForViewport) {
   initializeWithAndroidSettings();
 
   webViewImpl()->resize(IntSize(500, 800));
@@ -1722,7 +1778,7 @@
   EXPECT_SIZE_EQ(expectedBounds.size(), boundsInViewport.size());
 }
 
-TEST_P(VisualViewportTest, ElementVisibleBoundsInVisualViewport) {
+TEST_P(ParameterizedVisualViewportTest, ElementVisibleBoundsInVisualViewport) {
   initializeWithAndroidSettings();
   webViewImpl()->resize(IntSize(640, 1080));
   registerMockedHttpURLLoad("viewport-select.html");
@@ -1739,7 +1795,8 @@
 
 // Test that the various window.scroll and document.body.scroll properties and
 // methods work unchanged from the pre-virtual viewport mode.
-TEST_P(VisualViewportTest, bodyAndWindowScrollPropertiesAccountForViewport) {
+TEST_P(ParameterizedVisualViewportTest,
+       bodyAndWindowScrollPropertiesAccountForViewport) {
   initializeWithAndroidSettings();
 
   webViewImpl()->resize(IntSize(200, 300));
@@ -1818,7 +1875,7 @@
 
 // Tests that when a new frame is created, it is created with the intended size
 // (i.e. viewport at minimum scale, 100x200 / 0.5).
-TEST_P(VisualViewportTest, TestMainFrameInitializationSizing) {
+TEST_P(ParameterizedVisualViewportTest, TestMainFrameInitializationSizing) {
   initializeWithAndroidSettings();
 
   webViewImpl()->resize(IntSize(100, 200));
@@ -1838,7 +1895,7 @@
 }
 
 // Tests that the maximum scroll offset of the viewport can be fractional.
-TEST_P(VisualViewportTest, FractionalMaxScrollOffset) {
+TEST_P(ParameterizedVisualViewportTest, FractionalMaxScrollOffset) {
   initializeWithDesktopSettings();
   webViewImpl()->resize(IntSize(101, 201));
   navigateTo("about:blank");
@@ -1858,7 +1915,7 @@
 // Tests that the slow scrolling after an impl scroll on the visual viewport is
 // continuous. crbug.com/453460 was caused by the impl-path not updating the
 // ScrollAnimatorBase class.
-TEST_P(VisualViewportTest, SlowScrollAfterImplScroll) {
+TEST_P(ParameterizedVisualViewportTest, SlowScrollAfterImplScroll) {
   initializeWithDesktopSettings();
   webViewImpl()->resize(IntSize(800, 600));
   navigateTo("about:blank");
@@ -1892,7 +1949,7 @@
   settings->setAccessibilityEnabled(true);
 }
 
-TEST_P(VisualViewportTest, AccessibilityHitTestWhileZoomedIn) {
+TEST_P(ParameterizedVisualViewportTest, AccessibilityHitTestWhileZoomedIn) {
   initializeWithDesktopSettings(accessibilitySettings);
 
   registerMockedHttpURLLoad("hit-test.html");
@@ -1922,7 +1979,7 @@
 }
 
 // Tests that the maximum scroll offset of the viewport can be fractional.
-TEST_P(VisualViewportTest, TestCoordinateTransforms) {
+TEST_P(ParameterizedVisualViewportTest, TestCoordinateTransforms) {
   initializeWithAndroidSettings();
   webViewImpl()->resize(IntSize(800, 600));
   registerMockedHttpURLLoad("content-width-1000.html");
@@ -1976,7 +2033,7 @@
 // More specifically, it checks that the innerWidth and innerHeight window
 // properties will trigger a layout which will cause an update to viewport
 // constraints and a refreshed initial scale. crbug.com/466718
-TEST_P(VisualViewportTest, WindowDimensionsOnLoad) {
+TEST_P(ParameterizedVisualViewportTest, WindowDimensionsOnLoad) {
   initializeWithAndroidSettings();
   registerMockedHttpURLLoad("window_dimensions.html");
   webViewImpl()->resize(IntSize(800, 600));
@@ -1992,7 +2049,7 @@
 // width for a very wide page. That is, make that innerWidth/Height actually
 // trigger a layout of the content, and not just an update of the viepwort.
 // crbug.com/466718
-TEST_P(VisualViewportTest, WindowDimensionsOnLoadWideContent) {
+TEST_P(ParameterizedVisualViewportTest, WindowDimensionsOnLoadWideContent) {
   initializeWithAndroidSettings();
   registerMockedHttpURLLoad("window_dimensions_wide_div.html");
   webViewImpl()->resize(IntSize(800, 600));
@@ -2004,7 +2061,8 @@
             std::string(output->innerHTML().ascii().data()));
 }
 
-TEST_P(VisualViewportTest, PinchZoomGestureScrollsVisualViewportOnly) {
+TEST_P(ParameterizedVisualViewportTest,
+       PinchZoomGestureScrollsVisualViewportOnly) {
   initializeWithDesktopSettings();
   webViewImpl()->resize(IntSize(100, 100));
 
@@ -2030,7 +2088,7 @@
                  frameView.layoutViewportScrollableArea()->scrollOffset());
 }
 
-TEST_P(VisualViewportTest, ResizeWithScrollAnchoring) {
+TEST_P(ParameterizedVisualViewportTest, ResizeWithScrollAnchoring) {
   bool wasScrollAnchoringEnabled =
       RuntimeEnabledFeatures::scrollAnchoringEnabled();
   RuntimeEnabledFeatures::setScrollAnchoringEnabled(true);
@@ -2055,7 +2113,7 @@
 
 // Ensure that resize anchoring as happens when browser controls hide/show
 // affects the scrollable area that's currently set as the root scroller.
-TEST_P(VisualViewportTest, ResizeAnchoringWithRootScroller) {
+TEST_P(ParameterizedVisualViewportTest, ResizeAnchoringWithRootScroller) {
   bool wasRootScrollerEnabled =
       RuntimeEnabledFeatures::setRootScrollerEnabled();
   RuntimeEnabledFeatures::setSetRootScrollerEnabled(true);
@@ -2090,7 +2148,7 @@
 
 // Ensure that resize anchoring as happens when the device is rotated affects
 // the scrollable area that's currently set as the root scroller.
-TEST_P(VisualViewportTest, RotationAnchoringWithRootScroller) {
+TEST_P(ParameterizedVisualViewportTest, RotationAnchoringWithRootScroller) {
   bool wasRootScrollerEnabled =
       RuntimeEnabledFeatures::setRootScrollerEnabled();
   RuntimeEnabledFeatures::setSetRootScrollerEnabled(true);
@@ -2130,7 +2188,7 @@
 
 // Make sure a composited background-attachment:fixed background gets resized
 // when using inert (non-layout affecting) browser controls.
-TEST_P(VisualViewportTest, ResizeCompositedAndFixedBackground) {
+TEST_P(ParameterizedVisualViewportTest, ResizeCompositedAndFixedBackground) {
   bool originalInertTopControls =
       RuntimeEnabledFeatures::inertTopControlsEnabled();
   RuntimeEnabledFeatures::setInertTopControlsEnabled(true);
@@ -2217,7 +2275,7 @@
 
 // Make sure a non-composited background-attachment:fixed background gets
 // resized when using inert (non-layout affecting) browser controls.
-TEST_P(VisualViewportTest, ResizeNonCompositedAndFixedBackground) {
+TEST_P(ParameterizedVisualViewportTest, ResizeNonCompositedAndFixedBackground) {
   bool originalInertTopControls =
       RuntimeEnabledFeatures::inertTopControlsEnabled();
   RuntimeEnabledFeatures::setInertTopControlsEnabled(true);
@@ -2320,7 +2378,8 @@
 
 // Make sure a browser control resize with background-attachment:not-fixed
 // background doesn't cause invalidation or layout.
-TEST_P(VisualViewportTest, ResizeNonFixedBackgroundNoLayoutOrInvalidation) {
+TEST_P(ParameterizedVisualViewportTest,
+       ResizeNonFixedBackgroundNoLayoutOrInvalidation) {
   bool originalInertTopControls =
       RuntimeEnabledFeatures::inertTopControlsEnabled();
   RuntimeEnabledFeatures::setInertTopControlsEnabled(true);
diff --git a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
index 637306d0..cfe94611 100644
--- a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
@@ -79,13 +79,18 @@
 #include "core/testing/NullExecutionContext.h"
 #include "modules/mediastream/MediaStream.h"
 #include "modules/mediastream/MediaStreamRegistry.h"
+#include "platform/Cursor.h"
 #include "platform/DragImage.h"
+#include "platform/PlatformMouseEvent.h"
 #include "platform/PlatformResourceLoader.h"
 #include "platform/RuntimeEnabledFeatures.h"
 #include "platform/UserGestureIndicator.h"
 #include "platform/geometry/FloatRect.h"
 #include "platform/network/ResourceError.h"
+#include "platform/scroll/Scrollbar.h"
+#include "platform/scroll/ScrollbarTestSuite.h"
 #include "platform/scroll/ScrollbarTheme.h"
+#include "platform/scroll/ScrollbarThemeMock.h"
 #include "platform/scroll/ScrollbarThemeOverlayMock.h"
 #include "platform/testing/RuntimeEnabledFeaturesTestHelpers.h"
 #include "platform/testing/URLTestHelpers.h"
@@ -10461,6 +10466,104 @@
       scrollerArea->verticalScrollbar()->shouldParticipateInHitTesting());
 }
 
+// Makes sure that mouse hover over an overlay scrollbar doesn't activate
+// elements below unless the scrollbar is faded out.
+TEST_F(WebFrameTest, MouseOverLinkAndOverlayScrollbar) {
+  FrameTestHelpers::WebViewHelper webViewHelper;
+  webViewHelper.initialize(
+      true, nullptr, nullptr, nullptr,
+      [](WebSettings* settings) { settings->setDeviceSupportsMouse(true); });
+  webViewHelper.resize(WebSize(20, 20));
+  WebViewImpl* webView = webViewHelper.webView();
+
+  initializeWithHTML(*webView->mainFrameImpl()->frame(),
+                     "<!DOCTYPE html>"
+                     "<a id='a' href='javascript:void(0);'>"
+                     "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+                     "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+                     "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+                     "</a>"
+                     "<div style='position: absolute; top: 1000px'>end</div>");
+
+  webView->updateAllLifecyclePhases();
+
+  Document* document = webView->mainFrameImpl()->frame()->document();
+  Element* aTag = document->getElementById("a");
+
+  // Ensure hittest has scrollbar and link
+  HitTestResult hitTestResult =
+      webView->coreHitTestResultAt(WebPoint(18, aTag->offsetTop()));
+
+  EXPECT_TRUE(hitTestResult.URLElement());
+  EXPECT_TRUE(hitTestResult.innerElement());
+  EXPECT_TRUE(hitTestResult.scrollbar());
+
+  // Mouse over link. Mouse cursor should be hand.
+  PlatformMouseEvent mouseMoveOverLinkEvent(
+      IntPoint(aTag->offsetLeft(), aTag->offsetTop()),
+      IntPoint(aTag->offsetLeft(), aTag->offsetTop()),
+      WebPointerProperties::Button::NoButton, PlatformEvent::MouseMoved, 0,
+      PlatformEvent::NoModifiers, WTF::monotonicallyIncreasingTime());
+  document->frame()->eventHandler().handleMouseMoveEvent(
+      mouseMoveOverLinkEvent);
+
+  EXPECT_EQ(
+      Cursor::Type::Hand,
+      document->frame()->chromeClient().lastSetCursorForTesting().getType());
+
+  // Mouse over enabled overlay scrollbar. Mouse cursor should be pointer and no
+  // active hover element.
+  PlatformMouseEvent mouseMoveEvent(
+      IntPoint(18, aTag->offsetTop()), IntPoint(18, aTag->offsetTop()),
+      WebPointerProperties::Button::NoButton, PlatformEvent::MouseMoved, 0,
+      PlatformEvent::NoModifiers, WTF::monotonicallyIncreasingTime());
+  document->frame()->eventHandler().handleMouseMoveEvent(mouseMoveEvent);
+
+  EXPECT_EQ(
+      Cursor::Type::Pointer,
+      document->frame()->chromeClient().lastSetCursorForTesting().getType());
+
+  PlatformMouseEvent mousePressEvent(
+      IntPoint(18, aTag->offsetTop()), IntPoint(18, aTag->offsetTop()),
+      WebPointerProperties::Button::Left, PlatformEvent::MousePressed, 0,
+      PlatformEvent::Modifiers::LeftButtonDown,
+      WTF::monotonicallyIncreasingTime());
+  document->frame()->eventHandler().handleMousePressEvent(mousePressEvent);
+
+  EXPECT_FALSE(document->activeHoverElement());
+  EXPECT_FALSE(document->hoverNode());
+
+  PlatformMouseEvent MouseReleaseEvent(
+      IntPoint(18, aTag->offsetTop()), IntPoint(18, aTag->offsetTop()),
+      WebPointerProperties::Button::Left, PlatformEvent::MouseReleased, 0,
+      PlatformEvent::Modifiers::LeftButtonDown,
+      WTF::monotonicallyIncreasingTime());
+  document->frame()->eventHandler().handleMouseReleaseEvent(MouseReleaseEvent);
+
+  // Mouse over disabled overlay scrollbar. Mouse cursor should be hand and has
+  // active hover element.
+  webView->mainFrameImpl()->frameView()->setScrollbarsHidden(true);
+
+  // Ensure hittest only has link
+  hitTestResult = webView->coreHitTestResultAt(WebPoint(18, aTag->offsetTop()));
+
+  EXPECT_TRUE(hitTestResult.URLElement());
+  EXPECT_TRUE(hitTestResult.innerElement());
+  EXPECT_FALSE(hitTestResult.scrollbar());
+
+  document->frame()->eventHandler().handleMouseMoveEvent(mouseMoveEvent);
+
+  EXPECT_EQ(
+      Cursor::Type::Hand,
+      document->frame()->chromeClient().lastSetCursorForTesting().getType());
+
+  document->frame()->eventHandler().handleMousePressEvent(mousePressEvent);
+
+  EXPECT_TRUE(document->activeHoverElement());
+  EXPECT_TRUE(document->hoverNode());
+
+  document->frame()->eventHandler().handleMouseReleaseEvent(MouseReleaseEvent);
+}
 static void disableCompositing(WebSettings* settings) {
   settings->setAcceleratedCompositingEnabled(false);
   settings->setPreferCompositingToLCDTextEnabled(false);
diff --git a/third_party/WebKit/Source/web/tests/WebInputEventConversionTest.cpp b/third_party/WebKit/Source/web/tests/WebInputEventConversionTest.cpp
index 85a2341..b2b2af8 100644
--- a/third_party/WebKit/Source/web/tests/WebInputEventConversionTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebInputEventConversionTest.cpp
@@ -149,7 +149,7 @@
     touchList->append(touch0);
     TouchEvent* touchEvent = TouchEvent::create(
         touchList, touchList, touchList, EventTypeNames::touchstart, domWindow,
-        PlatformEvent::NoModifiers, false, false, true, 0);
+        PlatformEvent::NoModifiers, false, false, true, 0, TouchActionAuto);
 
     WebTouchEventBuilder webTouchBuilder(documentLayoutView, *touchEvent);
     ASSERT_EQ(1u, webTouchBuilder.touchesLength);
@@ -174,7 +174,7 @@
     touchList->append(touch0);
     TouchEvent* touchEvent = TouchEvent::create(
         touchList, touchList, touchList, EventTypeNames::touchstart, domWindow,
-        PlatformEvent::NoModifiers, true, false, true, 0);
+        PlatformEvent::NoModifiers, true, false, true, 0, TouchActionAuto);
 
     WebTouchEventBuilder webTouchBuilder(documentLayoutView, *touchEvent);
     EXPECT_EQ(WebInputEvent::Blocking, webTouchBuilder.dispatchType);
@@ -187,10 +187,10 @@
     activeTouchList->append(touch0);
     activeTouchList->append(touch1);
     movedTouchList->append(touch0);
-    TouchEvent* touchEvent =
-        TouchEvent::create(activeTouchList, activeTouchList, movedTouchList,
-                           EventTypeNames::touchmove, domWindow,
-                           PlatformEvent::NoModifiers, false, false, true, 0);
+    TouchEvent* touchEvent = TouchEvent::create(
+        activeTouchList, activeTouchList, movedTouchList,
+        EventTypeNames::touchmove, domWindow, PlatformEvent::NoModifiers, false,
+        false, true, 0, TouchActionAuto);
 
     WebTouchEventBuilder webTouchBuilder(documentLayoutView, *touchEvent);
     ASSERT_EQ(2u, webTouchBuilder.touchesLength);
@@ -209,10 +209,10 @@
     activeTouchList->append(touch0);
     activeTouchList->append(touch1);
     movedTouchList->append(touch1);
-    TouchEvent* touchEvent =
-        TouchEvent::create(activeTouchList, activeTouchList, movedTouchList,
-                           EventTypeNames::touchmove, domWindow,
-                           PlatformEvent::NoModifiers, false, false, true, 0);
+    TouchEvent* touchEvent = TouchEvent::create(
+        activeTouchList, activeTouchList, movedTouchList,
+        EventTypeNames::touchmove, domWindow, PlatformEvent::NoModifiers, false,
+        false, true, 0, TouchActionAuto);
 
     WebTouchEventBuilder webTouchBuilder(documentLayoutView, *touchEvent);
     ASSERT_EQ(2u, webTouchBuilder.touchesLength);
@@ -230,10 +230,10 @@
     TouchList* releasedTouchList = TouchList::create();
     activeTouchList->append(touch0);
     releasedTouchList->append(touch1);
-    TouchEvent* touchEvent =
-        TouchEvent::create(activeTouchList, activeTouchList, releasedTouchList,
-                           EventTypeNames::touchend, domWindow,
-                           PlatformEvent::NoModifiers, false, false, false, 0);
+    TouchEvent* touchEvent = TouchEvent::create(
+        activeTouchList, activeTouchList, releasedTouchList,
+        EventTypeNames::touchend, domWindow, PlatformEvent::NoModifiers, false,
+        false, false, 0, TouchActionAuto);
 
     WebTouchEventBuilder webTouchBuilder(documentLayoutView, *touchEvent);
     ASSERT_EQ(2u, webTouchBuilder.touchesLength);
@@ -251,10 +251,10 @@
     TouchList* cancelledTouchList = TouchList::create();
     cancelledTouchList->append(touch0);
     cancelledTouchList->append(touch1);
-    TouchEvent* touchEvent =
-        TouchEvent::create(activeTouchList, activeTouchList, cancelledTouchList,
-                           EventTypeNames::touchcancel, domWindow,
-                           PlatformEvent::NoModifiers, false, false, false, 0);
+    TouchEvent* touchEvent = TouchEvent::create(
+        activeTouchList, activeTouchList, cancelledTouchList,
+        EventTypeNames::touchcancel, domWindow, PlatformEvent::NoModifiers,
+        false, false, false, 0, TouchActionAuto);
 
     WebTouchEventBuilder webTouchBuilder(documentLayoutView, *touchEvent);
     ASSERT_EQ(2u, webTouchBuilder.touchesLength);
@@ -281,7 +281,7 @@
     }
     TouchEvent* touchEvent = TouchEvent::create(
         touchList, touchList, touchList, EventTypeNames::touchstart, domWindow,
-        PlatformEvent::NoModifiers, false, false, true, 0);
+        PlatformEvent::NoModifiers, false, false, true, 0, TouchActionAuto);
 
     WebTouchEventBuilder webTouchBuilder(documentLayoutView, *touchEvent);
     ASSERT_EQ(static_cast<unsigned>(WebTouchEvent::kTouchesLengthCap),
@@ -559,7 +559,7 @@
     touchList->append(touch);
     TouchEvent* touchEvent = TouchEvent::create(
         touchList, touchList, touchList, EventTypeNames::touchmove, domWindow,
-        PlatformEvent::NoModifiers, false, false, true, 0);
+        PlatformEvent::NoModifiers, false, false, true, 0, TouchActionAuto);
 
     WebTouchEventBuilder webTouchBuilder(documentLayoutView, *touchEvent);
     ASSERT_EQ(1u, webTouchBuilder.touchesLength);
diff --git a/third_party/WebKit/Source/web/tests/WebViewTest.cpp b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
index 74a3254..694a33eff 100644
--- a/third_party/WebKit/Source/web/tests/WebViewTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
@@ -1847,9 +1847,10 @@
 
   const WebPoint clientPoint(0, 0);
   const WebPoint screenPoint(0, 0);
-  webView->dragTargetDragEnter(dragData, clientPoint, screenPoint,
-                               WebDragOperationCopy, 0);
-  webView->dragTargetDrop(dragData, clientPoint, screenPoint, 0);
+  WebFrameWidgetBase* widget = webView->mainFrameImpl()->frameWidget();
+  widget->dragTargetDragEnter(dragData, clientPoint, screenPoint,
+                              WebDragOperationCopy, 0);
+  widget->dragTargetDrop(dragData, clientPoint, screenPoint, 0);
   FrameTestHelpers::pumpPendingRequestsForFrameToLoad(webView->mainFrame());
 }
 
diff --git a/third_party/WebKit/Source/wtf/Threading.h b/third_party/WebKit/Source/wtf/Threading.h
index f24b9c5..ffe95e6 100644
--- a/third_party/WebKit/Source/wtf/Threading.h
+++ b/third_party/WebKit/Source/wtf/Threading.h
@@ -65,11 +65,11 @@
 WTF_EXPORT void lockAtomicallyInitializedStaticMutex();
 WTF_EXPORT void unlockAtomicallyInitializedStaticMutex();
 
-// Implementations of these functions are only defined when ENABLE(ASSERT), and
-// should only be used within ASSERT or DCHECK.
+#if ENABLE(ASSERT)
 WTF_EXPORT bool isAtomicallyInitializedStaticMutexLockHeld();
 WTF_EXPORT bool isBeforeThreadCreated();
 WTF_EXPORT void willCreateThread();
+#endif
 
 }  // namespace WTF
 
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/bindings/bindings_tests.py b/third_party/WebKit/Tools/Scripts/webkitpy/bindings/bindings_tests.py
index 2a0b108a..c091314f 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/bindings/bindings_tests.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/bindings/bindings_tests.py
@@ -44,6 +44,7 @@
 from code_generator_v8 import CodeGeneratorV8
 from code_generator_v8 import CodeGeneratorUnionType
 from code_generator_v8 import CodeGeneratorCallbackFunction
+from code_generator_web_module import CodeGeneratorWebModule
 from compute_interfaces_info_individual import InterfaceInfoCollector
 from compute_interfaces_info_overall import (compute_interfaces_info_overall,
                                              interfaces_info)
@@ -329,25 +330,31 @@
                                 'dependencies_other_component_full_paths']:
                             partial_interface_filenames.append(idl_path)
 
+            info_provider = component_info_providers[component]
+            partial_interface_info_provider = component_info_providers['modules']
+
             generate_union_type_containers(CodeGeneratorUnionType,
-                                           component_info_providers[component],
-                                           options)
+                                           info_provider, options)
             generate_callback_function_impl(CodeGeneratorCallbackFunction,
-                                            component_info_providers[component],
-                                            options)
+                                            info_provider, options)
             generate_bindings(
                 CodeGeneratorV8,
-                component_info_providers[component],
+                info_provider,
+                options,
+                idl_filenames)
+            generate_bindings(
+                CodeGeneratorWebModule,
+                info_provider,
                 options,
                 idl_filenames)
             generate_bindings(
                 CodeGeneratorV8,
-                component_info_providers['modules'],
+                partial_interface_info_provider,
                 partial_interface_options,
                 partial_interface_filenames)
             generate_dictionary_impl(
                 CodeGeneratorDictionaryImpl,
-                component_info_providers[component],
+                info_provider,
                 options,
                 dictionary_impl_filenames)
 
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py
index 57e141e..dbcc2a0c 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py
@@ -56,9 +56,6 @@
 
 _log = logging.getLogger(__name__)
 
-# Builder base URL where we have the archived test results.
-BUILDER_BASE_URL = "http://build.chromium.org/buildbot/layout_test_results/"
-
 TestExpectations = test_expectations.TestExpectations
 
 
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android_unittest.py
index de9b63f9..44333faf 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android_unittest.py
@@ -54,13 +54,6 @@
 sys.path.append(_MOCK_ROOT)
 import mock
 
-# Type of tombstone test which the mocked Android Debug Bridge should execute.
-VALID_TOMBSTONE_TEST_TYPE = 0
-NO_FILES_TOMBSTONE_TEST_TYPE = 1
-NO_PERMISSION_TOMBSTONE_TEST_TYPE = 2
-INVALID_ENTRY_TOMBSTONE_TEST_TYPE = 3
-INVALID_ENTRIES_TOMBSTONE_TEST_TYPE = 4
-
 
 def mock_devices():
     serials = ['123456789ABCDEF0', '123456789ABCDEF1', '123456789ABCDEF2',
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py
index 7ee66f20..5439519 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py
@@ -19,7 +19,6 @@
 
 # pylint: disable=protected-access
 class BaseTestCase(unittest.TestCase):
-    MOCK_WEB_RESULT = 'MOCK Web result, convert 404 to None=True'
     WEB_PREFIX = 'https://storage.googleapis.com/chromium-layout-test-archives/MOCK_Mac10_11/results/layout-test-results'
 
     command_constructor = None
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/deps_updater.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/deps_updater.py
index b927bf2..be88f0b 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/deps_updater.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/deps_updater.py
@@ -25,8 +25,6 @@
 WPT_REPO_URL = 'https://chromium.googlesource.com/external/w3c/web-platform-tests.git'
 CSS_REPO_URL = 'https://chromium.googlesource.com/external/w3c/csswg-test.git'
 
-POLL_DELAY_SECONDS = 900
-
 
 _log = logging.getLogger(__file__)
 
diff --git a/third_party/WebKit/public/BUILD.gn b/third_party/WebKit/public/BUILD.gn
index 502fef7e..39f1284 100644
--- a/third_party/WebKit/public/BUILD.gn
+++ b/third_party/WebKit/public/BUILD.gn
@@ -68,10 +68,13 @@
   configs = [ "//v8:external_config" ]
 }
 
-# Public feature flags usabout outside of Blink.
+# Public feature flags used outside of Blink.
 buildflag_header("features") {
   header = "public_features.h"
-  flags = [ "DEBUG_DEVTOOLS=$debug_devtools" ]
+  flags = [
+    "DEBUG_DEVTOOLS=$debug_devtools",
+    "USE_MINIKIN_HYPHENATION=$use_minikin_hyphenation",
+  ]
 }
 
 if (is_android) {
diff --git a/third_party/WebKit/public/public_features.gni b/third_party/WebKit/public/public_features.gni
index 6d2e373..2eb71d3 100644
--- a/third_party/WebKit/public/public_features.gni
+++ b/third_party/WebKit/public/public_features.gni
@@ -9,3 +9,6 @@
   # --debug-devtools cmdline switch.
   debug_devtools = false
 }
+
+# Use Minikin hyphenation engine.
+use_minikin_hyphenation = is_android
diff --git a/third_party/WebKit/public/web/WebFrameWidget.h b/third_party/WebKit/public/web/WebFrameWidget.h
index 29267ce..952132d 100644
--- a/third_party/WebKit/public/web/WebFrameWidget.h
+++ b/third_party/WebKit/public/web/WebFrameWidget.h
@@ -32,11 +32,13 @@
 #define WebFrameWidget_h
 
 #include "../platform/WebCommon.h"
+#include "../platform/WebDragOperation.h"
 #include "../platform/WebPageVisibilityState.h"
 #include "public/web/WebWidget.h"
 
 namespace blink {
 
+class WebDragData;
 class WebLocalFrame;
 class WebInputMethodController;
 class WebView;
@@ -74,7 +76,7 @@
   virtual void setBaseBackgroundColor(WebColor) = 0;
 
   // Returns the local root of this WebFrameWidget.
-  virtual WebLocalFrame* localRoot() = 0;
+  virtual WebLocalFrame* localRoot() const = 0;
 
   // WebWidget implementation.
   bool isWebFrameWidget() const final { return true; }
@@ -85,6 +87,25 @@
   // frames or possibly when the WebFrameWidget does not accept IME events.
   virtual WebInputMethodController* getActiveWebInputMethodController()
       const = 0;
+
+  // Callback methods when a drag-and-drop operation is trying to drop something
+  // on the WebWidget.
+  virtual WebDragOperation dragTargetDragEnter(
+      const WebDragData&,
+      const WebPoint& pointInViewport,
+      const WebPoint& screenPoint,
+      WebDragOperationsMask operationsAllowed,
+      int modifiers) = 0;
+  virtual WebDragOperation dragTargetDragOver(
+      const WebPoint& pointInViewport,
+      const WebPoint& screenPoint,
+      WebDragOperationsMask operationsAllowed,
+      int modifiers) = 0;
+  virtual void dragTargetDragLeave() = 0;
+  virtual void dragTargetDrop(const WebDragData&,
+                              const WebPoint& pointInViewport,
+                              const WebPoint& screenPoint,
+                              int modifiers) = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/public/web/WebView.h b/third_party/WebKit/public/web/WebView.h
index a83fe01..d1e98a7 100644
--- a/third_party/WebKit/public/web/WebView.h
+++ b/third_party/WebKit/public/web/WebView.h
@@ -376,25 +376,6 @@
   // Notfies the WebView that the system drag and drop operation has ended.
   virtual void dragSourceSystemDragEnded() = 0;
 
-  // Callback methods when a drag-and-drop operation is trying to drop
-  // something on the WebView.
-  virtual WebDragOperation dragTargetDragEnter(
-      const WebDragData&,
-      const WebPoint& pointInViewport,
-      const WebPoint& screenPoint,
-      WebDragOperationsMask operationsAllowed,
-      int modifiers) = 0;
-  virtual WebDragOperation dragTargetDragOver(
-      const WebPoint& pointInViewport,
-      const WebPoint& screenPoint,
-      WebDragOperationsMask operationsAllowed,
-      int modifiers) = 0;
-  virtual void dragTargetDragLeave() = 0;
-  virtual void dragTargetDrop(const WebDragData&,
-                              const WebPoint& pointInViewport,
-                              const WebPoint& screenPoint,
-                              int modifiers) = 0;
-
   // Retrieves a list of spelling markers.
   virtual void spellingMarkers(WebVector<uint32_t>* markers) = 0;
   virtual void removeSpellingMarkersUnderWords(
diff --git a/third_party/gvr-android-sdk/BUILD.gn b/third_party/gvr-android-sdk/BUILD.gn
index 3436b43..a31bff9 100644
--- a/third_party/gvr-android-sdk/BUILD.gn
+++ b/third_party/gvr-android-sdk/BUILD.gn
@@ -4,17 +4,8 @@
 
 import("//build/config/android/rules.gni")
 
-gvr_arch = current_cpu
-if (gvr_arch == "x64") {
-  gvr_arch = "x86_64"
-}
-
 android_aar_prebuilt("gvr_common_java") {
-  aar_path = "src/ndk/lib/common_library.aar"
-}
-
-android_aar_prebuilt("gvr_base_java") {
-  aar_path = "src/libraries/base/base.aar"
+  aar_path = "common_library.aar"
   proguard_configs = [ "src/proguard-gvr.txt" ]
   ignore_manifest = true  # Ignored because manifest merging is not supported (http://crbug.com/643967)
   ignore_native_libraries = true
@@ -24,26 +15,6 @@
   aar_path = "src/libraries/controller/controller.aar"
 }
 
-source_set("libgvr") {
-  if (gvr_arch != "mipsel") {
-    data_deps = [
-      ":libgvr_copy",
-    ]
-    libs = [ "${root_out_dir}/libgvr.so" ]
-  }
-}
-
-if (gvr_arch != "mipsel") {
-  copy("libgvr_copy") {
-    sources = [
-      "src/ndk/lib/android_${gvr_arch}/libgvr.so",
-    ]
-    outputs = [
-      "${root_out_dir}/libgvr.so",
-    ]
-  }
-}
-
 config("libgvr_config") {
   include_dirs = [ "src/ndk/include/" ]
 }
diff --git a/third_party/gvr-android-sdk/DEPS b/third_party/gvr-android-sdk/DEPS
new file mode 100644
index 0000000..925c5f0
--- /dev/null
+++ b/third_party/gvr-android-sdk/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  '+base/android/jni_android.h',
+  '+base/android/jni_generator/jni_generator_helper.h',
+  '+base/android/jni_int_wrapper.h',
+]
diff --git a/third_party/gvr-android-sdk/README.chromium b/third_party/gvr-android-sdk/README.chromium
index f0da5dc..b7cda7f0 100644
--- a/third_party/gvr-android-sdk/README.chromium
+++ b/third_party/gvr-android-sdk/README.chromium
@@ -14,4 +14,14 @@
 complex API for supporting Daydream-ready phones and the Daydream controller.
 
 Local Modifications:
-None
+Only header files in NDK are used. Due to binary size concern, we have decided
+to use a static shim library instead of the shared library that comes with
+this checkout. The static libraries are downloaded from a public storage through
+gclient sync. Currently, the static libraries needs a newer version of
+common_library.aar than the one provided by this checkout. So we also download
+it from the same public storage. Once the version on github catches up, we will
+remove common_library.aar.
+All JNI calls in the static library also needs to be manually registered. So
+we have 3 jni related files. These files were generated by
+base/android/jni_generator/jni_generator.py from Java files. Modifications to
+these generated files are documented in the files.
diff --git a/third_party/gvr-android-sdk/common_library.aar.sha1 b/third_party/gvr-android-sdk/common_library.aar.sha1
new file mode 100644
index 0000000..0c18a64
--- /dev/null
+++ b/third_party/gvr-android-sdk/common_library.aar.sha1
@@ -0,0 +1 @@
+cf3910016cad0fd147d57031e4025dd6de44a737
\ No newline at end of file
diff --git a/third_party/gvr-android-sdk/display_synchronizer_jni.h b/third_party/gvr-android-sdk/display_synchronizer_jni.h
new file mode 100644
index 0000000..89f1fc6
--- /dev/null
+++ b/third_party/gvr-android-sdk/display_synchronizer_jni.h
@@ -0,0 +1,137 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is of the same format as file that generated by
+//     base/android/jni_generator/jni_generator.py
+// For
+//     com/google/vr/cardboard/DisplaySynchronizer
+
+// Local modification includes:
+// 1. Remove all implementaiton, only keep definition.
+// 2. Use absolute path instead of relative path.
+// 3. Removed all helper functions such as: Create.
+// 4. Added function RegisterDisplaySynchronizerNatives at the end of this file.
+
+#ifndef com_google_vr_cardboard_DisplaySynchronizer_JNI
+#define com_google_vr_cardboard_DisplaySynchronizer_JNI
+
+#include "base/android/jni_android.h"
+// ----------------------------------------------------------------------------
+// Native JNI methods
+// ----------------------------------------------------------------------------
+#include <jni.h>
+
+#include "base/android/jni_generator/jni_generator_helper.h"
+
+#include "base/android/jni_int_wrapper.h"
+
+// Step 1: forward declarations.
+namespace {
+
+const char kDisplaySynchronizerClassPath[] =
+    "com/google/vr/cardboard/DisplaySynchronizer";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+base::subtle::AtomicWord g_DisplaySynchronizer_clazz __attribute__((unused)) =
+    0;
+#define DisplaySynchronizer_clazz(env)                            \
+  base::android::LazyGetClass(env, kDisplaySynchronizerClassPath, \
+                              &g_DisplaySynchronizer_clazz)
+
+}  // namespace
+
+namespace DisplaySynchronizer {
+
+extern "C" __attribute__((visibility("default"))) jlong
+Java_com_google_vr_cardboard_DisplaySynchronizer_nativeCreate(
+    JNIEnv* env,
+    jobject jcaller,
+    jclass classLoader,
+    jobject appContext);
+
+// Step 2: method stubs.
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_cardboard_DisplaySynchronizer_nativeDestroy(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeDisplaySynchronizer);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_cardboard_DisplaySynchronizer_nativeReset(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeDisplaySynchronizer,
+    jlong expectedInterval,
+    jlong vsyncOffset);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_cardboard_DisplaySynchronizer_nativeUpdate(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeDisplaySynchronizer,
+    jlong syncTime,
+    jint currentRotation);
+
+// Step 3: RegisterNatives.
+
+static const JNINativeMethod kMethodsDisplaySynchronizer[] = {
+    {"nativeCreate",
+     "("
+     "Ljava/lang/ClassLoader;"
+     "Landroid/content/Context;"
+     ")"
+     "J",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_cardboard_DisplaySynchronizer_nativeCreate)},
+    {"nativeDestroy",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_cardboard_DisplaySynchronizer_nativeDestroy)},
+    {"nativeReset",
+     "("
+     "J"
+     "J"
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_cardboard_DisplaySynchronizer_nativeReset)},
+    {"nativeUpdate",
+     "("
+     "J"
+     "J"
+     "I"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_cardboard_DisplaySynchronizer_nativeUpdate)},
+};
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+  if (base::android::IsManualJniRegistrationDisabled())
+    return true;
+
+  const int kMethodsDisplaySynchronizerSize =
+      arraysize(kMethodsDisplaySynchronizer);
+
+  if (env->RegisterNatives(DisplaySynchronizer_clazz(env),
+                           kMethodsDisplaySynchronizer,
+                           kMethodsDisplaySynchronizerSize) < 0) {
+    jni_generator::HandleRegistrationError(env, DisplaySynchronizer_clazz(env),
+                                           __FILE__);
+    return false;
+  }
+
+  return true;
+}
+
+static bool RegisterDisplaySynchronizerNatives(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+}  // namespace DisplaySynchronizer
+
+#endif  // com_google_vr_cardboard_DisplaySynchronizer_JNI
diff --git a/third_party/gvr-android-sdk/gvr_api_jni.h b/third_party/gvr-android-sdk/gvr_api_jni.h
new file mode 100644
index 0000000..0c91a9a0
--- /dev/null
+++ b/third_party/gvr-android-sdk/gvr_api_jni.h
@@ -0,0 +1,1306 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is of the same format as file that generated by
+//     base/android/jni_generator/jni_generator.py
+// For
+//     com/google/vr/ndk/base/GvrApi
+
+// Local modification includes:
+// 1. Remove all implementaiton, only keep definition.
+// 2. Use absolute path instead of relative path.
+// 3. Removed all helper functions such as: Create.
+// 4. Changed RectF, Point, and PoseTracker to correct package name.
+// 5. Added function RegisterGvrApiNatives at the end of this file.
+
+#ifndef com_google_vr_ndk_base_GvrApi_JNI
+#define com_google_vr_ndk_base_GvrApi_JNI
+
+#include "base/android/jni_android.h"
+// ----------------------------------------------------------------------------
+// Native JNI methods
+// ----------------------------------------------------------------------------
+#include <jni.h>
+
+#include "base/android/jni_generator/jni_generator_helper.h"
+
+#include "base/android/jni_int_wrapper.h"
+
+// Step 1: forward declarations.
+namespace {
+const char kGvrApiClassPath[] = "com/google/vr/ndk/base/GvrApi";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+base::subtle::AtomicWord g_GvrApi_clazz __attribute__((unused)) = 0;
+#define GvrApi_clazz(env) \
+  base::android::LazyGetClass(env, kGvrApiClassPath, &g_GvrApi_clazz)
+
+}  // namespace
+
+namespace GvrApi {
+
+// Step 2: method stubs.
+extern "C" __attribute__((visibility("default"))) jlong
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportListCreate(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportListDestroy(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferViewportList);
+
+extern "C" __attribute__((visibility("default"))) jint
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportListGetSize(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferViewportList);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportListGetItem(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferViewportList,
+    jint index,
+    jlong nativeBufferViewport);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportListSetItem(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferViewportList,
+    jint index,
+    jlong nativeBufferViewport);
+
+extern "C" __attribute__((visibility("default"))) jlong
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportCreate(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportDestroy(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferViewport);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportGetSourceUv(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferViewport,
+    jobject out);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportSetSourceUv(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferViewport,
+    jfloat left,
+    jfloat top,
+    jfloat right,
+    jfloat bottom);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportGetSourceFov(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferViewport,
+    jobject out);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportSetSourceFov(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferViewport,
+    jfloat left,
+    jfloat top,
+    jfloat right,
+    jfloat bottom);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportGetTransform(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferViewport,
+    jfloatArray matrix);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportSetTransform(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferViewport,
+    jfloatArray matrix);
+
+extern "C" __attribute__((visibility("default"))) jboolean
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportEqual(JNIEnv* env,
+                                                             jobject jcaller,
+                                                             jlong nativeA,
+                                                             jlong nativeB);
+
+extern "C" __attribute__((visibility("default"))) jint
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportGetTargetEye(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferViewport);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportSetTargetEye(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferViewport,
+    jint eye);
+
+extern "C" __attribute__((visibility("default"))) jint
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportGetSourceBufferIndex(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferViewport);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportSetSourceBufferIndex(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferViewport,
+    jint index);
+
+extern "C" __attribute__((visibility("default"))) jint
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportGetExternalSurfaceId(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferViewport);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportSetExternalSurfaceId(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferViewport,
+    jint id);
+
+extern "C" __attribute__((visibility("default"))) jint
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportGetReprojection(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferViewport);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportSetReprojection(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferViewport,
+    jint reprojection);
+
+extern "C" __attribute__((visibility("default"))) jlong
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferSpecCreate(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferSpecDestroy(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferSpec);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferSpecGetSize(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferSpec,
+    jobject size);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferSpecSetSize(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferSpec,
+    jint width,
+    jint height);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferSpecSetColorFormat(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferSpec,
+    jint format);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferSpecSetDepthStencilFormat(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferSpec,
+    jint format);
+
+extern "C" __attribute__((visibility("default"))) jint
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferSpecGetSamples(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferSpec);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeBufferSpecSetSamples(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeBufferSpec,
+    jint samples);
+
+extern "C" __attribute__((visibility("default"))) jlong
+Java_com_google_vr_ndk_base_GvrApi_nativeSwapChainCreate(JNIEnv* env,
+                                                         jobject jcaller,
+                                                         jlong nativeContext,
+                                                         jlongArray specs);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeSwapChainDestroy(JNIEnv* env,
+                                                          jobject jcaller,
+                                                          jlong nativeContext);
+
+extern "C" __attribute__((visibility("default"))) jint
+Java_com_google_vr_ndk_base_GvrApi_nativeSwapChainGetBufferCount(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeSwapChain);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeSwapChainGetBufferSize(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeSwapChain,
+    jint bufferIndex,
+    jobject size);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeSwapChainResizeBuffer(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeSwapChain,
+    jint bufferIndex,
+    jint width,
+    jint height);
+
+extern "C" __attribute__((visibility("default"))) jlong
+Java_com_google_vr_ndk_base_GvrApi_nativeSwapChainAcquireFrame(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeSwapChain);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeFrameBindBuffer(JNIEnv* env,
+                                                         jobject jcaller,
+                                                         jlong nativeFrame,
+                                                         jint bufferIndex);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeFrameUnbind(JNIEnv* env,
+                                                     jobject jcaller,
+                                                     jlong nativeFrame);
+
+extern "C" __attribute__((visibility("default"))) jint
+Java_com_google_vr_ndk_base_GvrApi_nativeFrameGetFramebufferObject(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeFrame,
+    jint bufferIndex);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeFrameGetBufferSize(JNIEnv* env,
+                                                            jobject jcaller,
+                                                            jlong nativeFrame,
+                                                            jint bufferIndex,
+                                                            jobject size);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeFrameSubmit(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeFrame,
+    jlong nativeBufferViewportList,
+    jfloatArray transform);
+
+extern "C" __attribute__((visibility("default"))) jlong
+Java_com_google_vr_ndk_base_GvrApi_nativeCreate(
+    JNIEnv* env,
+    jobject jcaller,
+    jclass classLoader,
+    jobject context,
+    jlong synchronizer,
+    jint widthPixels,
+    jint heightPixels,
+    jfloat xDpi,
+    jfloat yDpi,
+    jobject optionalPoseTrackingForTesting);
+
+extern "C" __attribute__((visibility("default"))) jint
+Java_com_google_vr_ndk_base_GvrApi_nativeGetError(JNIEnv* env,
+                                                  jobject jcaller,
+                                                  jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) jint
+Java_com_google_vr_ndk_base_GvrApi_nativeClearError(JNIEnv* env,
+                                                    jobject jcaller,
+                                                    jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) jstring
+Java_com_google_vr_ndk_base_GvrApi_nativeGetErrorString(JNIEnv* env,
+                                                        jclass jcaller,
+                                                        jint errorCode);
+
+extern "C" __attribute__((visibility("default"))) jlong
+Java_com_google_vr_ndk_base_GvrApi_nativeGetUserPrefs(JNIEnv* env,
+                                                      jobject jcaller,
+                                                      jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) jint
+Java_com_google_vr_ndk_base_GvrApi_nativeUserPrefsGetControllerHandedness(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeUserPrefs);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativePause(JNIEnv* env,
+                                               jobject jcaller,
+                                               jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeResume(JNIEnv* env,
+                                                jobject jcaller,
+                                                jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeReleaseGvrContext(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeInitializeGl(JNIEnv* env,
+                                                      jobject jcaller,
+                                                      jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeOnSurfaceCreatedReprojectionThread(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeGetRecommendedBufferViewports(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext,
+    jlong nativeBufferViewportList);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeGetScreenBufferViewports(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext,
+    jlong nativeBufferViewportList);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeGetMaximumEffectiveRenderTargetSize(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext,
+    jobject size);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeGetScreenTargetSize(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext,
+    jobject size);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeDistortToScreen(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext,
+    jint textureId,
+    jlong nativeBufferViewportList,
+    jfloatArray pose,
+    jlong timeNs);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeSetDefaultFramebufferActive(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) jobject
+Java_com_google_vr_ndk_base_GvrApi_nativeRenderReprojectionThread(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeOnPauseReprojectionThread(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeUpdateSurfaceReprojectionThread(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext,
+    jint surfaceId,
+    jint textureId,
+    jlong timestamp,
+    jfloatArray transformMatrix);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeRemoveAllSurfacesReprojectionThread(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeGetHeadSpaceFromStartSpaceRotation(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext,
+    jfloatArray outPose,
+    jlong timeNs);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeSetIgnoreManualPauseResumeTracker(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext,
+    jboolean shouldIgnore);
+
+extern "C" __attribute__((visibility("default"))) jbyteArray
+Java_com_google_vr_ndk_base_GvrApi_nativePauseTracking(JNIEnv* env,
+                                                       jobject jcaller,
+                                                       jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeResumeTracking(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext,
+    jbyteArray trackerStateBytes);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeResetTracking(JNIEnv* env,
+                                                       jobject jcaller,
+                                                       jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeRecenterTracking(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeGetEyeFromHeadMatrix(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext,
+    jint eye,
+    jfloatArray out);
+
+extern "C" __attribute__((visibility("default"))) jintArray
+Java_com_google_vr_ndk_base_GvrApi_nativeGetWindowBounds(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) jfloatArray
+Java_com_google_vr_ndk_base_GvrApi_nativeComputeDistortedPoint(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext,
+    jint eyeType,
+    jfloatArray uvIn);
+
+extern "C" __attribute__((visibility("default"))) jboolean
+Java_com_google_vr_ndk_base_GvrApi_nativeSetDefaultViewerProfile(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext,
+    jstring viewerProfileUri);
+
+extern "C" __attribute__((visibility("default"))) jstring
+Java_com_google_vr_ndk_base_GvrApi_nativeGetViewerVendor(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) jstring
+Java_com_google_vr_ndk_base_GvrApi_nativeGetViewerModel(JNIEnv* env,
+                                                        jobject jcaller,
+                                                        jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) jint
+Java_com_google_vr_ndk_base_GvrApi_nativeGetViewerType(JNIEnv* env,
+                                                       jobject jcaller,
+                                                       jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) jboolean
+Java_com_google_vr_ndk_base_GvrApi_nativeSetAsyncReprojectionEnabled(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext,
+    jboolean enabled);
+
+extern "C" __attribute__((visibility("default"))) jboolean
+Java_com_google_vr_ndk_base_GvrApi_nativeGetAsyncReprojectionEnabled(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeReconnectSensors(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) jboolean
+Java_com_google_vr_ndk_base_GvrApi_nativeSetViewerParams(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext,
+    jbyteArray serializedViewerParams);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeSetDisplayMetrics(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext,
+    jint widthPixels,
+    jint heightPixels,
+    jfloat xDpi,
+    jfloat yDpi);
+
+extern "C" __attribute__((visibility("default"))) jfloat
+Java_com_google_vr_ndk_base_GvrApi_nativeGetBorderSizeMeters(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeSetSurfaceSize(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext,
+    jint surfaceWidthPixels,
+    jint surfaceHeightPixels);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeSetLensOffset(JNIEnv* env,
+                                                       jobject jcaller,
+                                                       jlong nativeGvrContext,
+                                                       jfloat x,
+                                                       jfloat y);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_ndk_base_GvrApi_nativeDumpDebugData(JNIEnv* env,
+                                                       jobject jcaller,
+                                                       jlong nativeGvrContext);
+
+extern "C" __attribute__((visibility("default"))) jboolean
+Java_com_google_vr_ndk_base_GvrApi_nativeUsingVrDisplayService(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeGvrContext);
+
+// Step 3: RegisterNatives.
+
+static const JNINativeMethod kMethodsGvrApi[] = {
+    {"nativeBufferViewportListCreate",
+     "("
+     "J"
+     ")"
+     "J",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportListCreate)},
+    {"nativeBufferViewportListDestroy",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportListDestroy)},
+    {"nativeBufferViewportListGetSize",
+     "("
+     "J"
+     ")"
+     "I",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportListGetSize)},
+    {"nativeBufferViewportListGetItem",
+     "("
+     "J"
+     "I"
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportListGetItem)},
+    {"nativeBufferViewportListSetItem",
+     "("
+     "J"
+     "I"
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportListSetItem)},
+    {"nativeBufferViewportCreate",
+     "("
+     "J"
+     ")"
+     "J",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportCreate)},
+    {"nativeBufferViewportDestroy",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportDestroy)},
+    {"nativeBufferViewportGetSourceUv",
+     "("
+     "J"
+     "Landroid/graphics/RectF;"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportGetSourceUv)},
+    {"nativeBufferViewportSetSourceUv",
+     "("
+     "J"
+     "F"
+     "F"
+     "F"
+     "F"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportSetSourceUv)},
+    {"nativeBufferViewportGetSourceFov",
+     "("
+     "J"
+     "Landroid/graphics/RectF;"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportGetSourceFov)},
+    {"nativeBufferViewportSetSourceFov",
+     "("
+     "J"
+     "F"
+     "F"
+     "F"
+     "F"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportSetSourceFov)},
+    {"nativeBufferViewportGetTransform",
+     "("
+     "J"
+     "[F"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportGetTransform)},
+    {"nativeBufferViewportSetTransform",
+     "("
+     "J"
+     "[F"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportSetTransform)},
+    {"nativeBufferViewportEqual",
+     "("
+     "J"
+     "J"
+     ")"
+     "Z",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportEqual)},
+    {"nativeBufferViewportGetTargetEye",
+     "("
+     "J"
+     ")"
+     "I",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportGetTargetEye)},
+    {"nativeBufferViewportSetTargetEye",
+     "("
+     "J"
+     "I"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportSetTargetEye)},
+    {"nativeBufferViewportGetSourceBufferIndex",
+     "("
+     "J"
+     ")"
+     "I",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportGetSourceBufferIndex)},
+    {"nativeBufferViewportSetSourceBufferIndex",
+     "("
+     "J"
+     "I"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportSetSourceBufferIndex)},
+    {"nativeBufferViewportGetExternalSurfaceId",
+     "("
+     "J"
+     ")"
+     "I",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportGetExternalSurfaceId)},
+    {"nativeBufferViewportSetExternalSurfaceId",
+     "("
+     "J"
+     "I"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportSetExternalSurfaceId)},
+    {"nativeBufferViewportGetReprojection",
+     "("
+     "J"
+     ")"
+     "I",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportGetReprojection)},
+    {"nativeBufferViewportSetReprojection",
+     "("
+     "J"
+     "I"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferViewportSetReprojection)},
+    {"nativeBufferSpecCreate",
+     "("
+     "J"
+     ")"
+     "J",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferSpecCreate)},
+    {"nativeBufferSpecDestroy",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferSpecDestroy)},
+    {"nativeBufferSpecGetSize",
+     "("
+     "J"
+     "Landroid/graphics/Point;"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferSpecGetSize)},
+    {"nativeBufferSpecSetSize",
+     "("
+     "J"
+     "I"
+     "I"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferSpecSetSize)},
+    {"nativeBufferSpecSetColorFormat",
+     "("
+     "J"
+     "I"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferSpecSetColorFormat)},
+    {"nativeBufferSpecSetDepthStencilFormat",
+     "("
+     "J"
+     "I"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferSpecSetDepthStencilFormat)},
+    {"nativeBufferSpecGetSamples",
+     "("
+     "J"
+     ")"
+     "I",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferSpecGetSamples)},
+    {"nativeBufferSpecSetSamples",
+     "("
+     "J"
+     "I"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeBufferSpecSetSamples)},
+    {"nativeSwapChainCreate",
+     "("
+     "J"
+     "[J"
+     ")"
+     "J",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeSwapChainCreate)},
+    {"nativeSwapChainDestroy",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeSwapChainDestroy)},
+    {"nativeSwapChainGetBufferCount",
+     "("
+     "J"
+     ")"
+     "I",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeSwapChainGetBufferCount)},
+    {"nativeSwapChainGetBufferSize",
+     "("
+     "J"
+     "I"
+     "Landroid/graphics/Point;"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeSwapChainGetBufferSize)},
+    {"nativeSwapChainResizeBuffer",
+     "("
+     "J"
+     "I"
+     "I"
+     "I"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeSwapChainResizeBuffer)},
+    {"nativeSwapChainAcquireFrame",
+     "("
+     "J"
+     ")"
+     "J",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeSwapChainAcquireFrame)},
+    {"nativeFrameBindBuffer",
+     "("
+     "J"
+     "I"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeFrameBindBuffer)},
+    {"nativeFrameUnbind",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeFrameUnbind)},
+    {"nativeFrameGetFramebufferObject",
+     "("
+     "J"
+     "I"
+     ")"
+     "I",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeFrameGetFramebufferObject)},
+    {"nativeFrameGetBufferSize",
+     "("
+     "J"
+     "I"
+     "Landroid/graphics/Point;"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeFrameGetBufferSize)},
+    {"nativeFrameSubmit",
+     "("
+     "J"
+     "J"
+     "[F"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeFrameSubmit)},
+    {"nativeCreate",
+     "("
+     "Ljava/lang/ClassLoader;"
+     "Landroid/content/Context;"
+     "J"
+     "I"
+     "I"
+     "F"
+     "F"
+     "Lcom/google/vr/ndk/base/GvrApi$PoseTracker;"
+     ")"
+     "J",
+     reinterpret_cast<void*>(Java_com_google_vr_ndk_base_GvrApi_nativeCreate)},
+    {"nativeGetError",
+     "("
+     "J"
+     ")"
+     "I",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeGetError)},
+    {"nativeClearError",
+     "("
+     "J"
+     ")"
+     "I",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeClearError)},
+    {"nativeGetErrorString",
+     "("
+     "I"
+     ")"
+     "Ljava/lang/String;",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeGetErrorString)},
+    {"nativeGetUserPrefs",
+     "("
+     "J"
+     ")"
+     "J",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeGetUserPrefs)},
+    {"nativeUserPrefsGetControllerHandedness",
+     "("
+     "J"
+     ")"
+     "I",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeUserPrefsGetControllerHandedness)},
+    {"nativePause",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(Java_com_google_vr_ndk_base_GvrApi_nativePause)},
+    {"nativeResume",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(Java_com_google_vr_ndk_base_GvrApi_nativeResume)},
+    {"nativeReleaseGvrContext",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeReleaseGvrContext)},
+    {"nativeInitializeGl",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeInitializeGl)},
+    {"nativeOnSurfaceCreatedReprojectionThread",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeOnSurfaceCreatedReprojectionThread)},
+    {"nativeGetRecommendedBufferViewports",
+     "("
+     "J"
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeGetRecommendedBufferViewports)},
+    {"nativeGetScreenBufferViewports",
+     "("
+     "J"
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeGetScreenBufferViewports)},
+    {"nativeGetMaximumEffectiveRenderTargetSize",
+     "("
+     "J"
+     "Landroid/graphics/Point;"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeGetMaximumEffectiveRenderTargetSize)},
+    {"nativeGetScreenTargetSize",
+     "("
+     "J"
+     "Landroid/graphics/Point;"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeGetScreenTargetSize)},
+    {"nativeDistortToScreen",
+     "("
+     "J"
+     "I"
+     "J"
+     "[F"
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeDistortToScreen)},
+    {"nativeSetDefaultFramebufferActive",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeSetDefaultFramebufferActive)},
+    {"nativeRenderReprojectionThread",
+     "("
+     "J"
+     ")"
+     "Landroid/graphics/Point;",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeRenderReprojectionThread)},
+    {"nativeOnPauseReprojectionThread",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeOnPauseReprojectionThread)},
+    {"nativeUpdateSurfaceReprojectionThread",
+     "("
+     "J"
+     "I"
+     "I"
+     "J"
+     "[F"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeUpdateSurfaceReprojectionThread)},
+    {"nativeRemoveAllSurfacesReprojectionThread",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeRemoveAllSurfacesReprojectionThread)},
+    {"nativeGetHeadSpaceFromStartSpaceRotation",
+     "("
+     "J"
+     "[F"
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeGetHeadSpaceFromStartSpaceRotation)},
+    {"nativeSetIgnoreManualPauseResumeTracker",
+     "("
+     "J"
+     "Z"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeSetIgnoreManualPauseResumeTracker)},
+    {"nativePauseTracking",
+     "("
+     "J"
+     ")"
+     "[B",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativePauseTracking)},
+    {"nativeResumeTracking",
+     "("
+     "J"
+     "[B"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeResumeTracking)},
+    {"nativeResetTracking",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeResetTracking)},
+    {"nativeRecenterTracking",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeRecenterTracking)},
+    {"nativeGetEyeFromHeadMatrix",
+     "("
+     "J"
+     "I"
+     "[F"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeGetEyeFromHeadMatrix)},
+    {"nativeGetWindowBounds",
+     "("
+     "J"
+     ")"
+     "[I",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeGetWindowBounds)},
+    {"nativeComputeDistortedPoint",
+     "("
+     "J"
+     "I"
+     "[F"
+     ")"
+     "[F",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeComputeDistortedPoint)},
+    {"nativeSetDefaultViewerProfile",
+     "("
+     "J"
+     "Ljava/lang/String;"
+     ")"
+     "Z",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeSetDefaultViewerProfile)},
+    {"nativeGetViewerVendor",
+     "("
+     "J"
+     ")"
+     "Ljava/lang/String;",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeGetViewerVendor)},
+    {"nativeGetViewerModel",
+     "("
+     "J"
+     ")"
+     "Ljava/lang/String;",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeGetViewerModel)},
+    {"nativeGetViewerType",
+     "("
+     "J"
+     ")"
+     "I",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeGetViewerType)},
+    {"nativeSetAsyncReprojectionEnabled",
+     "("
+     "J"
+     "Z"
+     ")"
+     "Z",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeSetAsyncReprojectionEnabled)},
+    {"nativeGetAsyncReprojectionEnabled",
+     "("
+     "J"
+     ")"
+     "Z",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeGetAsyncReprojectionEnabled)},
+    {"nativeReconnectSensors",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeReconnectSensors)},
+    {"nativeSetViewerParams",
+     "("
+     "J"
+     "[B"
+     ")"
+     "Z",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeSetViewerParams)},
+    {"nativeSetDisplayMetrics",
+     "("
+     "J"
+     "I"
+     "I"
+     "F"
+     "F"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeSetDisplayMetrics)},
+    {"nativeGetBorderSizeMeters",
+     "("
+     "J"
+     ")"
+     "F",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeGetBorderSizeMeters)},
+    {"nativeSetSurfaceSize",
+     "("
+     "J"
+     "I"
+     "I"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeSetSurfaceSize)},
+    {"nativeSetLensOffset",
+     "("
+     "J"
+     "F"
+     "F"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeSetLensOffset)},
+    {"nativeDumpDebugData",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeDumpDebugData)},
+    {"nativeUsingVrDisplayService",
+     "("
+     "J"
+     ")"
+     "Z",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_ndk_base_GvrApi_nativeUsingVrDisplayService)},
+};
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+  if (base::android::IsManualJniRegistrationDisabled())
+    return true;
+
+  const int kMethodsGvrApiSize = arraysize(kMethodsGvrApi);
+
+  if (env->RegisterNatives(GvrApi_clazz(env), kMethodsGvrApi,
+                           kMethodsGvrApiSize) < 0) {
+    jni_generator::HandleRegistrationError(env, GvrApi_clazz(env), __FILE__);
+    return false;
+  }
+
+  return true;
+}
+
+static bool RegisterGvrApiNatives(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+}  // namespace GvrApi
+
+#endif  // com_google_vr_ndk_base_GvrApi_JNI
diff --git a/third_party/gvr-android-sdk/libgvr_shim_static_arm.a.sha1 b/third_party/gvr-android-sdk/libgvr_shim_static_arm.a.sha1
new file mode 100644
index 0000000..3557bd51
--- /dev/null
+++ b/third_party/gvr-android-sdk/libgvr_shim_static_arm.a.sha1
@@ -0,0 +1 @@
+4a1c59b99f0501f7fc5c924db69220895cec9fb9
\ No newline at end of file
diff --git a/third_party/gvr-android-sdk/libgvr_shim_static_arm64.a.sha1 b/third_party/gvr-android-sdk/libgvr_shim_static_arm64.a.sha1
new file mode 100644
index 0000000..93f5ee95
--- /dev/null
+++ b/third_party/gvr-android-sdk/libgvr_shim_static_arm64.a.sha1
@@ -0,0 +1 @@
+c45df3493989b45d176c583939d83ac1ade5ab23
\ No newline at end of file
diff --git a/third_party/gvr-android-sdk/native_callbacks_jni.h b/third_party/gvr-android-sdk/native_callbacks_jni.h
new file mode 100644
index 0000000..7bbec0a
--- /dev/null
+++ b/third_party/gvr-android-sdk/native_callbacks_jni.h
@@ -0,0 +1,289 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is autogenerated by
+//     base/android/jni_generator/jni_generator.py
+// For
+//     com/google/vr/internal/controller/NativeCallbacks
+
+// Local modification includes:
+// 1. Remove all implementaiton, only keep definition.
+// 2. Use absolute path instead of relative path.
+// 3. Removed all helper functions such as: Create.
+// 4. Replace all nativeHandle to handle. This is because jni_generator.py
+// require jni functions start with "native" prefix. So we add the prefix to
+// generate the file. But the real jni functions in the static library
+// doesn't have the prefix.
+// 5. Added function RegisterNativeCallbacksNatives at the end of this file.
+
+#ifndef com_google_vr_internal_controller_NativeCallbacks_JNI
+#define com_google_vr_internal_controller_NativeCallbacks_JNI
+
+#include "base/android/jni_android.h"
+// ----------------------------------------------------------------------------
+// Native JNI methods
+// ----------------------------------------------------------------------------
+#include <jni.h>
+
+#include "base/android/jni_generator/jni_generator_helper.h"
+
+#include "base/android/jni_int_wrapper.h"
+
+// Step 1: forward declarations.
+namespace {
+const char kNativeCallbacksClassPath[] =
+    "com/google/vr/internal/controller/NativeCallbacks";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+base::subtle::AtomicWord g_NativeCallbacks_clazz __attribute__((unused)) = 0;
+#define NativeCallbacks_clazz(env)                            \
+  base::android::LazyGetClass(env, kNativeCallbacksClassPath, \
+                              &g_NativeCallbacks_clazz)
+
+}  // namespace
+
+namespace NativeCallbacks {
+
+// Step 2: method stubs.
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_internal_controller_NativeCallbacks_handleStateChanged(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong userData,
+    jint controllerId,
+    jint newState);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_internal_controller_NativeCallbacks_handleControllerRecentered(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong userData,
+    jlong timestampNanos,
+    jfloat qx,
+    jfloat qy,
+    jfloat qz,
+    jfloat qw);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_internal_controller_NativeCallbacks_handleTouchEvent(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong userData,
+    jlong timestampNanos,
+    jint action,
+    jfloat x,
+    jfloat y);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_internal_controller_NativeCallbacks_handleOrientationEvent(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong userData,
+    jlong timestampNanos,
+    jfloat qx,
+    jfloat qy,
+    jfloat qz,
+    jfloat qw);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_internal_controller_NativeCallbacks_handleButtonEvent(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong userData,
+    jlong timestampNanos,
+    jint buttonCode,
+    jboolean down);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_internal_controller_NativeCallbacks_handleAccelEvent(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong userData,
+    jlong timestampNanos,
+    jfloat x,
+    jfloat y,
+    jfloat z);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_internal_controller_NativeCallbacks_handleGyroEvent(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong userData,
+    jlong timestampNanos,
+    jfloat x,
+    jfloat y,
+    jfloat z);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_internal_controller_NativeCallbacks_handleServiceInitFailed(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong userData,
+    jint failureReason);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_internal_controller_NativeCallbacks_handleServiceFailed(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong userData);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_internal_controller_NativeCallbacks_handleServiceUnavailable(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong userData);
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_internal_controller_NativeCallbacks_handleServiceConnected(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong userData,
+    jint flags);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_internal_controller_NativeCallbacks_handleServiceDisconnected(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong userData);
+
+// Step 3: RegisterNatives.
+
+static const JNINativeMethod kMethodsNativeCallbacks[] = {
+    {"handleStateChanged",
+     "("
+     "J"
+     "I"
+     "I"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_internal_controller_NativeCallbacks_handleStateChanged)},
+    {"handleControllerRecentered",
+     "("
+     "J"
+     "J"
+     "F"
+     "F"
+     "F"
+     "F"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_internal_controller_NativeCallbacks_handleControllerRecentered)},
+    {"handleTouchEvent",
+     "("
+     "J"
+     "J"
+     "I"
+     "F"
+     "F"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_internal_controller_NativeCallbacks_handleTouchEvent)},
+    {"handleOrientationEvent",
+     "("
+     "J"
+     "J"
+     "F"
+     "F"
+     "F"
+     "F"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_internal_controller_NativeCallbacks_handleOrientationEvent)},
+    {"handleButtonEvent",
+     "("
+     "J"
+     "J"
+     "I"
+     "Z"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_internal_controller_NativeCallbacks_handleButtonEvent)},
+    {"handleAccelEvent",
+     "("
+     "J"
+     "J"
+     "F"
+     "F"
+     "F"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_internal_controller_NativeCallbacks_handleAccelEvent)},
+    {"handleGyroEvent",
+     "("
+     "J"
+     "J"
+     "F"
+     "F"
+     "F"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_internal_controller_NativeCallbacks_handleGyroEvent)},
+    {"handleServiceInitFailed",
+     "("
+     "J"
+     "I"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_internal_controller_NativeCallbacks_handleServiceInitFailed)},
+    {"handleServiceFailed",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_internal_controller_NativeCallbacks_handleServiceFailed)},
+    {"handleServiceUnavailable",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_internal_controller_NativeCallbacks_handleServiceUnavailable)},
+    {"handleServiceConnected",
+     "("
+     "J"
+     "I"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_internal_controller_NativeCallbacks_handleServiceConnected)},
+    {"handleServiceDisconnected",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_internal_controller_NativeCallbacks_handleServiceDisconnected)},
+};
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+  if (base::android::IsManualJniRegistrationDisabled())
+    return true;
+
+  const int kMethodsNativeCallbacksSize = arraysize(kMethodsNativeCallbacks);
+
+  if (env->RegisterNatives(NativeCallbacks_clazz(env), kMethodsNativeCallbacks,
+                           kMethodsNativeCallbacksSize) < 0) {
+    jni_generator::HandleRegistrationError(env, NativeCallbacks_clazz(env),
+                                           __FILE__);
+    return false;
+  }
+
+  return true;
+}
+
+static bool RegisterNativeCallbacksNatives(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+}  // namespace NativeCallbacks
+
+#endif  // com_google_vr_internal_controller_NativeCallbacks_JNI
diff --git a/tools/android/BUILD.gn b/tools/android/BUILD.gn
index c88cd08..fc667ef2 100644
--- a/tools/android/BUILD.gn
+++ b/tools/android/BUILD.gn
@@ -7,6 +7,7 @@
 group("android_tools") {
   deps = [
     "//build/android:wrapper_scripts",
+    "//build/android/pylib/device/commands",
     "//third_party/android_platform:stack_wrapper",
     "//third_party/catapult/telemetry:bitmaptools($host_toolchain)",
     "//tools/android/adb_reboot",
diff --git a/tools/gn/build_settings.cc b/tools/gn/build_settings.cc
index 14f1aa8..3fac976 100644
--- a/tools/gn/build_settings.cc
+++ b/tools/gn/build_settings.cc
@@ -9,8 +9,7 @@
 #include "base/files/file_util.h"
 #include "tools/gn/filesystem_utils.h"
 
-BuildSettings::BuildSettings()
-    : check_for_bad_items_(true) {
+BuildSettings::BuildSettings() {
 }
 
 BuildSettings::BuildSettings(const BuildSettings& other)
@@ -20,8 +19,7 @@
       python_path_(other.python_path_),
       build_config_file_(other.build_config_file_),
       build_dir_(other.build_dir_),
-      build_args_(other.build_args_),
-      check_for_bad_items_(true) {
+      build_args_(other.build_args_) {
 }
 
 BuildSettings::~BuildSettings() {
diff --git a/tools/gn/build_settings.h b/tools/gn/build_settings.h
index 5424cf9..0b986cd 100644
--- a/tools/gn/build_settings.h
+++ b/tools/gn/build_settings.h
@@ -95,18 +95,6 @@
     exec_script_whitelist_ = std::move(list);
   }
 
-  // When set (the default), code should perform normal validation of inputs
-  // and structures, like undefined or possibly incorrectly used things. For
-  // some interrogation commands, we don't care about this and actually want
-  // to allow the user to check the structure of the build to solve their
-  // problem, and these checks are undesirable.
-  bool check_for_bad_items() const {
-    return check_for_bad_items_;
-  }
-  void set_check_for_bad_items(bool c) {
-    check_for_bad_items_ = c;
-  }
-
  private:
   base::FilePath root_path_;
   std::string root_path_utf8_;
@@ -122,8 +110,6 @@
 
   std::unique_ptr<std::set<SourceFile>> exec_script_whitelist_;
 
-  bool check_for_bad_items_;
-
   BuildSettings& operator=(const BuildSettings& other);  // Disallow.
 };
 
diff --git a/tools/gn/command_analyze.cc b/tools/gn/command_analyze.cc
index ee3774c..6fd5823 100644
--- a/tools/gn/command_analyze.cc
+++ b/tools/gn/command_analyze.cc
@@ -107,7 +107,6 @@
   }
 
   Setup* setup = new Setup;
-  setup->build_settings().set_check_for_bad_items(false);
   if (!setup->DoSetup(args[0], false) || !setup->Run())
     return 1;
 
diff --git a/tools/gn/command_args.cc b/tools/gn/command_args.cc
index 374a274..1b81135b 100644
--- a/tools/gn/command_args.cc
+++ b/tools/gn/command_args.cc
@@ -118,7 +118,6 @@
 
 int ListArgs(const std::string& build_dir) {
   Setup* setup = new Setup;
-  setup->build_settings().set_check_for_bad_items(false);
   if (!setup->DoSetup(build_dir, false) || !setup->Run())
     return 1;
 
@@ -231,7 +230,6 @@
     // Scope the setup. We only use it for some basic state. We'll do the
     // "real" build below in the gen command.
     Setup setup;
-    setup.build_settings().set_check_for_bad_items(false);
     // Don't fill build arguments. We're about to edit the file which supplies
     // these in the first place.
     setup.set_fill_arguments(false);
diff --git a/tools/gn/command_desc.cc b/tools/gn/command_desc.cc
index 2257221..9f7f903 100644
--- a/tools/gn/command_desc.cc
+++ b/tools/gn/command_desc.cc
@@ -430,7 +430,6 @@
 
   // Deliberately leaked to avoid expensive process teardown.
   Setup* setup = new Setup;
-  setup->build_settings().set_check_for_bad_items(false);
   if (!setup->DoSetup(args[0], false))
     return 1;
   if (!setup->Run())
diff --git a/tools/gn/command_ls.cc b/tools/gn/command_ls.cc
index 676ff57..58b9c875 100644
--- a/tools/gn/command_ls.cc
+++ b/tools/gn/command_ls.cc
@@ -74,7 +74,6 @@
   }
 
   Setup* setup = new Setup;
-  setup->build_settings().set_check_for_bad_items(false);
   if (!setup->DoSetup(args[0], false) || !setup->Run())
     return 1;
 
diff --git a/tools/gn/command_refs.cc b/tools/gn/command_refs.cc
index 6fa0146..4d7ad7b5 100644
--- a/tools/gn/command_refs.cc
+++ b/tools/gn/command_refs.cc
@@ -403,7 +403,6 @@
   bool all_toolchains = cmdline->HasSwitch(switches::kAllToolchains);
 
   Setup* setup = new Setup;
-  setup->build_settings().set_check_for_bad_items(false);
   if (!setup->DoSetup(args[0], false) || !setup->Run())
     return 1;
 
diff --git a/tools/gn/docs/cookbook.md b/tools/gn/docs/cookbook.md
index 0f3d1018..9a1b259 100644
--- a/tools/gn/docs/cookbook.md
+++ b/tools/gn/docs/cookbook.md
@@ -274,7 +274,6 @@
 | `use_ash` (0/1)                         | `use_ash` (true/false)                         | `//build/config/ui.gni`       |
 | `use_athena` (0/1)                      | `use_athena` (true/false)                      | `//build/config/ui.gni`       |
 | `use_aura` (0/1)                        | `use_aura` (true/false)                        | `//build/config/ui.gni`       |
-| `use_brlapi` (0/1)                      | `use_brlapi` (true/false)                      | `//build/config/features.gni` |
 | `use_cairo` (0/1)                       | `use_cairo` (true/false)                       | `//build/config/ui.gni`       |
 | `use_clipboard_aurax11` (0/1)           | `use_aura && use_x11`                          |                               |
 | `use_cups` (0/1)                        | `use_cups` (true/false)                        | `//build/config/features.gni` |
diff --git a/tools/gn/setup.cc b/tools/gn/setup.cc
index c5eace2..8a433bc 100644
--- a/tools/gn/setup.cc
+++ b/tools/gn/setup.cc
@@ -330,22 +330,20 @@
 
 bool Setup::RunPostMessageLoop() {
   Err err;
-  if (build_settings_.check_for_bad_items()) {
-    if (!builder_.CheckForBadItems(&err)) {
-      err.PrintToStdout();
-      return false;
-    }
+  if (!builder_.CheckForBadItems(&err)) {
+    err.PrintToStdout();
+    return false;
+  }
 
-    if (!build_settings_.build_args().VerifyAllOverridesUsed(&err)) {
-      // TODO(brettw) implement a system to have a different marker for
-      // warnings. Until we have a better system, print the error but don't
-      // return failure unless requested on the command line.
-      err.PrintToStdout();
-      if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-              switches::kFailOnUnusedArgs))
-        return false;
-      return true;
-    }
+  if (!build_settings_.build_args().VerifyAllOverridesUsed(&err)) {
+    // TODO(brettw) implement a system to have a different marker for
+    // warnings. Until we have a better system, print the error but don't
+    // return failure unless requested on the command line.
+    err.PrintToStdout();
+    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+            switches::kFailOnUnusedArgs))
+      return false;
+    return true;
   }
 
   if (check_public_headers_) {
diff --git a/tools/gn/target.cc b/tools/gn/target.cc
index aafdc4fe..49de35d 100644
--- a/tools/gn/target.cc
+++ b/tools/gn/target.cc
@@ -300,15 +300,13 @@
 
   FillOutputFiles();
 
-  if (settings()->build_settings()->check_for_bad_items()) {
-    if (!CheckVisibility(err))
-      return false;
-    if (!CheckTestonly(err))
-      return false;
-    if (!CheckAssertNoDeps(err))
-      return false;
-    CheckSourcesGenerated();
-  }
+  if (!CheckVisibility(err))
+    return false;
+  if (!CheckTestonly(err))
+    return false;
+  if (!CheckAssertNoDeps(err))
+    return false;
+  CheckSourcesGenerated();
 
   if (!write_runtime_deps_output_.value().empty())
     g_scheduler->AddWriteRuntimeDepsTarget(this);
diff --git a/tools/grit/grit_rule.gni b/tools/grit/grit_rule.gni
index 694f1ca..532f4a9 100644
--- a/tools/grit/grit_rule.gni
+++ b/tools/grit/grit_rule.gni
@@ -186,13 +186,6 @@
   ]
 }
 
-if (enable_image_loader_extension) {
-  grit_defines += [
-    "-D",
-    "image_loader_extension",
-  ]
-}
-
 if (is_android) {
   grit_defines += [
     "-E",
@@ -241,12 +234,6 @@
   }
 }
 
-if (enable_extensions) {
-  grit_defines += [
-    "-D",
-    "enable_extensions",
-  ]
-}
 if (enable_media_router) {
   grit_defines += [
     "-D",
diff --git a/tools/json_schema_compiler/json_features.gni b/tools/json_schema_compiler/json_features.gni
index cdf6c33a..ab940f7 100644
--- a/tools/json_schema_compiler/json_features.gni
+++ b/tools/json_schema_compiler/json_features.gni
@@ -8,6 +8,7 @@
 #   feature_class: The name of the feature class to generate, e.g. APIFeature.
 #   provider_class: The name of the provider class to generate, e.g.
 #                   APIFeatureProvider.
+#   deps/public_deps/visibility: normal meaning
 template("json_features") {
   assert(defined(invoker.sources),
          "\"sources\" must be defined for the $target_name template.")
@@ -21,12 +22,14 @@
   compiler_root = "//tools/json_schema_compiler"
   base_filename = target_name
   action_name = "${target_name}_json_features"
+  source_set_name = target_name
   generated_files = [
     "$target_gen_dir/$base_filename.cc",
     "$target_gen_dir/$base_filename.h",
   ]
 
   action(action_name) {
+    visibility = [ ":$source_set_name" ]
     sources = invoker.sources
     script = "$compiler_root/feature_compiler.py"
     inputs = [
@@ -42,12 +45,34 @@
              rebase_path(target_gen_dir, root_build_dir),
              "$base_filename",
            ] + rebased
+
+    # Add the deps in for the action as well, in case the deps generate the
+    # inputs used by the action.
+    forward_variables_from(invoker,
+                           [
+                             "deps",
+                             "public_deps",
+                           ])
+
+    # Append a dependency on the extensions system. Headers in this target
+    # are included by the feature compiler automatically.
+    if (!defined(deps)) {
+      deps = []
+    }
+    deps += [ "//extensions/common" ]
   }
 
   source_set(target_name) {
     sources = generated_files
-    public_deps = [
-      ":$action_name",
-    ]
+    forward_variables_from(invoker,
+                           [
+                             "deps",
+                             "public_deps",
+                             "visibility",
+                           ])
+    if (!defined(public_deps)) {
+      public_deps = []
+    }
+    public_deps += [ ":$action_name" ]
   }
 }
diff --git a/tools/json_schema_compiler/test/BUILD.gn b/tools/json_schema_compiler/test/BUILD.gn
index fe124f0e..95a7a87 100644
--- a/tools/json_schema_compiler/test/BUILD.gn
+++ b/tools/json_schema_compiler/test/BUILD.gn
@@ -4,8 +4,11 @@
 
 import("//build/config/features.gni")
 import("//build/json_schema_api.gni")
+import("//extensions/features/features.gni")
 import("//tools/json_schema_compiler/json_features.gni")
 
+assert(enable_extensions)
+
 json_schema_api("api") {
   visibility = [ ":*" ]
 
@@ -32,22 +35,37 @@
 
   schemas = true
   root_namespace = "test::api::%(namespace)s"
+
+  deps = [
+    "//extensions/features",
+  ]
 }
 
-if (enable_extensions) {
-  json_features("features_compiler_test") {
-    feature_class = "APIFeature"
-    provider_class = "CompilerTestFeatureProvider"
-    sources = [
-      "features_test.json",
-      "features_test2.json",
-    ]
-  }
+json_features("features_compiler_test") {
+  feature_class = "APIFeature"
+  provider_class = "CompilerTestFeatureProvider"
+  sources = [
+    "features_test.json",
+    "features_test2.json",
+  ]
 }
 
-source_set("schema_test") {
+source_set("unit_tests") {
   testonly = true
   sources = [
+    "additional_properties_unittest.cc",
+    "any_unittest.cc",
+    "arrays_unittest.cc",
+    "callbacks_unittest.cc",
+    "choices_unittest.cc",
+    "crossref_unittest.cc",
+    "enums_unittest.cc",
+    "error_generation_unittest.cc",
+    "functions_as_parameters_unittest.cc",
+    "functions_on_types_unittest.cc",
+    "idl_schemas_unittest.cc",
+    "objects_unittest.cc",
+    "simple_api_unittest.cc",
     "test_util.cc",
     "test_util.h",
   ]
@@ -56,16 +74,12 @@
   configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
   public_deps = [
+    ":features_compiler_test",
+  ]
+
+  deps = [
     ":api",
     "//base",
+    "//testing/gtest",
   ]
 }
-
-if (enable_extensions) {
-  source_set("features_generation_test") {
-    testonly = true
-    public_deps = [
-      ":features_compiler_test",
-    ]
-  }
-}
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index f2c8cd2..144223d 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -153,7 +153,7 @@
       'CrWinAsanCov': 'asan_clang_edge_fuzzer_static_v8_heap_x86_full_symbols_release',
       'CrWinClang(shared)': 'clang_minimal_symbols_shared_release_bot_x86',
       'CrWinClang64(dbg)': 'win_clang_debug_bot',
-      'CrWinClangLLD': 'clang_tot_minimal_symbols_shared_release_use_lld_x86',
+      'CrWinClangLLD': 'clang_tot_official_minimal_symbols_static_use_lld_x86',
       'CrWinClangLLD64': 'clang_tot_minimal_symbols_shared_release_use_lld',
       'CrWinClngLLD64dbg': 'clang_tot_minimal_symbols_shared_debug_use_lld',
       'CrWinClngLLDdbg': 'clang_tot_minimal_symbols_shared_debug_use_lld_x86',
@@ -532,6 +532,7 @@
       'linux_chromium_gn_upload': 'gn_linux_upload',
       'linux_chromium_headless_dbg': '//build/args/bots/tryserver.chromium.linux/linux_chromium_headless_dbg.gn',
       'linux_chromium_headless_rel': '//build/args/bots/tryserver.chromium.linux/linux_chromium_headless_rel.gn',
+      'linux_chromium_ozone_compile_only_ng': 'ozone_linux_release_trybot',
 
       # This is intentionally a release_bot and not a release_trybot;
       # enabling DCHECKs seems to cause flaky failures that don't show up
@@ -1075,8 +1076,8 @@
       'clang_tot', 'minimal_symbols', 'shared', 'release', 'use_lld',
     ],
 
-    'clang_tot_minimal_symbols_shared_release_use_lld_x86': [
-      'clang_tot', 'minimal_symbols', 'shared', 'release', 'use_lld', 'x86',
+    'clang_tot_official_minimal_symbols_static_use_lld_x86': [
+      'clang_tot', 'official', 'minimal_symbols', 'static', 'release', 'use_lld', 'x86',
     ],
 
     'clang_tot_minimal_symbols_shared_release': [
@@ -1320,6 +1321,10 @@
       'release_bot', 'ozone', 'ozone_linux',
     ],
 
+    'ozone_linux_release_trybot': [
+      'release_trybot', 'ozone', 'ozone_linux',
+    ],
+
     'release_afl_asan': [
       'release', 'afl', 'asan', 'chromeos_codecs', 'pdf_xfa', 'disable_nacl',
     ],
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index ac05428..fab6407 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -25554,6 +25554,11 @@
   </summary>
 </histogram>
 
+<histogram name="Memory.Coordinator.StateDuration" units="seconds">
+  <owner>bashi@chromium.org</owner>
+  <summary>Time elapsed between the global state changes.</summary>
+</histogram>
+
 <histogram name="Memory.Coordinator.StateOnCriticalNotificationReceived"
     enum="MemoryState">
   <owner>bashi@chromium.org</owner>
@@ -25572,6 +25577,14 @@
   </summary>
 </histogram>
 
+<histogram name="Memory.Coordinator.TotalPrivate" units="MB">
+  <owner>bashi@chromium.org</owner>
+  <summary>
+    The total private working set memory used by the browser and renderer
+    processes when the memory coordinator changes the global memory state.
+  </summary>
+</histogram>
+
 <histogram name="Memory.Coordinator.TrimMemoryLevel.Normal"
     enum="TrimMemoryLevel">
   <owner>bashi@chromium.org</owner>
@@ -64899,6 +64912,10 @@
 </histogram>
 
 <histogram name="Tab.Deactivation.Bookmarked" enum="Boolean">
+  <obsolete>
+    Deprecated 11/2016. No longer useful after finding out that it has no effect
+    on tab reactivation rates.
+  </obsolete>
   <owner>pmonette@chromium.org</owner>
   <summary>
     A tab was deactivated. Closing tabs are not included. This histogram also
@@ -65030,6 +65047,10 @@
 </histogram>
 
 <histogram name="Tab.Reactivation.Bookmarked" enum="Boolean">
+  <obsolete>
+    Deprecated 11/2016. No longer useful after finding out that it has no effect
+    on tab reactivation rates.
+  </obsolete>
   <owner>pmonette@chromium.org</owner>
   <summary>
     A tab was reactivated after being hidden. This histogram also records if the
@@ -65221,6 +65242,23 @@
   </summary>
 </histogram>
 
+<histogram name="Tab.TimeToReactivation.Important" units="ms">
+  <owner>pmonette@chromium.org</owner>
+  <summary>
+    The time elapsed from the moment a tab was deactivated until it was
+    reactivated. Only recorded for tabs that are pinned or had form interaction.
+  </summary>
+</histogram>
+
+<histogram name="Tab.TimeToReactivation.Normal" units="ms">
+  <owner>pmonette@chromium.org</owner>
+  <summary>
+    The time elapsed from the moment a tab was deactivated until it was
+    reactivated. Only recorded for tabs that are not pinned nor had form
+    interaction.
+  </summary>
+</histogram>
+
 <histogram name="Tab.TotalTabCount.BeforeLeavingApp" units="tabs">
   <owner>jaekyun@chromium.org</owner>
   <summary>
@@ -68953,6 +68991,16 @@
   </summary>
 </histogram>
 
+<histogram name="Variations.GoogleUpdateRegistryLabelsNeedClearing"
+    enum="BooleanNeedsClearing">
+  <owner>jwd@chromium.org</owner>
+  <summary>
+    If the registry value for Google Update experiment labels contains
+    Variations experiments, and therefore needs to have them cleared. This will
+    be recorderd once per sessions, right before attempting to clear the value.
+  </summary>
+</histogram>
+
 <histogram name="Variations.HeaderConstructionTime" units="microseconds">
   <owner>asvitkine@chromium.org</owner>
   <summary>How long it took to create the X-Client-Data header.</summary>
@@ -75795,6 +75843,11 @@
   <int value="1" label="Missing data in disk cache"/>
 </enum>
 
+<enum name="BooleanNeedsClearing" type="int">
+  <int value="0" label="Doesn't need clearing"/>
+  <int value="1" label="Needs to be clearred"/>
+</enum>
+
 <enum name="BooleanNullStream" type="int">
   <int value="0" label="Stream is not a nullptr"/>
   <int value="1" label="Stream is a nullptr"/>
@@ -85706,6 +85759,9 @@
   <int value="1679" label="HTMLMediaElementPreloadForcedMetadata"/>
   <int value="1680" label="GenericSensorStart"/>
   <int value="1681" label="GenericSensorStop"/>
+  <int value="1682" label="TouchEventPreventedNoTouchAction"/>
+  <int value="1683"
+      label="TouchEventPreventedForcedDocumentPassiveNoTouchAction"/>
 </enum>
 
 <enum name="FetchRequestMode" type="int">
@@ -107946,6 +108002,17 @@
   <affected-histogram name="Media.WatchTime"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="MemoryStateTransition" separator=".">
+  <suffix name="NormalToThrottled"/>
+  <suffix name="NormalToSuspended"/>
+  <suffix name="ThrottledToNormal"/>
+  <suffix name="ThrottledToSuspended"/>
+  <suffix name="SuspendedToNormal"/>
+  <suffix name="SuspendedToThrottled"/>
+  <affected-histogram name="Memory.Coordinator.StateDuration"/>
+  <affected-histogram name="Memory.Coordinator.TotalPrivate"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="MobileDownloadBytesDownloadedTypes" separator=".">
   <suffix name="ChromeNetworkStack.Failure"/>
   <suffix name="ChromeNetworkStack.Success"/>
@@ -111205,6 +111272,7 @@
 
 <histogram_suffixes name="SafeBrowsing.V4Store.Metrics" separator=".">
   <suffix name="UrlMalware"/>
+  <suffix name="UrlMalBin"/>
   <suffix name="UrlSoceng"/>
   <suffix name="UrlUws"/>
   <affected-histogram name="SafeBrowsing.V4Database.Size"/>
diff --git a/tools/perf/benchmarks/system_health_smoke_test.py b/tools/perf/benchmarks/system_health_smoke_test.py
index 5e146a9..6da740b 100644
--- a/tools/perf/benchmarks/system_health_smoke_test.py
+++ b/tools/perf/benchmarks/system_health_smoke_test.py
@@ -42,18 +42,6 @@
   # crbug.com/637230
   'benchmarks.system_health_smoke_test.SystemHealthBenchmarkSmokeTest.system_health.memory_desktop.browse:news:cnn',  # pylint: disable=line-too-long
 
-  # crbug.com/641934
-  # These tests are taking too long (more than 100s each).
-  # TODO(nedn): reenable these test once we have more capacity to run them on
-  # CQ.
-  'benchmarks.system_health_smoke_test.SystemHealthBenchmarkSmokeTest.system_health.memory_mobile.browse:news:flipboard',  # pylint: disable=line-too-long
-  'benchmarks.system_health_smoke_test.SystemHealthBenchmarkSmokeTest.system_health.memory_mobile.browse:media:imgur',  # pylint: disable=line-too-long
-  'benchmarks.system_health_smoke_test.SystemHealthBenchmarkSmokeTest.system_health.memory_mobile.browse:media:youtube',  # pylint: disable=line-too-long
-  'benchmarks.system_health_smoke_test.SystemHealthBenchmarkSmokeTest.system_health.memory_mobile.browse:media:facebook_photos',  # pylint: disable=line-too-long
-  'benchmarks.system_health_smoke_test.SystemHealthBenchmarkSmokeTest.system_health.memory_mobile.browse:news:reddit',  # pylint: disable=line-too-long
-  'benchmarks.system_health_smoke_test.SystemHealthBenchmarkSmokeTest.system_health.memory_mobile.browse:social:facebook',  # pylint: disable=line-too-long
-  'benchmarks.system_health_smoke_test.SystemHealthBenchmarkSmokeTest.system_health.memory_mobile.load:news:cnn',  # pylint: disable=line-too-long
-
   # Permenently disabled from smoke test for being long-running.
   'benchmarks.system_health_smoke_test.SystemHealthBenchmarkSmokeTest.system_health.memory_mobile.long_running:tools:gmail-foreground',  # pylint: disable=line-too-long
   'benchmarks.system_health_smoke_test.SystemHealthBenchmarkSmokeTest.system_health.memory_mobile.long_running:tools:gmail-background',  # pylint: disable=line-too-long
diff --git a/tools/perf/page_sets/system_health/browsing_stories.py b/tools/perf/page_sets/system_health/browsing_stories.py
index 8cbc0cc..2e78f40 100644
--- a/tools/perf/page_sets/system_health/browsing_stories.py
+++ b/tools/perf/page_sets/system_health/browsing_stories.py
@@ -30,6 +30,7 @@
     item_selector = 'document.querySelectorAll("%s")[%d]' % (
         self.ITEM_SELECTOR, index)
     # Only scrolls if element is not currently in viewport.
+    action_runner.WaitForElement(element_function=item_selector)
     action_runner.ScrollPageToElement(element_function=item_selector)
     self._ClickLink(action_runner, item_selector)
 
@@ -96,7 +97,6 @@
   ITEMS_TO_VISIT = 2
 
 
-@decorators.Disabled('android')  # crbug.com/664518
 class FacebookMobileStory(_NewsBrowsingStory):
   NAME = 'browse:social:facebook'
   URL = 'https://www.facebook.com/rihanna'
@@ -258,7 +258,6 @@
     action_runner.Wait(self.ITEM_VIEW_TIME_IN_SECONDS)
 
 
-@decorators.Disabled('android')  # crbug.com/664518
 class ImgurMobileStory(_MediaBrowsingStory):
   NAME = 'browse:media:imgur'
   URL = 'http://imgur.com/gallery/5UlBN'
diff --git a/ui/android/resources/crushed_sprite_resource.cc b/ui/android/resources/crushed_sprite_resource.cc
index aac7ccc6..f2885496 100644
--- a/ui/android/resources/crushed_sprite_resource.cc
+++ b/ui/android/resources/crushed_sprite_resource.cc
@@ -59,7 +59,7 @@
   return src_dst_rects_.size();
 }
 
-size_t CrushedSpriteResource::GetAllocatedSizeInBytes() const {
+size_t CrushedSpriteResource::EstimateMemoryUsage() const {
   return bitmap_.getSize();
 }
 
diff --git a/ui/android/resources/crushed_sprite_resource.h b/ui/android/resources/crushed_sprite_resource.h
index 9bfb57bb..f4c5102 100644
--- a/ui/android/resources/crushed_sprite_resource.h
+++ b/ui/android/resources/crushed_sprite_resource.h
@@ -71,7 +71,7 @@
   int GetFrameCount();
 
   // Returns the memory usage of the bitmap.
-  size_t GetAllocatedSizeInBytes() const;
+  size_t EstimateMemoryUsage() const;
 
  private:
   SkBitmap bitmap_;
diff --git a/ui/android/resources/resource_manager.cc b/ui/android/resources/resource_manager.cc
index 2117572..80112978 100644
--- a/ui/android/resources/resource_manager.cc
+++ b/ui/android/resources/resource_manager.cc
@@ -4,6 +4,7 @@
 
 #include "ui/android/resources/resource_manager.h"
 
+#include "base/trace_event/memory_usage_estimator.h"
 #include "ui/gfx/geometry/insets_f.h"
 
 namespace ui {
@@ -35,4 +36,8 @@
                    (size.height() - aperture.height()) * bottom_scale);
 }
 
+size_t ResourceManager::Resource::EstimateMemoryUsage() const {
+  return base::trace_event::EstimateMemoryUsage(ui_resource);
+}
+
 }  // namespace ui
diff --git a/ui/android/resources/resource_manager.h b/ui/android/resources/resource_manager.h
index 9b207aa..b812d109 100644
--- a/ui/android/resources/resource_manager.h
+++ b/ui/android/resources/resource_manager.h
@@ -46,6 +46,7 @@
     ~Resource();
     gfx::Rect Border(const gfx::Size& bounds) const;
     gfx::Rect Border(const gfx::Size& bounds, const gfx::InsetsF& scale) const;
+    size_t EstimateMemoryUsage() const;
 
     std::unique_ptr<cc::ScopedUIResource> ui_resource;
     gfx::Size size;
diff --git a/ui/android/resources/resource_manager_impl.cc b/ui/android/resources/resource_manager_impl.cc
index 2e60510..2e0cc89 100644
--- a/ui/android/resources/resource_manager_impl.cc
+++ b/ui/android/resources/resource_manager_impl.cc
@@ -16,6 +16,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/memory_usage_estimator.h"
 #include "base/trace_event/process_memory_dump.h"
 #include "base/trace_event/trace_event.h"
 #include "cc/resources/scoped_ui_resource.h"
@@ -301,31 +302,18 @@
 bool ResourceManagerImpl::OnMemoryDump(
     const base::trace_event::MemoryDumpArgs& args,
     base::trace_event::ProcessMemoryDump* pmd) {
-  size_t size = 0;
-  for (const auto& resource_map : resources_) {
-    for (const auto& id_and_resource : resource_map) {
-      if (id_and_resource.second && id_and_resource.second->ui_resource)
-        size += id_and_resource.second->ui_resource->GetAllocatedSizeInBytes();
-    }
-  }
-  for (const auto& id_and_resource : crushed_sprite_resources_) {
-    if (id_and_resource.second)
-      size += id_and_resource.second->GetAllocatedSizeInBytes();
-  }
-  for (const auto& color_and_resources : tinted_resources_) {
-    for (const auto& id_and_resource : *color_and_resources.second) {
-      if (id_and_resource.second && id_and_resource.second->ui_resource)
-        size += id_and_resource.second->ui_resource->GetAllocatedSizeInBytes();
-    }
-  }
+  size_t memory_usage =
+      base::trace_event::EstimateMemoryUsage(resources_) +
+      base::trace_event::EstimateMemoryUsage(crushed_sprite_resources_) +
+      base::trace_event::EstimateMemoryUsage(tinted_resources_);
 
   base::trace_event::MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(
       base::StringPrintf("ui/resource_manager_0x%" PRIXPTR,
                          reinterpret_cast<uintptr_t>(this)));
   dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
-                  base::trace_event::MemoryAllocatorDump::kUnitsBytes, size);
+                  base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+                  memory_usage);
 
-  // Bitmaps are allocated from malloc.
   const char* system_allocator_name =
       base::trace_event::MemoryDumpManager::GetInstance()
           ->system_allocator_pool_name();
diff --git a/ui/arc/BUILD.gn b/ui/arc/BUILD.gn
index b3b3b61a..3139ee8 100644
--- a/ui/arc/BUILD.gn
+++ b/ui/arc/BUILD.gn
@@ -23,7 +23,6 @@
     "//ash",
     "//base",
     "//components/arc:arc_base",
-    "//components/arc:arc_bindings",
     "//components/exo",
     "//components/signin/core/account_id",
     "//mojo/common:common_base",
@@ -53,7 +52,6 @@
     ":arc",
     "//base",
     "//base/test:test_support",
-    "//components/arc:arc_base",
     "//components/arc:arc_test_support",
     "//mojo/edk/system",
     "//testing/gtest",
diff --git a/ui/aura/BUILD.gn b/ui/aura/BUILD.gn
index d0f3975b..d18cfaf 100644
--- a/ui/aura/BUILD.gn
+++ b/ui/aura/BUILD.gn
@@ -96,6 +96,7 @@
     "mus/mus_util.h",
     "mus/os_exchange_data_provider_mus.cc",
     "mus/os_exchange_data_provider_mus.h",
+    "mus/property_converter.cc",
     "mus/property_converter.h",
     "mus/surface_id_handler.h",
     "mus/text_input_client_impl.cc",
@@ -346,6 +347,7 @@
     "//base/test:test_support",
     "//mojo/common",
     "//net",
+    "//services/ui/public/cpp",
     "//skia",
     "//testing/gtest",
     "//ui/base:test_support",
diff --git a/ui/aura/mus/DEPS b/ui/aura/mus/DEPS
index fb60df1..a67b740f 100644
--- a/ui/aura/mus/DEPS
+++ b/ui/aura/mus/DEPS
@@ -9,6 +9,7 @@
   "+mojo/public/cpp/system/buffer.h",
   "+mojo/public/cpp/system/platform_handle.h",
   "+services/ui/public/cpp/context_provider.h",
+  "+services/ui/public/cpp/property_type_converters.h",
   "+services/ui/public/cpp/raster_thread_helper.h",
   "+ui/gl/gl_bindings.h",
 ]
\ No newline at end of file
diff --git a/ui/aura/mus/property_converter.cc b/ui/aura/mus/property_converter.cc
new file mode 100644
index 0000000..070c8ff0
--- /dev/null
+++ b/ui/aura/mus/property_converter.cc
@@ -0,0 +1,77 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/mus/property_converter.h"
+
+#include "mojo/public/cpp/bindings/type_converter.h"
+#include "services/ui/public/cpp/property_type_converters.h"
+#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/window_property.h"
+
+namespace aura {
+
+namespace {
+
+// Get the aura property key and name for a mus property transport |name|.
+bool GetPropertyKeyForTransportName(const std::string& name,
+                                    const void** key_out,
+                                    const char** name_out) {
+  if (name == ui::mojom::WindowManager::kAlwaysOnTop_Property) {
+    *key_out = client::kAlwaysOnTopKey;
+    *name_out = client::kAlwaysOnTopKey->name;
+    return true;
+  }
+  return false;
+}
+
+}  // namespace
+
+PropertyConverter::PropertyConverter() {}
+
+PropertyConverter::~PropertyConverter() {}
+
+bool PropertyConverter::ConvertPropertyForTransport(
+    Window* window,
+    const void* key,
+    std::string* server_property_name,
+    std::unique_ptr<std::vector<uint8_t>>* server_property_value) {
+  *server_property_name = GetTransportNameForPropertyKey(key);
+  if (!server_property_name->empty()) {
+    // TODO(msw): Using the int64_t accessor is wasteful for bool, etc.
+    const int64_t value = window->GetPropertyInternal(key, 0);
+    *server_property_value = base::MakeUnique<std::vector<uint8_t>>(
+        mojo::ConvertTo<std::vector<uint8_t>>(value));
+    return true;
+  }
+  DVLOG(2) << "Unknown aura property key: " << key;
+  return false;
+}
+
+std::string PropertyConverter::GetTransportNameForPropertyKey(const void* key) {
+  if (key == client::kAlwaysOnTopKey)
+    return ui::mojom::WindowManager::kAlwaysOnTop_Property;
+  return std::string();
+}
+
+void PropertyConverter::SetPropertyFromTransportValue(
+    Window* window,
+    const std::string& server_property_name,
+    const std::vector<uint8_t>* data) {
+  const void* key = nullptr;
+  const char* name = nullptr;
+  if (GetPropertyKeyForTransportName(server_property_name, &key, &name)) {
+    DCHECK(key);
+    DCHECK(name);
+    // Aura window only supports property types that fit in int64_t.
+    CHECK_LE(8u, data->size()) << " Property type not supported: " << key;
+    const int64_t value = mojo::ConvertTo<int64_t>(*data);
+    // TODO(msw): Should aura::Window just store all properties by name?
+    window->SetPropertyInternal(key, name, nullptr, value, 0);
+  } else {
+    DVLOG(2) << "Unknown mus property name: " << server_property_name;
+  }
+}
+
+}  // namespace aura
diff --git a/ui/aura/mus/property_converter.h b/ui/aura/mus/property_converter.h
index dc06565..4fd2512 100644
--- a/ui/aura/mus/property_converter.h
+++ b/ui/aura/mus/property_converter.h
@@ -11,6 +11,7 @@
 #include <string>
 #include <vector>
 
+#include "base/macros.h"
 #include "ui/aura/aura_export.h"
 
 namespace aura {
@@ -22,9 +23,10 @@
 // mapped to the other using this class. Not all Window properties need to map
 // to server properties, and similarly not all transport properties need map to
 // Window properties.
-class PropertyConverter {
+class AURA_EXPORT PropertyConverter {
  public:
-  virtual ~PropertyConverter() {}
+  PropertyConverter();
+  virtual ~PropertyConverter();
 
   // Maps a property on the Window to a property pushed to the server. Return
   // true if the property should be sent to the server, false if the property
@@ -33,10 +35,10 @@
       Window* window,
       const void* key,
       std::string* transport_name,
-      std::unique_ptr<std::vector<uint8_t>>* transport_value) = 0;
+      std::unique_ptr<std::vector<uint8_t>>* transport_value);
 
   // Returns the transport name for a Window property.
-  virtual std::string GetTransportNameForPropertyKey(const void* key) = 0;
+  virtual std::string GetTransportNameForPropertyKey(const void* key);
 
   // Applies a value from the server to |window|. |transport_name| is the
   // name of the property and |transport_data| the value. |transport_data| may
@@ -44,7 +46,10 @@
   virtual void SetPropertyFromTransportValue(
       Window* window,
       const std::string& transport_name,
-      const std::vector<uint8_t>* transport_data) = 0;
+      const std::vector<uint8_t>* transport_data);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PropertyConverter);
 };
 
 }  // namespace aura
diff --git a/ui/aura/mus/window_tree_client_unittest.cc b/ui/aura/mus/window_tree_client_unittest.cc
index c7705d87..24d4b640 100644
--- a/ui/aura/mus/window_tree_client_unittest.cc
+++ b/ui/aura/mus/window_tree_client_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "mojo/common/common_type_converters.h"
+#include "services/ui/public/cpp/property_type_converters.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/capture_client.h"
@@ -60,7 +61,6 @@
   return window->GetRootWindow()->GetHost()->compositor()->IsVisible();
 }
 
-const char kAlwaysOnTopServerKey[] = "always-on-top-server";
 const char kTestPropertyServerKey1[] = "test-property-server1";
 const char kTestPropertyServerKey2[] = "test-property-server2";
 
@@ -75,13 +75,6 @@
       const void* key,
       std::string* server_property_name,
       std::unique_ptr<std::vector<uint8_t>>* server_property_value) override {
-    if (key == client::kAlwaysOnTopKey) {
-      *server_property_name = kAlwaysOnTopServerKey;
-      *server_property_value = base::MakeUnique<std::vector<uint8_t>>(1);
-      (*server_property_value->get())[0] =
-          window->GetProperty(client::kAlwaysOnTopKey);
-      return true;
-    }
     if (key == kTestPropertyKey1) {
       *server_property_name = kTestPropertyServerKey1;
       *server_property_value = base::MakeUnique<std::vector<uint8_t>>(1);
@@ -96,30 +89,29 @@
           window->GetProperty(kTestPropertyKey2);
       return true;
     }
-    return false;
+    return PropertyConverter::ConvertPropertyForTransport(
+        window, key, server_property_name, server_property_value);
   }
 
   std::string GetTransportNameForPropertyKey(const void* key) override {
-    if (key == client::kAlwaysOnTopKey)
-      return kAlwaysOnTopServerKey;
     if (key == kTestPropertyKey1)
       return kTestPropertyServerKey1;
     if (key == kTestPropertyKey2)
       return kTestPropertyServerKey2;
-    return std::string();
+    return PropertyConverter::GetTransportNameForPropertyKey(key);
   }
 
   void SetPropertyFromTransportValue(
       Window* window,
       const std::string& server_property_name,
       const std::vector<uint8_t>* data) override {
-    if (server_property_name == kAlwaysOnTopServerKey) {
-      window->SetProperty(client::kAlwaysOnTopKey,
-                          static_cast<bool>((*data)[0]));
-    } else if (server_property_name == kTestPropertyServerKey1) {
+    if (server_property_name == kTestPropertyServerKey1) {
       window->SetProperty(kTestPropertyKey1, (*data)[0]);
     } else if (server_property_name == kTestPropertyServerKey2) {
       window->SetProperty(kTestPropertyKey2, (*data)[0]);
+    } else {
+      PropertyConverter::SetPropertyFromTransportValue(
+          window, server_property_name, data);
     }
   }
 
@@ -307,17 +299,34 @@
   EXPECT_EQ(original_bounds, root_window()->bounds());
 }
 
-// Verifies properties are reverted if the server replied that the change
-// failed.
-TEST_F(WindowTreeClientWmTest, SetPropertyFailed) {
-  SetPropertyConverter(base::MakeUnique<TestPropertyConverter>());
+// Verifies properties are set if the server replied that the change succeeded.
+TEST_F(WindowTreeClientWmTest, SetPropertySucceeded) {
   ASSERT_FALSE(root_window()->GetProperty(client::kAlwaysOnTopKey));
   root_window()->SetProperty(client::kAlwaysOnTopKey, true);
   EXPECT_TRUE(root_window()->GetProperty(client::kAlwaysOnTopKey));
-  mojo::Array<uint8_t> transport_value = window_tree()->GetLastPropertyValue();
-  ASSERT_FALSE(transport_value.is_null());
-  ASSERT_EQ(1u, transport_value.size());
-  EXPECT_EQ(1, transport_value[0]);
+  mojo::Array<uint8_t> value = window_tree()->GetLastPropertyValue();
+  ASSERT_FALSE(value.is_null());
+  // PropertyConverter uses int64_t values, even for smaller types, like bool.
+  ASSERT_EQ(8u, value.size());
+  std::vector<uint8_t> array = mojo::ConvertTo<std::vector<uint8_t>>(value);
+  EXPECT_EQ(1, mojo::ConvertTo<int64_t>(array));
+  ASSERT_TRUE(window_tree()->AckSingleChangeOfType(
+      WindowTreeChangeType::PROPERTY, true));
+  EXPECT_TRUE(root_window()->GetProperty(client::kAlwaysOnTopKey));
+}
+
+// Verifies properties are reverted if the server replied that the change
+// failed.
+TEST_F(WindowTreeClientWmTest, SetPropertyFailed) {
+  ASSERT_FALSE(root_window()->GetProperty(client::kAlwaysOnTopKey));
+  root_window()->SetProperty(client::kAlwaysOnTopKey, true);
+  EXPECT_TRUE(root_window()->GetProperty(client::kAlwaysOnTopKey));
+  mojo::Array<uint8_t> value = window_tree()->GetLastPropertyValue();
+  ASSERT_FALSE(value.is_null());
+  // PropertyConverter uses int64_t values, even for smaller types, like bool.
+  ASSERT_EQ(8u, value.size());
+  std::vector<uint8_t> array = mojo::ConvertTo<std::vector<uint8_t>>(value);
+  EXPECT_EQ(1, mojo::ConvertTo<int64_t>(array));
   ASSERT_TRUE(window_tree()->AckSingleChangeOfType(
       WindowTreeChangeType::PROPERTY, false));
   EXPECT_FALSE(root_window()->GetProperty(client::kAlwaysOnTopKey));
diff --git a/ui/aura/test/aura_test_base.cc b/ui/aura/test/aura_test_base.cc
index d14353d..75fa47e 100644
--- a/ui/aura/test/aura_test_base.cc
+++ b/ui/aura/test/aura_test_base.cc
@@ -20,36 +20,6 @@
 
 namespace aura {
 namespace test {
-namespace {
-
-class TestPropertyConverter : public PropertyConverter {
- public:
-  TestPropertyConverter() {}
-  ~TestPropertyConverter() override {}
-
-  // PropertyConverter:
-  bool ConvertPropertyForTransport(
-      Window* window,
-      const void* key,
-      std::string* server_property_name,
-      std::unique_ptr<std::vector<uint8_t>>* server_property_value) override {
-    return false;
-  }
-
-  std::string GetTransportNameForPropertyKey(const void* key) override {
-    return std::string();
-  }
-
-  void SetPropertyFromTransportValue(
-      Window* window,
-      const std::string& server_property_name,
-      const std::vector<uint8_t>* data) override {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TestPropertyConverter);
-};
-
-}  // namespace
 
 AuraTestBase::AuraTestBase()
     : window_manager_delegate_(this), window_tree_client_delegate_(this) {}
@@ -65,7 +35,7 @@
   setup_called_ = true;
   testing::Test::SetUp();
   if (!property_converter_)
-    property_converter_ = base::MakeUnique<TestPropertyConverter>();
+    property_converter_ = base::MakeUnique<PropertyConverter>();
   // ContentTestSuiteBase might have already initialized
   // MaterialDesignController in unit_tests suite.
   ui::test::MaterialDesignControllerTestAPI::Uninitialize();
diff --git a/ui/aura/window.h b/ui/aura/window.h
index 2592c320..17e4115 100644
--- a/ui/aura/window.h
+++ b/ui/aura/window.h
@@ -347,6 +347,7 @@
 
  private:
   friend class LayoutManager;
+  friend class PropertyConverter;
   friend class WindowPort;
   friend class WindowTargeter;
   friend class subtle::PropertyHelper;
diff --git a/ui/events/event.cc b/ui/events/event.cc
index f04c842e..e82747ba 100644
--- a/ui/events/event.cc
+++ b/ui/events/event.cc
@@ -1227,11 +1227,7 @@
 // so this is a synthetic or native keystroke event.
 // Therefore, perform only the fallback action.
 #elif defined(USE_X11)
-  // When a control key is held, prefer ASCII characters to non ASCII
-  // characters in order to use it for shortcut keys.  GetCharacterFromKeyCode
-  // returns 'a' for VKEY_A even if the key is actually bound to 'à' in X11.
-  // GetCharacterFromXEvent returns 'à' in that case.
-  if (!IsControlDown() && native_event()) {
+  if (native_event()) {
     key_ = GetDomKeyFromXEvent(native_event());
     return;
   }
diff --git a/ui/events/keycodes/keyboard_code_conversion_x.cc b/ui/events/keycodes/keyboard_code_conversion_x.cc
index dfac6ed..c6f2bfe 100644
--- a/ui/events/keycodes/keyboard_code_conversion_x.cc
+++ b/ui/events/keycodes/keyboard_code_conversion_x.cc
@@ -904,17 +904,27 @@
 
 DomKey GetDomKeyFromXEvent(const XEvent* xev) {
   XEvent xkeyevent = {0};
-  const XKeyEvent* xkey = NULL;
+  XKeyEvent xkey;
   if (xev->type == GenericEvent) {
     // Convert the XI2 key event into a core key event so that we can
     // continue to use XLookupString() until crbug.com/367732 is complete.
     InitXKeyEventFromXIDeviceEvent(*xev, &xkeyevent);
-    xkey = &xkeyevent.xkey;
+    xkey = xkeyevent.xkey;
   } else {
-    xkey = &xev->xkey;
+    xkey = xev->xkey;
   }
+  // There is no good way to check whether a key combination will print a
+  // character on screen.
+  // e.g. On Linux US keyboard with French layout, |XLookupString()|
+  //        * Returns '?' for ctrl-shift-/
+  //        * Returns '§' for shift-/
+  //      According to spec the DomKey for ctrl-shift-/ should also be '§'.
+  // The solution is to take out ctrl modifier directly, as according to XKB map
+  // no keyboard combinations with ctrl key are mapped to printable character.
+  // https://crbug.com/633838
+  xkey.state &= ~ControlMask;
   KeySym keysym = XK_VoidSymbol;
-  XLookupString(const_cast<XKeyEvent*>(xkey), NULL, 0, &keysym, NULL);
+  XLookupString(&xkey, NULL, 0, &keysym, NULL);
   base::char16 ch = GetUnicodeCharacterFromXKeySym(keysym);
   return XKeySymToDomKey(keysym, ch);
 }
diff --git a/ui/file_manager/file_manager_resources.grd b/ui/file_manager/file_manager_resources.grd
index ec13284..6678891 100644
--- a/ui/file_manager/file_manager_resources.grd
+++ b/ui/file_manager/file_manager_resources.grd
@@ -186,7 +186,7 @@
       <include name="IDR_FILE_MANAGER_IMG_GALLERY_2X_CURSOR_CROP" file="gallery/images/200/cursor_crop.png" type="BINDATA" />
 
       <!-- Image loader extension manifest and scripts. -->
-      <if expr="image_loader_extension">
+      <if expr="chromeos">
         <include name="IDR_IMAGE_LOADER_MANIFEST" file="image_loader/manifest.json" type="BINDATA" />
         <include name="IDR_IMAGE_LOADER_BACKGROUND_JS" file="image_loader/background_scripts.js" flattenhtml="true" type="BINDATA" />
         <include name="IDR_IMAGE_LOADER_CLIENT_JS" file="image_loader/image_loader_client.js" type="BINDATA" />
diff --git a/ui/gfx/transform_unittest.cc b/ui/gfx/transform_unittest.cc
index e0b2013f..8d99b6e 100644
--- a/ui/gfx/transform_unittest.cc
+++ b/ui/gfx/transform_unittest.cc
@@ -757,9 +757,8 @@
   }
 }
 
-#if defined(_WIN64) || defined(ARCH_CPU_ARM_FAMILY)
-// Win: https://crbug.com/406574
-// Arm: https://crbug.com/662558
+#if defined(_WIN64)
+// https://crbug.com/406574
 #define MAYBE_BlendScale DISABLED_BlendScale
 #else
 #define MAYBE_BlendScale BlendScale
@@ -769,11 +768,12 @@
   for (int i = -5; i < 15; ++i) {
     Transform to;
     to.Scale3d(5, 4, 3);
-    double t = i / 9.0;
-    EXPECT_TRUE(to.Blend(from, t));
-    EXPECT_FLOAT_EQ(t * 4 + 1, to.matrix().get(0, 0)) << "i: " << i;
-    EXPECT_FLOAT_EQ(t * 3 + 1, to.matrix().get(1, 1)) << "i: " << i;
-    EXPECT_FLOAT_EQ(t * 2 + 1, to.matrix().get(2, 2)) << "i: " << i;
+    double s1 = i / 9.0;
+    double s2 = 1 - s1;
+    EXPECT_TRUE(to.Blend(from, s1));
+    EXPECT_FLOAT_EQ(5 * s1 + s2, to.matrix().get(0, 0)) << "i: " << i;
+    EXPECT_FLOAT_EQ(4 * s1 + s2, to.matrix().get(1, 1)) << "i: " << i;
+    EXPECT_FLOAT_EQ(3 * s1 + s2, to.matrix().get(2, 2)) << "i: " << i;
   }
 }
 
diff --git a/ui/login/display_manager.js b/ui/login/display_manager.js
index 94e37a95..460086a 100644
--- a/ui/login/display_manager.js
+++ b/ui/login/display_manager.js
@@ -29,6 +29,7 @@
 /** @const */ var SCREEN_FATAL_ERROR = 'fatal-error';
 /** @const */ var SCREEN_KIOSK_ENABLE = 'kiosk-enable';
 /** @const */ var SCREEN_TERMS_OF_SERVICE = 'terms-of-service';
+/** @const */ var SCREEN_ARC_TERMS_OF_SERVICE = 'arc-tos';
 /** @const */ var SCREEN_WRONG_HWID = 'wrong-hwid';
 /** @const */ var SCREEN_DEVICE_DISABLED = 'device-disabled';
 /** @const */ var SCREEN_UNRECOVERABLE_CRYPTOHOME_ERROR =
@@ -133,6 +134,7 @@
     SCREEN_TPM_ERROR,
     SCREEN_PASSWORD_CHANGED,
     SCREEN_TERMS_OF_SERVICE,
+    SCREEN_ARC_TERMS_OF_SERVICE,
     SCREEN_WRONG_HWID,
     SCREEN_CONFIRM_PASSWORD,
     SCREEN_FATAL_ERROR
diff --git a/ui/login/screen_container.css b/ui/login/screen_container.css
index 61da0ac8..cf505f2d 100644
--- a/ui/login/screen_container.css
+++ b/ui/login/screen_container.css
@@ -85,6 +85,7 @@
 #oobe.supervised-user-creation #inner-container,
 #oobe.supervised-user-creation-dialog #inner-container,
 #oobe.terms-of-service #inner-container,
+#oobe.arc-tos #inner-container,
 #oobe.update #inner-container,
 #oobe.user-image #inner-container,
 #oobe.wrong-hwid #inner-container,
@@ -172,6 +173,7 @@
 #supervised-user-creation-dialog-dot,
 #supervised-user-creation-dot,
 #terms-of-service-dot,
+#arc-tos-dot,
 #tpm-error-message-dot,
 #wrong-hwid-dot,
 #unrecoverable-cryptohome-error-dot {
diff --git a/ui/views/animation/ink_drop_impl.cc b/ui/views/animation/ink_drop_impl.cc
index 0ec1aef..dd44207 100644
--- a/ui/views/animation/ink_drop_impl.cc
+++ b/ui/views/animation/ink_drop_impl.cc
@@ -62,6 +62,27 @@
   return state_factory_->ink_drop();
 }
 
+// A HighlightState to be used during InkDropImpl destruction. All event
+// handlers are no-ops so as to avoid triggering animations during tear down.
+class InkDropImpl::DestroyingHighlightState
+    : public InkDropImpl::HighlightState {
+ public:
+  DestroyingHighlightState() : HighlightState(nullptr) {}
+
+  // InkDropImpl::HighlightState:
+  void Enter() override {}
+  void ShowOnHoverChanged() override {}
+  void OnHoverChanged() override {}
+  void ShowOnFocusChanged() override {}
+  void OnFocusChanged() override {}
+  void AnimationStarted(InkDropState ink_drop_state) override {}
+  void AnimationEnded(InkDropState ink_drop_state,
+                      InkDropAnimationEndedReason reason) override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DestroyingHighlightState);
+};
+
 //
 // AutoHighlightMode::NONE states
 //
@@ -562,12 +583,18 @@
       show_highlight_on_focus_(false),
       is_hovered_(false),
       is_focused_(false),
-      exiting_highlight_state_(false) {
+      exiting_highlight_state_(false),
+      destroying_(false) {
   SetAutoHighlightMode(AutoHighlightMode::NONE);
   root_layer_->set_name("InkDropImpl:RootLayer");
 }
 
 InkDropImpl::~InkDropImpl() {
+  destroying_ = true;
+  // Setting a no-op state prevents animations from being triggered on a null
+  // |ink_drop_ripple_| as a side effect of the tear down.
+  SetHighlightState(base::MakeUnique<DestroyingHighlightState>());
+
   // Explicitly destroy the InkDropRipple so that this still exists if
   // views::InkDropRippleObserver methods are called on this.
   DestroyInkDropRipple();
@@ -638,6 +665,8 @@
 }
 
 void InkDropImpl::CreateInkDropRipple() {
+  DCHECK(!destroying_);
+
   DestroyInkDropRipple();
   ink_drop_ripple_ = ink_drop_host_->CreateInkDropRipple();
   ink_drop_ripple_->set_observer(this);
@@ -654,6 +683,8 @@
 }
 
 void InkDropImpl::CreateInkDropHighlight() {
+  DCHECK(!destroying_);
+
   DestroyInkDropHighlight();
 
   highlight_ = ink_drop_host_->CreateInkDropHighlight();
diff --git a/ui/views/animation/ink_drop_impl.h b/ui/views/animation/ink_drop_impl.h
index 0ac4e97..04641ba 100644
--- a/ui/views/animation/ink_drop_impl.h
+++ b/ui/views/animation/ink_drop_impl.h
@@ -109,7 +109,9 @@
     // but is required before exiting |this| state (e.g. releasing resources).
     //
     // Subclass implementations should NOT do any work that may trigger another
-    // state change since a state change is already in progress.
+    // state change since a state change is already in progress. They must also
+    // avoid triggering any animations since Exit() will be called during
+    // InkDropImpl destruction.
     virtual void Exit() {}
 
     // Input state change handlers.
@@ -179,6 +181,8 @@
     DISALLOW_COPY_AND_ASSIGN(HighlightStateFactory);
   };
 
+  class DestroyingHighlightState;
+
   // AutoHighlightMode::NONE
   class NoAutoHighlightHiddenState;
   class NoAutoHighlightVisibleState;
@@ -305,6 +309,9 @@
   // the current state.
   bool exiting_highlight_state_;
 
+  // Used to fail DCHECKS to catch unexpected behavior during tear down.
+  bool destroying_;
+
   DISALLOW_COPY_AND_ASSIGN(InkDropImpl);
 };
 
diff --git a/ui/views/animation/ink_drop_impl_unittest.cc b/ui/views/animation/ink_drop_impl_unittest.cc
index e7717da..2c5c031 100644
--- a/ui/views/animation/ink_drop_impl_unittest.cc
+++ b/ui/views/animation/ink_drop_impl_unittest.cc
@@ -238,6 +238,10 @@
           test_api()->state_factory()),
       ".*HighlightStates should not be changed within a call to "
       "HighlightState::Exit\\(\\)\\..*");
+  // Need to set the |highlight_state_| directly because the
+  // SetStateOnExitHighlightState will recursively try to set it during tear
+  // down and cause a stack overflow.
+  test_api()->SetHighlightState(nullptr);
 }
 #endif
 
diff --git a/ui/views/animation/test/ink_drop_impl_test_api.cc b/ui/views/animation/test/ink_drop_impl_test_api.cc
index dac20c4..e9cc21c 100644
--- a/ui/views/animation/test/ink_drop_impl_test_api.cc
+++ b/ui/views/animation/test/ink_drop_impl_test_api.cc
@@ -102,6 +102,11 @@
   DCHECK_EQ(should_highlight, ink_drop_->ShouldHighlight());
 }
 
+void InkDropImplTestApi::SetHighlightState(
+    std::unique_ptr<InkDropImpl::HighlightState> highlight_state) {
+  ink_drop_->highlight_state_ = std::move(highlight_state);
+}
+
 const InkDropHighlight* InkDropImplTestApi::highlight() const {
   return ink_drop_->highlight_.get();
 }
diff --git a/ui/views/animation/test/ink_drop_impl_test_api.h b/ui/views/animation/test/ink_drop_impl_test_api.h
index 9185799..665baedb 100644
--- a/ui/views/animation/test/ink_drop_impl_test_api.h
+++ b/ui/views/animation/test/ink_drop_impl_test_api.h
@@ -88,6 +88,9 @@
     return ink_drop_->highlight_state_factory_.get();
   }
 
+  void SetHighlightState(
+      std::unique_ptr<InkDropImpl::HighlightState> highlight_state);
+
   const InkDropHighlight* highlight() const;
   bool IsHighlightFadingInOrVisible() const;
   bool ShouldHighlight() const;
diff --git a/ui/views/mus/BUILD.gn b/ui/views/mus/BUILD.gn
index efae737..326377a7 100644
--- a/ui/views/mus/BUILD.gn
+++ b/ui/views/mus/BUILD.gn
@@ -394,19 +394,16 @@
 }
 
 service_manifest("unittests_manifest") {
-  type = "exe"
   name = "views_mus_unittests"
   source = "unittests_manifest.json"
 }
 
 service_manifest("unittests_aura_manifest") {
-  type = "exe"
   name = "views_aura_mus_unittests"
   source = "unittests_aura_manifest.json"
 }
 
 service_manifest("interactive_ui_tests_manifest") {
-  type = "exe"
   name = "views_mus_interactive_ui_tests"
   source = "interactive_ui_tests_manifest.json"
 }
diff --git a/ui/views/mus/mus_client.cc b/ui/views/mus/mus_client.cc
index 3273186..b5ac279 100644
--- a/ui/views/mus/mus_client.cc
+++ b/ui/views/mus/mus_client.cc
@@ -28,36 +28,6 @@
 #include "ui/wm/core/wm_state.h"
 
 namespace views {
-namespace {
-
-// TODO: property converter should likely live in aura as it needs to be used
-// by both ash and views. http://crbug.com/663522.
-class PropertyConverterImpl : public aura::PropertyConverter {
- public:
-  PropertyConverterImpl() {}
-  ~PropertyConverterImpl() override {}
-
-  // aura::PropertyConverter:
-  bool ConvertPropertyForTransport(
-      aura::Window* window,
-      const void* key,
-      std::string* transport_name,
-      std::unique_ptr<std::vector<uint8_t>>* transport_value) override {
-    return false;
-  }
-  std::string GetTransportNameForPropertyKey(const void* key) override {
-    return std::string();
-  }
-  void SetPropertyFromTransportValue(
-      aura::Window* window,
-      const std::string& transport_name,
-      const std::vector<uint8_t>* transport_data) override {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(PropertyConverterImpl);
-};
-
-}  // namespace
 
 // static
 MusClient* MusClient::instance_ = nullptr;
@@ -113,7 +83,8 @@
     : connector_(connector), identity_(identity) {
   DCHECK(!instance_);
   instance_ = this;
-  property_converter_ = base::MakeUnique<PropertyConverterImpl>();
+  // TODO(msw): Avoid this... use some default value? Allow clients to extend?
+  property_converter_ = base::MakeUnique<aura::PropertyConverter>();
 
   wm_state_ = base::MakeUnique<wm::WMState>();
 
diff --git a/ui/views/mus/unittests_aura_manifest.json b/ui/views/mus/unittests_aura_manifest.json
index f36456fe..cbb365f 100644
--- a/ui/views/mus/unittests_aura_manifest.json
+++ b/ui/views/mus/unittests_aura_manifest.json
@@ -1,5 +1,5 @@
 {
-  "name": "exe:views_aura_mus_unittests",
+  "name": "service:views_aura_mus_unittests",
   "display_name": "Views Aura Mus Unittests",
   "interface_provider_specs": {
     "service_manager:connector": {
diff --git a/ui/views/mus/unittests_manifest.json b/ui/views/mus/unittests_manifest.json
index 6c7293a..28a0d91 100644
--- a/ui/views/mus/unittests_manifest.json
+++ b/ui/views/mus/unittests_manifest.json
@@ -1,5 +1,5 @@
 {
-  "name": "exe:views_mus_unittests",
+  "name": "service:views_mus_unittests",
   "display_name": "Views Mus Unittests",
   "interface_provider_specs": {
     "service_manager:connector": {
diff --git a/ui/views/mus/views_aura_mus_test_suite.cc b/ui/views/mus/views_aura_mus_test_suite.cc
index eb94100..4e06c090 100644
--- a/ui/views/mus/views_aura_mus_test_suite.cc
+++ b/ui/views/mus/views_aura_mus_test_suite.cc
@@ -160,13 +160,13 @@
   }
 
   // Returns the name of the test executable, e.g.
-  // "exe:views_aura_mus_unittests".
+  // "service:views_mus_unittests".
   std::string GetTestName() {
     base::FilePath executable = base::CommandLine::ForCurrentProcess()
                                     ->GetProgram()
                                     .BaseName()
                                     .RemoveExtension();
-    return std::string("exe:") + executable.MaybeAsASCII();
+    return std::string("service:") + executable.MaybeAsASCII();
   }
 
   base::Thread thread_;
diff --git a/ui/views/mus/views_mus_test_suite.cc b/ui/views/mus/views_mus_test_suite.cc
index 892c096..18b3d50 100644
--- a/ui/views/mus/views_mus_test_suite.cc
+++ b/ui/views/mus/views_mus_test_suite.cc
@@ -150,13 +150,14 @@
     wait->Signal();
   }
 
-  // Returns the name of the test executable, e.g. "exe:views_mus_unittests".
+  // Returns the name of the test executable, e.g.
+  // "service:views_mus_unittests".
   std::string GetTestName() {
     base::FilePath executable = base::CommandLine::ForCurrentProcess()
                                     ->GetProgram()
                                     .BaseName()
                                     .RemoveExtension();
-    return std::string("exe:") + executable.MaybeAsASCII();
+    return std::string("service:") + executable.MaybeAsASCII();
   }
 
   base::Thread thread_;
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
index 574a2e99d..0dea5ca55 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
@@ -424,7 +424,7 @@
   // is therefore transparent. Note: This is not equivalent to calling
   // IsAeroGlassEnabled, because ShouldUseNativeFrame is overridden in a
   // subclass.
-  return ShouldUseNativeFrame();
+  return ShouldUseNativeFrame() && !IsFullscreen();
 }
 
 void DesktopWindowTreeHostWin::FrameTypeChanged() {
@@ -962,7 +962,7 @@
 }
 
 void DesktopWindowTreeHostWin::SetWindowTransparency() {
-  bool transparent = ShouldUseNativeFrame() && !IsFullscreen();
+  bool transparent = ShouldWindowContentsBeTransparent();
   compositor()->SetHostHasTransparentBackground(transparent);
   window()->SetTransparent(transparent);
   content_window_->SetTransparent(transparent);
diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc
index f5687e9..c4e3a7c 100644
--- a/ui/views/widget/widget_unittest.cc
+++ b/ui/views/widget/widget_unittest.cc
@@ -312,14 +312,15 @@
 
   // NOTE: for aura-mus-client stacking of top-levels is not maintained in the
   // client, so z-order of top-levels can't be determined.
-  if (!IsAuraMusClient())
+  const bool check_toplevel_z_order = !IsAuraMusClient();
+  if (check_toplevel_z_order)
     EXPECT_TRUE(IsWindowStackedAbove(popover.get(), child));
   EXPECT_TRUE(IsWindowStackedAbove(child, parent.get()));
 
   // Showing the parent again should raise it and its child above the popover.
   parent->Show();
   EXPECT_TRUE(IsWindowStackedAbove(child, parent.get()));
-  if (!IsAuraMusClient())
+  if (check_toplevel_z_order)
     EXPECT_TRUE(IsWindowStackedAbove(parent.get(), popover.get()));
 
   // Test grandchildren.
@@ -328,17 +329,17 @@
   grandchild->ShowInactive();
   EXPECT_TRUE(IsWindowStackedAbove(grandchild, child));
   EXPECT_TRUE(IsWindowStackedAbove(child, parent.get()));
-  if (!IsAuraMusClient())
+  if (check_toplevel_z_order)
     EXPECT_TRUE(IsWindowStackedAbove(parent.get(), popover.get()));
 
   popover->Show();
-  if (!IsAuraMusClient())
+  if (check_toplevel_z_order)
     EXPECT_TRUE(IsWindowStackedAbove(popover.get(), grandchild));
   EXPECT_TRUE(IsWindowStackedAbove(grandchild, child));
 
   parent->Show();
   EXPECT_TRUE(IsWindowStackedAbove(grandchild, child));
-  if (!IsAuraMusClient())
+  if (check_toplevel_z_order)
     EXPECT_TRUE(IsWindowStackedAbove(child, popover.get()));
 
   // Test hiding and reshowing.
@@ -348,7 +349,7 @@
 
   EXPECT_TRUE(IsWindowStackedAbove(grandchild, child));
   EXPECT_TRUE(IsWindowStackedAbove(child, parent.get()));
-  if (!IsAuraMusClient())
+  if (check_toplevel_z_order)
     EXPECT_TRUE(IsWindowStackedAbove(parent.get(), popover.get()));
 
   grandchild->Hide();
@@ -357,7 +358,7 @@
 
   EXPECT_TRUE(IsWindowStackedAbove(grandchild, child));
   EXPECT_TRUE(IsWindowStackedAbove(child, parent.get()));
-  if (!IsAuraMusClient())
+  if (check_toplevel_z_order)
     EXPECT_TRUE(IsWindowStackedAbove(parent.get(), popover.get()));
 }
 
diff --git a/ui/webui/resources/html/cr/ui/array_data_model.html b/ui/webui/resources/html/cr/ui/array_data_model.html
new file mode 100644
index 0000000..56ae242
--- /dev/null
+++ b/ui/webui/resources/html/cr/ui/array_data_model.html
@@ -0,0 +1 @@
+<script src="chrome://resources/js/cr/ui/array_data_model.js"></script>
\ No newline at end of file
diff --git a/ui/webui/resources/webui_resources.grd b/ui/webui/resources/webui_resources.grd
index 0190e3f..6f35491 100644
--- a/ui/webui/resources/webui_resources.grd
+++ b/ui/webui/resources/webui_resources.grd
@@ -277,6 +277,8 @@
                  file="html/cr/ui.html" type="chrome_html" />
       <structure name="IDR_WEBUI_HTML_CR_UI_ALERT_OVERLAY"
                  file="html/cr/ui/alert_overlay.html" type="chrome_html" />
+      <structure name="IDR_WEBUI_HTML_CR_UI_ARRAY_DATA_MODEL"
+                 file="html/cr/ui/array_data_model.html" type="chrome_html" />
       <structure name="IDR_WEBUI_HTML_CR_UI_COMMAND"
                  file="html/cr/ui/command.html" type="chrome_html" />
       <structure name="IDR_WEBUI_HTML_CR_UI_CONTEXT_MENU_BUTTON"
diff --git a/url/OWNERS b/url/OWNERS
index 02323de..a37e762 100644
--- a/url/OWNERS
+++ b/url/OWNERS
@@ -1,3 +1,2 @@
-abarth@chromium.org
 brettw@chromium.org
 mkwst@chromium.org