diff --git a/BUILD.gn b/BUILD.gn
index 9f220a0..f7380652d 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -399,7 +399,6 @@
   if (is_linux) {
     # The following are definitely linux-only.
     deps += [
-      "//chrome:manpage",
       "//chrome:xdg_mime",
       "//net:disk_cache_memory_test",
       "//net:quic_client",
diff --git a/DEPS b/DEPS
index 487487d..556c173 100644
--- a/DEPS
+++ b/DEPS
@@ -69,7 +69,7 @@
   # 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': '15dffdf6bd8d595193cfb072974a8efeb8052a91',
+  'pdfium_revision': '7d04f1b0ab4848f1d10983b7a7b1444ac93dec70',
   # 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.
@@ -97,11 +97,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': '02e80da6090c21d6e59ac955b7f56e1ad4a9850b',
+  'freetype_revision': '6f2b6f8f72ffb5017ab00fca83185b21f1a9f56d',
   # 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': '0b563bed301ef780e6f0cac7bab670aaffb65fdf',
+  'catapult_revision': 'cf05c91b67574954c0f62d369077943f04f5de07',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 196e103..c6b6444 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -78,6 +78,7 @@
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.navigation_controller.LoadURLType;
 import org.chromium.content_public.browser.navigation_controller.UserAgentOverrideOption;
+import org.chromium.content_public.common.BrowserSideNavigationPolicy;
 import org.chromium.content_public.common.ContentUrlConstants;
 import org.chromium.content_public.common.Referrer;
 import org.chromium.device.gamepad.GamepadList;
@@ -1663,7 +1664,8 @@
             requestVisitedHistoryFromClient();
         }
 
-        if (params.getLoadUrlType() == LoadURLType.DATA && params.getBaseUrl() != null) {
+        if (params.getLoadUrlType() == LoadURLType.DATA && params.getBaseUrl() != null
+                && !BrowserSideNavigationPolicy.isBrowserSideNavigationEnabled()) {
             // Data loads with a base url will be resolved in Blink, and not cause an onPageStarted
             // event to be sent. Sending the callback directly from here.
             mContentsClient.getCallbackHelper().postOnPageStarted(params.getBaseUrl());
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java
index 7f5f5f5..06f42eb2 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java
@@ -187,11 +187,18 @@
         final String baseUrl = "http://base.com/";
         TestCallbackHelperContainer.OnPageStartedHelper onPageStartedHelper =
                 mContentsClient.getOnPageStartedHelper();
-        final int callCount = onPageStartedHelper.getCallCount();
+        TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
+                mContentsClient.getOnPageFinishedHelper();
+        final int pageStartedCount = onPageStartedHelper.getCallCount();
+        final int pageFinishedCount = onPageFinishedHelper.getCallCount();
         loadDataWithBaseUrlAsync(mAwContents, CommonResources.ABOUT_HTML, "text/html", false,
                 baseUrl, ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
-        onPageStartedHelper.waitForCallback(callCount);
+        onPageStartedHelper.waitForCallback(pageStartedCount);
         assertEquals(baseUrl, onPageStartedHelper.getUrl());
+
+        onPageFinishedHelper.waitForCallback(pageFinishedCount);
+        assertEquals("onPageStarted should only be called once", pageStartedCount + 1,
+                onPageStartedHelper.getCallCount());
     }
 
     @SmallTest
diff --git a/ash/shelf/app_list_button.cc b/ash/shelf/app_list_button.cc
index e01832e7..346a016 100644
--- a/ash/shelf/app_list_button.cc
+++ b/ash/shelf/app_list_button.cc
@@ -148,17 +148,7 @@
   switch (event->type()) {
     case ui::ET_GESTURE_SCROLL_BEGIN:
       AnimateInkDrop(views::InkDropState::HIDDEN, event);
-      shelf_view_->PointerPressedOnButton(this, ShelfView::TOUCH, *event);
-      event->SetHandled();
-      return;
-    case ui::ET_GESTURE_SCROLL_UPDATE:
-      shelf_view_->PointerDraggedOnButton(this, ShelfView::TOUCH, *event);
-      event->SetHandled();
-      return;
-    case ui::ET_GESTURE_SCROLL_END:
-    case ui::ET_SCROLL_FLING_START:
-      shelf_view_->PointerReleasedOnButton(this, ShelfView::TOUCH, false);
-      event->SetHandled();
+      ImageButton::OnGestureEvent(event);
       return;
     case ui::ET_GESTURE_TAP:
     case ui::ET_GESTURE_TAP_CANCEL:
diff --git a/ash/shelf/app_list_button_unittest.cc b/ash/shelf/app_list_button_unittest.cc
index 7612284..bff24bd1 100644
--- a/ash/shelf/app_list_button_unittest.cc
+++ b/ash/shelf/app_list_button_unittest.cc
@@ -4,9 +4,11 @@
 
 #include "ash/shelf/app_list_button.h"
 
+#include "ash/public/cpp/config.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller.h"
 #include "ash/shelf/shelf.h"
+#include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/shelf_view.h"
 #include "ash/shelf/shelf_view_test_api.h"
 #include "ash/shell.h"
@@ -19,6 +21,7 @@
 #include "ui/app_list/presenter/app_list.h"
 #include "ui/app_list/presenter/test/test_app_list_presenter.h"
 #include "ui/events/event_constants.h"
+#include "ui/events/test/event_generator.h"
 
 namespace ash {
 
@@ -91,6 +94,34 @@
   EXPECT_EQ(0u, test_app_list_presenter.voice_session_count());
 }
 
+TEST_F(AppListButtonTest, SwipingupToOpenFullscreenAppList) {
+  // TODO: investigate failure in mash, http://crbug.com/695686.
+  if (Shell::GetAshConfig() == Config::MASH)
+    return;
+
+  Shelf* shelf = GetPrimaryShelf();
+  EXPECT_EQ(SHELF_ALIGNMENT_BOTTOM, shelf->alignment());
+
+  // Start the drag from the center of the applist button's bottom.
+  gfx::Point center_point = app_list_button()->GetAppListButtonCenterPoint();
+  gfx::Point start(center_point.x(),
+                   center_point.y() + app_list_button()->height() / 2.f);
+  views::View::ConvertPointToScreen(app_list_button(), &start);
+  // Swiping up less than peeking threshold should keep the app list at PEEKING
+  // state.
+  gfx::Point end =
+      start -
+      gfx::Vector2d(
+          0, ShelfLayoutManager::kAppListDragSnapToPeekingThreshold - 10);
+  GetEventGenerator().GestureScrollSequence(
+      start, end, base::TimeDelta::FromMilliseconds(100), 4 /* steps */);
+  RunAllPendingInMessageLoop();
+  EXPECT_EQ(1u, test_app_list_presenter.show_count());
+  EXPECT_GE(test_app_list_presenter.set_y_position_count(), 1u);
+  EXPECT_EQ(app_list::mojom::AppListState::PEEKING,
+            test_app_list_presenter.app_list_state());
+}
+
 class VoiceInteractionAppListButtonTest : public AppListButtonTest {
  public:
   VoiceInteractionAppListButtonTest() {}
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 9a13c28..7326996 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -1656,6 +1656,7 @@
             test_app_list_presenter.app_list_state());
 
   // Swiping up more than the close threshold but less than peeking threshold
+  // should keep the app list at PEEKING state.
   delta.set_y(ShelfLayoutManager::kAppListDragSnapToPeekingThreshold - 10);
   end = start - delta;
   generator.GestureScrollSequence(start, end, kTimeDelta, kNumScrollSteps);
diff --git a/build/android/resource_sizes.py b/build/android/resource_sizes.py
index 7362223..7bee07b4 100755
--- a/build/android/resource_sizes.py
+++ b/build/android/resource_sizes.py
@@ -3,10 +3,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Prints the size of each given file and optionally computes the size of
-   libchrome.so without the dependencies added for building with android NDK.
-   Also breaks down the contents of the APK to determine the installed size
-   and assign size contributions to different classes of file.
+"""Reports binary size and static initializer metrics for an APK.
+
+More information at //docs/speed/binary_size/metrics.md.
 """
 
 import argparse
@@ -851,6 +850,9 @@
     argparser.error(
         '--dump-static-initializers requires --chromium-output-directory')
 
+  # Do not add any new metrics without also documenting them in:
+  # //docs/speed/binary_size/metrics.md.
+
   PrintApkAnalysis(args.apk, tool_prefix, out_dir, chartjson=chartjson)
   _PrintDexAnalysis(args.apk, chartjson=chartjson)
 
diff --git a/cc/layers/layer_impl_unittest.cc b/cc/layers/layer_impl_unittest.cc
index c17ccb7e..c8ac16a 100644
--- a/cc/layers/layer_impl_unittest.cc
+++ b/cc/layers/layer_impl_unittest.cc
@@ -486,7 +486,7 @@
  public:
   LayerImplScrollTest() : LayerImplScrollTest(LayerTreeSettings()) {}
 
-  LayerImplScrollTest(const LayerTreeSettings& settings)
+  explicit LayerImplScrollTest(const LayerTreeSettings& settings)
       : host_impl_(settings, &task_runner_provider_, &task_graph_runner_),
         root_id_(7) {
     host_impl_.active_tree()->SetRootLayerForTesting(
diff --git a/cc/resources/video_resource_updater.cc b/cc/resources/video_resource_updater.cc
index faeba2f1..70a52f34 100644
--- a/cc/resources/video_resource_updater.cc
+++ b/cc/resources/video_resource_updater.cc
@@ -210,7 +210,6 @@
     viz::ResourceFormat resource_format,
     const gfx::ColorSpace& color_space,
     bool software_resource,
-    bool immutable_hint,
     int unique_id,
     int plane_index) {
   ResourceList::iterator recyclable_resource = all_resources_.end();
@@ -240,8 +239,7 @@
 
     if (!in_use && it->resource_size() == resource_size &&
         it->resource_format() == resource_format &&
-        it->mailbox().IsZero() == software_resource &&
-        resource_provider_->IsImmutable(it->resource_id()) == immutable_hint) {
+        it->mailbox().IsZero() == software_resource) {
       recyclable_resource = it;
     }
   }
@@ -251,22 +249,19 @@
 
   // There was nothing available to reuse or recycle. Allocate a new resource.
   return AllocateResource(resource_size, resource_format, color_space,
-                          !software_resource, immutable_hint);
+                          !software_resource);
 }
 
 VideoResourceUpdater::ResourceList::iterator
 VideoResourceUpdater::AllocateResource(const gfx::Size& plane_size,
                                        viz::ResourceFormat format,
                                        const gfx::ColorSpace& color_space,
-                                       bool has_mailbox,
-                                       bool immutable_hint) {
+                                       bool has_mailbox) {
   // TODO(danakj): Abstract out hw/sw resource create/delete from
   // ResourceProvider and stop using ResourceProvider in this class.
   const viz::ResourceId resource_id = resource_provider_->CreateResource(
-      plane_size,
-      immutable_hint ? ResourceProvider::TEXTURE_HINT_IMMUTABLE
-                     : ResourceProvider::TEXTURE_HINT_DEFAULT,
-      format, color_space);
+      plane_size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
+      color_space);
   DCHECK_NE(resource_id, 0u);
 
   gpu::Mailbox mailbox;
@@ -389,10 +384,9 @@
       return VideoFrameExternalResources();
     }
 
-    const bool is_immutable = true;
     ResourceList::iterator resource_it = RecycleOrAllocateResource(
         output_plane_resource_size, output_resource_format, output_color_space,
-        software_compositor, is_immutable, video_frame->unique_id(), i);
+        software_compositor, video_frame->unique_id(), i);
 
     resource_it->add_ref();
     plane_resources.push_back(resource_it);
@@ -623,13 +617,12 @@
   // target to avoid loss of precision or dropping any alpha component.
   const viz::ResourceFormat copy_target_format = viz::ResourceFormat::RGBA_8888;
 
-  const bool is_immutable = false;
   const int no_unique_id = 0;
   const int no_plane_index = -1;  // Do not recycle referenced textures.
   VideoResourceUpdater::ResourceList::iterator resource =
       RecycleOrAllocateResource(output_plane_resource_size, copy_target_format,
-                                resource_color_space, false, is_immutable,
-                                no_unique_id, no_plane_index);
+                                resource_color_space, false, no_unique_id,
+                                no_plane_index);
   resource->add_ref();
 
   ResourceProvider::ScopedWriteLockGL lock(resource_provider_,
diff --git a/cc/resources/video_resource_updater.h b/cc/resources/video_resource_updater.h
index f7dc52d..34bf4bf 100644
--- a/cc/resources/video_resource_updater.h
+++ b/cc/resources/video_resource_updater.h
@@ -154,14 +154,12 @@
       viz::ResourceFormat resource_format,
       const gfx::ColorSpace& color_space,
       bool software_resource,
-      bool immutable_hint,
       int unique_id,
       int plane_index);
   ResourceList::iterator AllocateResource(const gfx::Size& plane_size,
                                           viz::ResourceFormat format,
                                           const gfx::ColorSpace& color_space,
-                                          bool has_mailbox,
-                                          bool immutable_hint);
+                                          bool has_mailbox);
   void DeleteResource(ResourceList::iterator resource_it);
   void CopyPlaneTexture(media::VideoFrame* video_frame,
                         const gfx::ColorSpace& resource_color_space,
diff --git a/cc/resources/video_resource_updater_unittest.cc b/cc/resources/video_resource_updater_unittest.cc
index f5600d0..238d6b8 100644
--- a/cc/resources/video_resource_updater_unittest.cc
+++ b/cc/resources/video_resource_updater_unittest.cc
@@ -41,7 +41,6 @@
                        GLuint internalformat,
                        GLint width,
                        GLint height) override {
-    immutable_texture_created_ = true;
   }
 
   GLuint createTexture() override {
@@ -65,13 +64,9 @@
   int TextureCreationCount() { return created_texture_count_; }
   void ResetTextureCreationCount() { created_texture_count_ = 0; }
 
-  bool WasImmutableTextureCreated() { return immutable_texture_created_; }
-  void ResetImmutableTextureCreated() { immutable_texture_created_ = false; }
-
  private:
   int upload_count_;
   int created_texture_count_;
-  bool immutable_texture_created_;
 };
 
 class SharedBitmapManagerAllocationCounter : public TestSharedBitmapManager {
@@ -575,7 +570,6 @@
   // GL_TEXTURE_2D texture.
   context3d_->ResetTextureCreationCount();
   video_frame = CreateTestStreamTextureHardwareVideoFrame(true);
-  context3d_->ResetImmutableTextureCreated();
   resources = updater.CreateExternalResourcesFromVideoFrame(video_frame);
   EXPECT_EQ(VideoFrameExternalResources::RGBA_PREMULTIPLIED_RESOURCE,
             resources.type);
@@ -584,13 +578,6 @@
   EXPECT_EQ(1u, resources.release_callbacks.size());
   EXPECT_EQ(0u, resources.software_resources.size());
   EXPECT_EQ(1, context3d_->TextureCreationCount());
-
-  // The texture copy path requires the use of CopyTextureCHROMIUM, which
-  // enforces that the target texture not be immutable, as it may need
-  // to alter the storage of the texture. Therefore, this test asserts
-  // that an immutable texture wasn't created by glTexStorage2DEXT, when
-  // that extension is supported.
-  EXPECT_FALSE(context3d_->WasImmutableTextureCreated());
 }
 
 TEST_F(VideoResourceUpdaterTest, CreateForHardwarePlanes_TextureQuad) {
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index c828018..e8387e1d 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -117,7 +117,6 @@
   group("chrome") {
     public_deps = [
       ":chrome_initial",
-      "//tools/v8_context_snapshot:v8_context_snapshot",
     ]
     data_deps = [
       ":chrome_initial",
@@ -260,7 +259,6 @@
           # Chromium functionality directly into the executable.
           ":browser_dependencies",
           ":child_dependencies",
-          ":manpage",
 
           # Needed to use the master_preferences functions
           "//chrome/installer/util:with_no_strings",
@@ -445,6 +443,7 @@
         # The browser DLL may not depend on blink or v8.
         "//third_party/WebKit/public:blink",
         "//gin",
+        "//tools/v8_context_snapshot",
         "//v8",
       ]
     } else {
@@ -883,7 +882,7 @@
     public_deps = [
       ":packed_resources",
       "//chrome/app_shim:app_mode_loader",
-      "//tools/v8_context_snapshot:v8_context_snapshot",
+      "//tools/v8_context_snapshot",
     ]
 
     if (is_chrome_branded) {
@@ -1444,6 +1443,7 @@
       assert_no_deps += [
         # V8/Gin should not be used in the browser DLL on Windows.
         "//gin",
+        "//tools/v8_context_snapshot",
         "//v8",
       ]
     }
@@ -1778,7 +1778,7 @@
 if (enable_resource_whitelist_generation) {
   generate_resource_whitelist("resource_whitelist") {
     deps = [
-      "//chrome/android:chrome",
+      "//chrome/android:libchrome",
     ]
     input = "$root_out_dir/libchrome$shlib_extension.whitelist"
     output = chrome_resource_whitelist
@@ -1786,37 +1786,6 @@
 }
 
 if (is_linux) {
-  action("manpage") {
-    if (is_chrome_branded) {
-      name = "Google Chrome"
-      filename = "google-chrome"
-      confdir = "google-chrome"
-    } else {
-      name = "Chromium"
-      filename = "chromium-browser"
-      confdir = "chromium"
-    }
-
-    script = "//chrome/tools/build/linux/sed.py"
-    infile = "app/resources/manpage.1.in"
-    inputs = [
-      infile,
-    ]
-
-    outfile = "$root_out_dir/chrome.1"
-    outputs = [
-      outfile,
-    ]
-
-    args = [
-      rebase_path(infile, root_build_dir),
-      rebase_path(outfile, root_build_dir),
-      "-e s/@@NAME@@/$name/",
-      "-e s/@@FILENAME@@/$filename/",
-      "-e s/@@CONFDIR@@/$confdir/",
-    ]
-  }
-
   if (is_official_build) {
     action("linux_symbols") {
       script = "//build/linux/dump_app_syms.py"
diff --git a/chrome/android/java/res/layout/accessibility_tab_switcher_list_item.xml b/chrome/android/java/res/layout/accessibility_tab_switcher_list_item.xml
index 66fdd7f..a19c4c3 100644
--- a/chrome/android/java/res/layout/accessibility_tab_switcher_list_item.xml
+++ b/chrome/android/java/res/layout/accessibility_tab_switcher_list_item.xml
@@ -77,6 +77,12 @@
         android:layout_height="match_parent"
         android:visibility="invisible">
 
+        <Space
+            android:id="@+id/undo_start_space"
+            android:layout_width="24dp"
+            android:layout_height="match_parent"
+            android:visibility="gone" />
+
         <Button
             android:id="@+id/undo_button"
             android:layout_height="match_parent"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index b717efd66..0176788 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -1711,8 +1711,11 @@
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
             if (newConfig.densityDpi != mDensityDpi) {
+                if (!VrShellDelegate.onDensityChanged(mDensityDpi, newConfig.densityDpi)) {
+                    recreate();
+                    return;
+                }
                 mDensityDpi = newConfig.densityDpi;
-                if (!VrShellDelegate.onDensityChanged()) recreate();
             }
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index a3c0ec83..2dc5e3f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -152,6 +152,7 @@
     public static final String ANDROID_PAYMENT_APPS = "AndroidPaymentApps";
     public static final String ANDROID_SIGNIN_PROMOS = "AndroidSigninPromos";
     public static final String AUTOFILL_SCAN_CARDHOLDER_NAME = "AutofillScanCardholderName";
+    public static final String CAPTIVE_PORTAL_CERTIFICATE_LIST = "CaptivePortalCertificateList";
     public static final String CCT_BACKGROUND_TAB = "CCTBackgroundTab";
     public static final String CCT_EXTERNAL_LINK_HANDLING = "CCTExternalLinkHandling";
     public static final String CCT_POST_MESSAGE_API = "CCTPostMessageAPI";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/media/router/OWNERS
index e98d2ad..9d94bc48 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/OWNERS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/OWNERS
@@ -1,5 +1,7 @@
 avayvod@chromium.org
+imcheng@chromium.org
 mlamouri@chromium.org
+zqzhang@chromium.org
 
 # TEAM: media-dev@chromium.org
 # COMPONENT: Blink>PresentationAPI
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 ef0b699d..08e4f1b 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
@@ -1392,9 +1392,7 @@
 
         changeLocationBarIcon();
         updateLocationBarIconContainerVisibility();
-        // Since we emphasize the scheme of the URL based on the security type, we need to
-        // refresh the emphasis.
-        mUrlBar.deEmphasizeUrl();
+
         emphasizeUrl();
         mIsEmphasizingHttpsScheme = shouldEmphasizeHttpsScheme;
     }
@@ -1405,7 +1403,9 @@
 
     @Override
     public boolean shouldEmphasizeHttpsScheme() {
-        if (mToolbarDataProvider.isUsingBrandColor() || mToolbarDataProvider.isIncognito()) {
+        if (!ColorUtils.isUsingDefaultToolbarColor(
+                    getResources(), mToolbarDataProvider.getPrimaryColor())
+                || mToolbarDataProvider.isIncognito()) {
             return false;
         }
         return true;
@@ -2147,7 +2147,6 @@
             setUrlBarText("", null);
         } else {
             if (setUrlBarText(url, displayText)) {
-                mUrlBar.deEmphasizeUrl();
                 emphasizeUrl();
             }
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
index f6b6452..5b763736 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
@@ -275,7 +275,6 @@
         }
 
         if (!hasFocus()) {
-            deEmphasizeUrl();
             emphasizeUrl();
         }
     }
@@ -800,7 +799,7 @@
      */
     public void emphasizeUrl() {
         Editable url = getText();
-        if (OmniboxUrlEmphasizer.hasEmphasisSpans(url) || hasFocus()) {
+        if (hasFocus()) {
             return;
         }
 
@@ -819,6 +818,9 @@
             // Ignore as this only is for applying color
         }
 
+        // Since we emphasize the scheme of the URL based on the security type, we need to
+        // deEmphasize first to refresh.
+        deEmphasizeUrl();
         OmniboxUrlEmphasizer.emphasizeUrl(url, getResources(), currentTab.getProfile(),
                 currentTab.getSecurityLevel(), isInternalPage,
                 mUseDarkColors, mUrlBarDelegate.shouldEmphasizeHttpsScheme());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ssl/CaptivePortalHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/ssl/CaptivePortalHelper.java
new file mode 100644
index 0000000..ea514052
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ssl/CaptivePortalHelper.java
@@ -0,0 +1,47 @@
+// Copyright 2017 The Chromium 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.ssl;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/** Helper class for captive portal related methods on Android. */
+@JNINamespace("chrome::android")
+public class CaptivePortalHelper {
+    public static void addCaptivePortalCertificateForTesting(String spkiHash) {
+        nativeAddCaptivePortalCertificateForTesting(spkiHash);
+    }
+
+    @CalledByNative
+    private static String getCaptivePortalServerUrl() {
+        // Since Android N MR2 it is possible that a captive portal was detected with a different
+        // URL than getCaptivePortalServerUrl(). By default, Android uses the URL from
+        // getCaptivePortalServerUrl() first, but there are also two additional fallback HTTP URLs
+        // to probe if the first HTTP probe does not find anything. Using the default URL is
+        // acceptable as the return value is only used by the captive portal interstitial.
+        try {
+            Context context = ContextUtils.getApplicationContext();
+            ConnectivityManager connectivityManager =
+                    (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+            Method getCaptivePortalServerUrlMethod =
+                    connectivityManager.getClass().getMethod("getCaptivePortalServerUrl");
+            return (String) getCaptivePortalServerUrlMethod.invoke(connectivityManager);
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            // To avoid crashing, return the default portal check URL on Android.
+            return "http://connectivitycheck.gstatic.com/generate_204";
+        }
+    }
+
+    private CaptivePortalHelper() {}
+
+    private static native void nativeAddCaptivePortalCertificateForTesting(String spkiHash);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
index f7115df..f790eda 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
@@ -254,9 +254,8 @@
 
     @Override
     public boolean shouldEmphasizeHttpsScheme() {
-        int securityLevel = getSecurityLevel();
-        return securityLevel == ConnectionSecurityLevel.DANGEROUS
-                || securityLevel == ConnectionSecurityLevel.SECURE_WITH_POLICY_INSTALLED_CERT;
+        return ColorUtils.isUsingDefaultToolbarColor(
+                getResources(), getToolbarDataProvider().getPrimaryColor());
     }
 
     @Override
@@ -388,7 +387,6 @@
         }
 
         if (mUrlBar.setUrl(url, displayText)) {
-            mUrlBar.deEmphasizeUrl();
             mUrlBar.emphasizeUrl();
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShell.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShell.java
index ee80947..f1335eb8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShell.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShell.java
@@ -85,4 +85,9 @@
      *  Triggers VrShell to navigate backward.
      */
     void navigateBack();
+
+    /**
+     * Should be called when the density changes. Updates UI in response to the new density.
+     */
+    void onDensityChanged(float oldDpi, float newDpi);
 }
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 6bdc6467..c226746 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
@@ -423,7 +423,7 @@
      * @return Whether VrShellDelegate handled the density change. If the density change is
      * unhandled, the Activity should be recreated in order to handle the change.
      */
-    public static boolean onDensityChanged() {
+    public static boolean onDensityChanged(float oldDpi, float newDpi) {
         if (sInstance == null) return false;
         // If density changed while in VR, we expect a second density change to restore the density
         // to what it previously was when we exit VR. We shouldn't have to recreate the activity as
@@ -434,6 +434,7 @@
             return true;
         }
         if (sInstance.mInVr || sInstance.mDonSucceeded) {
+            sInstance.onDensityChangedInternal(oldDpi, newDpi);
             sInstance.mDensityChanged = true;
             return true;
         }
@@ -1517,6 +1518,10 @@
         mRestoreSystemUiVisibilityFlag = -1;
     }
 
+    private void onDensityChangedInternal(float oldDpi, float newDpi) {
+        if (mVrShell != null) mVrShell.onDensityChanged(oldDpi, newDpi);
+    }
+
     /**
      * Clean up VrShell, and associated native objects.
      */
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 bc14e76..40242abe 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
@@ -14,8 +14,12 @@
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
 import android.view.ViewTreeObserver.OnPreDrawListener;
 import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.RelativeLayout;
 
 import com.google.vr.ndk.base.AndroidCompat;
 import com.google.vr.ndk.base.GvrLayout;
@@ -610,6 +614,46 @@
     }
 
     @Override
+    public void onDensityChanged(float oldDpi, float newDpi) {
+        // TODO(mthiesse, crbug.com/767603): Remove this workaround for b/66493165.
+        // This is extremely hacky. The GvrUiLayout doesn't update in response to density changes,
+        // so we manually go in and scale their elements to be the correct size (though due to the
+        // scaling they don't actually look pixel-perfectly identical to what they should be).
+        // These elements are dynamically loaded and inserted into the view hierarchy so we don't
+        // have IDs for them that we can look up.
+        try {
+            float scale = newDpi / oldDpi;
+            ViewGroup gvrLayoutImpl = (ViewGroup) getContainer().getChildAt(0);
+            RelativeLayout relativeLayout = (RelativeLayout) gvrLayoutImpl.getChildAt(1);
+
+            ImageButton x_button = (ImageButton) relativeLayout.getChildAt(0);
+            RelativeLayout alignment_marker = (RelativeLayout) relativeLayout.getChildAt(1);
+            ImageButton settings_button = (ImageButton) relativeLayout.getChildAt(2);
+
+            ViewGroup.LayoutParams params = alignment_marker.getLayoutParams();
+            params.width = (int) (params.width * scale);
+            params.height = (int) (params.height * scale);
+            alignment_marker.setLayoutParams(params);
+
+            int padding = (int) (x_button.getPaddingLeft() * scale);
+
+            x_button.setImageDrawable(x_button.getDrawable().getConstantState().newDrawable(
+                    mActivity.getResources()));
+            x_button.setPadding(padding, padding, padding, padding);
+
+            settings_button.setImageDrawable(
+                    settings_button.getDrawable().getConstantState().newDrawable(
+                            mActivity.getResources()));
+            settings_button.setPadding(padding, padding, padding, padding);
+        } catch (Throwable e) {
+            // Ignore any errors. We're working around a bug in dynamically loaded code, so if it
+            // goes wrong that means the loaded code changed. ¯\_(ツ)_/¯
+            // In the worst case the close and settings buttons won't be drawn for the correct
+            // density.
+        }
+    }
+
+    @Override
     public void surfaceCreated(SurfaceHolder holder) {
         nativeSetSurface(mNativeVrShell, holder.getSurface());
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/accessibility/AccessibilityTabModelListItem.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/accessibility/AccessibilityTabModelListItem.java
index 02cb258..15d960cb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/accessibility/AccessibilityTabModelListItem.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/accessibility/AccessibilityTabModelListItem.java
@@ -246,6 +246,10 @@
 
         mUndoContents = (LinearLayout) findViewById(R.id.undo_contents);
         mUndoButton = (Button) findViewById(R.id.undo_button);
+        if (FeatureUtilities.isChromeHomeEnabled()) {
+            findViewById(R.id.undo_start_space).setVisibility(View.VISIBLE);
+            ApiCompatibilityUtils.setTextAppearance(mUndoButton, R.style.BlueButtonText2);
+        }
 
         setClickable(true);
         setFocusable(true);
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index ab09d1d..c1de1ab 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -1039,6 +1039,7 @@
   "java/src/org/chromium/chrome/browser/snackbar/TemplatePreservingTextView.java",
   "java/src/org/chromium/chrome/browser/snackbar/smartlockautosignin/AutoSigninSnackbarController.java",
   "java/src/org/chromium/chrome/browser/snackbar/undo/UndoBarController.java",
+  "java/src/org/chromium/chrome/browser/ssl/CaptivePortalHelper.java",
   "java/src/org/chromium/chrome/browser/ssl/SecurityStateModel.java",
   "java/src/org/chromium/chrome/browser/suggestions/ContextualSuggestionsCardViewHolder.java",
   "java/src/org/chromium/chrome/browser/suggestions/DestructionObserver.java",
@@ -1660,6 +1661,7 @@
   "javatests/src/org/chromium/chrome/browser/printing/PrintingControllerTest.java",
   "javatests/src/org/chromium/chrome/browser/provider/ProviderBookmarkNodeUnitTest.java",
   "javatests/src/org/chromium/chrome/browser/provider/ProviderBookmarksUriTest.java",
+  "javatests/src/org/chromium/chrome/browser/ssl/CaptivePortalTest.java",
   "javatests/src/org/chromium/chrome/browser/provider/ProviderSearchesUriTest.java",
   "javatests/src/org/chromium/chrome/browser/provider/ProviderTestRule.java",
   "javatests/src/org/chromium/chrome/browser/push_messaging/PushMessagingTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
index e19f183..1040eea 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
@@ -824,6 +824,7 @@
                 "A custom tab toolbar is never shown", toolbarView instanceof CustomTabToolbar);
         CustomTabToolbar toolbar = (CustomTabToolbar) toolbarView;
         Assert.assertEquals(expectedColor, toolbar.getBackground().getColor());
+        Assert.assertFalse(toolbar.shouldEmphasizeHttpsScheme());
         if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
             Assert.assertEquals(ColorUtils.getDarkenedColorForStatusBar(expectedColor),
                     mCustomTabActivityTestRule.getActivity().getWindow().getStatusBarColor());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ssl/CaptivePortalTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ssl/CaptivePortalTest.java
new file mode 100644
index 0000000..1f53f367
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ssl/CaptivePortalTest.java
@@ -0,0 +1,105 @@
+// Copyright 2017 The Chromium 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.ssl;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.util.Base64;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.parameter.CommandLineParameter;
+import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.test.ChromeActivityTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.util.ChromeTabUtils;
+import org.chromium.chrome.test.util.browser.TabTitleObserver;
+import org.chromium.content.browser.test.util.Criteria;
+import org.chromium.content.browser.test.util.CriteriaHelper;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.net.X509Util;
+import org.chromium.net.test.EmbeddedTestServer;
+import org.chromium.net.test.ServerCertificate;
+import org.chromium.net.test.util.CertTestUtil;
+
+import java.util.concurrent.Callable;
+
+/** Tests for the Captive portal interstitial. */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@MediumTest
+@CommandLineFlags.Add({
+        ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
+        ChromeActivityTestRule.DISABLE_NETWORK_PREDICTION_FLAG,
+})
+@CommandLineParameter({"", "enable-features=" + ChromeFeatureList.CAPTIVE_PORTAL_CERTIFICATE_LIST})
+public class CaptivePortalTest {
+    private static final String CAPTIVE_PORTAL_INTERSTITIAL_TITLE_PREFIX = "Connect to";
+    private static final int INTERSTITIAL_TITLE_UPDATE_TIMEOUT_SECONDS = 5;
+
+    @Rule
+    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
+
+    private EmbeddedTestServer mServer;
+
+    @Before
+    public void setUp() throws Exception {
+        mActivityTestRule.startMainActivityFromLauncher();
+        mServer = EmbeddedTestServer.createAndStartHTTPSServer(
+                InstrumentationRegistry.getInstrumentation().getContext(),
+                ServerCertificate.CERT_MISMATCHED_NAME);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mServer.stopAndDestroyServer();
+    }
+
+    private void waitForInterstitial(final WebContents webContents, final boolean shouldBeShown) {
+        CriteriaHelper.pollUiThread(Criteria.equals(shouldBeShown, new Callable<Boolean>() {
+            @Override
+            public Boolean call() {
+                return webContents.isShowingInterstitialPage();
+            }
+        }));
+    }
+
+    @Test
+    public void testCaptivePortalInterstitial() throws Exception {
+        // Add the SPKI of the root cert to captive portal certificate list.
+        byte[] rootCertSPKI = CertTestUtil.getPublicKeySha256(X509Util.createCertificateFromBytes(
+                CertTestUtil.pemToDer(mServer.getRootCertPemPath())));
+        Assert.assertTrue(rootCertSPKI != null);
+        CaptivePortalHelper.addCaptivePortalCertificateForTesting(
+                "sha256/" + Base64.encodeToString(rootCertSPKI, Base64.NO_WRAP));
+
+        // Navigate the tab to an interstitial with a name mismatch error. This should
+        // result in a captive portal interstitial since the certificate's SPKI hash
+        // is added to the captive portal certificate list.
+        Tab tab = mActivityTestRule.getActivity().getActivityTab();
+        ChromeTabUtils.loadUrlOnUiThread(
+                tab, mServer.getURL("/chrome/test/data/android/navigate/simple.html"));
+        waitForInterstitial(tab.getWebContents(), true);
+        Assert.assertTrue(tab.isShowingInterstitialPage());
+
+        new TabTitleObserver(tab, CAPTIVE_PORTAL_INTERSTITIAL_TITLE_PREFIX) {
+            @Override
+            protected boolean doesTitleMatch(String expectedTitle, String actualTitle) {
+                return actualTitle.indexOf(expectedTitle) == 0;
+            }
+        }
+                .waitForTitleUpdate(INTERSTITIAL_TITLE_UPDATE_TIMEOUT_SECONDS);
+
+        Assert.assertEquals(0, tab.getTitle().indexOf(CAPTIVE_PORTAL_INTERSTITIAL_TITLE_PREFIX));
+    }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/OWNERS b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/OWNERS
index e98d2ad..9d94bc48 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/OWNERS
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/OWNERS
@@ -1,5 +1,7 @@
 avayvod@chromium.org
+imcheng@chromium.org
 mlamouri@chromium.org
+zqzhang@chromium.org
 
 # TEAM: media-dev@chromium.org
 # COMPONENT: Blink>PresentationAPI
diff --git a/chrome/app/resources/manpage.1.in b/chrome/app/resources/manpage.1.in
index c0d3960..04abbb3 100644
--- a/chrome/app/resources/manpage.1.in
+++ b/chrome/app/resources/manpage.1.in
@@ -1,12 +1,12 @@
-." This file is processed by chrome.gyp to generate manpages in the
+." This file is processed to generate manpages in the
 ." build diretory.
-.TH @@FILENAME@@ 1 "" "" "USER COMMANDS"
+.TH @@PACKAGE@@ 1 "" "" "USER COMMANDS"
 
 .SH NAME
-@@FILENAME@@ \- the web browser from Google
+@@PACKAGE@@ \- the web browser from Google
 
 .SH SYNOPSIS
-.B @@FILENAME@@
+.B @@PACKAGE@@
 [\fIOPTION\fR] [\fIPATH\fR|\fIURL\fR]
 
 .SH DESCRIPTION
@@ -17,16 +17,16 @@
 This manpage only describes invocation, environment, and arguments.
 
 .SH OPTIONS
-@@NAME@@ has hundreds of undocumented command-line flags that are added
+@@MENUNAME@@ has hundreds of undocumented command-line flags that are added
 and removed at the whim of the developers.  Here, we document relatively
 stable flags.
 .TP
 \fB\-\-user\-data\-dir\fR=\fIDIR\fR
 Specifies the directory that user data (your "profile") is kept in.
 Defaults to
-.I ~/.config/@@CONFDIR@@ .
-Separate instances of @@NAME@@ must use separate user data directories;
-repeated invocations of @@FILENAME@@ will reuse an existing process for
+.I ~/.config/@@PACKAGE@@ .
+Separate instances of @@MENUNAME@@ must use separate user data directories;
+repeated invocations of @@PACKAGE@@ will reuse an existing process for
 a given user data directory.
 
 .TP
@@ -108,7 +108,7 @@
 Show version information.
 
 .PP
-As a GTK+ app, @@NAME@@ also obeys GTK+ command-line flags, such
+As a GTK+ app, @@MENUNAME@@ also obeys GTK+ command-line flags, such
 as
 .BR \-\-display .
 See the GTK documentation for more:
@@ -117,7 +117,7 @@
 <http://library.gnome.org/devel/gtk/stable/gtk-x11.html>
 
 .SH ENVIRONMENT
-@@NAME@@ obeys the following environment variables:
+@@MENUNAME@@ obeys the following environment variables:
 
 .TP
 .B all_proxy
@@ -150,11 +150,11 @@
 
 .SH FILES
 .TP
-.I ~/.config/@@CONFDIR@@
+.I ~/.config/@@PACKAGE@@
 Default directory for configuration data.
 
 .TP
-.I ~/.cache/@@CONFDIR@@
+.I ~/.cache/@@PACKAGE@@
 Default directory for cache data.  (Why?  See
 <http://standards.freedesktop.org/basedir-spec/latest/> .)
 
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 9af9994..a9ede67 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1340,6 +1340,8 @@
     "speech/tts_win.cc",
     "ssl/bad_clock_blocking_page.cc",
     "ssl/bad_clock_blocking_page.h",
+    "ssl/captive_portal_blocking_page.cc",
+    "ssl/captive_portal_blocking_page.h",
     "ssl/cert_report_helper.cc",
     "ssl/cert_report_helper.h",
     "ssl/chrome_expect_ct_reporter.cc",
@@ -2572,8 +2574,6 @@
       "captive_portal/captive_portal_tab_reloader.h",
       "component_updater/ssl_error_assistant_component_installer.cc",
       "component_updater/ssl_error_assistant_component_installer.h",
-      "ssl/captive_portal_blocking_page.cc",
-      "ssl/captive_portal_blocking_page.h",
       "ssl/captive_portal_metrics_recorder.cc",
       "ssl/captive_portal_metrics_recorder.h",
     ]
@@ -3089,6 +3089,7 @@
       "search_engines/template_url_service_android.h",
       "signin/oauth2_token_service_delegate_android.cc",
       "signin/oauth2_token_service_delegate_android.h",
+      "ssl/captive_portal_helper_android.cc",
       "ssl/security_state_model_android.cc",
       "sync/glue/synced_tab_delegate_android.cc",
       "sync/glue/synced_tab_delegate_android.h",
@@ -4185,6 +4186,7 @@
       "../android/java/src/org/chromium/chrome/browser/signin/SigninManager.java",
       "../android/java/src/org/chromium/chrome/browser/signin/SigninPromoUtil.java",
       "../android/java/src/org/chromium/chrome/browser/snackbar/smartlockautosignin/AutoSigninSnackbarController.java",
+      "../android/java/src/org/chromium/chrome/browser/ssl/CaptivePortalHelper.java",
       "../android/java/src/org/chromium/chrome/browser/ssl/SecurityStateModel.java",
       "../android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSites.java",
       "../android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSitesBridge.java",
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 837f6835..3978854 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -137,6 +137,10 @@
       <if expr="safe_browsing_mode != 0">
         <include name="IDR_DOWNLOAD_FILE_TYPES_PB" file="${root_gen_dir}\chrome\browser\resources\safe_browsing\download_file_types.pb" use_base_dir="false" type="BINDATA" />
       </if>
+      <include name="IDR_DOWNLOAD_INTERNALS_HTML" file="resources\download_internals\download_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
+      <include name="IDR_DOWNLOAD_INTERNALS_CSS" file="resources\download_internals\download_internals.css" type="BINDATA" compress="gzip" />
+      <include name="IDR_DOWNLOAD_INTERNALS_JS" file="resources\download_internals\download_internals.js" type="BINDATA" compress="gzip" />
+      <include name="IDR_DOWNLOAD_INTERNALS_BROWSER_PROXY_JS" file="resources\download_internals\download_internals_browser_proxy.js" type="BINDATA" compress="gzip" />
       <if expr="not is_android">
         <include name="IDR_MD_DOWNLOADS_1X_INCOGNITO_MARKER_PNG" file="resources\md_downloads\1x\incognito_marker.png" type="BINDATA" />
         <include name="IDR_MD_DOWNLOADS_2X_INCOGNITO_MARKER_PNG" file="resources\md_downloads\2x\incognito_marker.png" type="BINDATA" />
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index 8db0115..f78f80f 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -75,7 +75,6 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/account_tracker_service_factory.h"
 #include "chrome/browser/signin/easy_unlock_service.h"
-#include "chrome/browser/signin/signin_error_controller_factory.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/supervised_user/child_accounts/child_account_service.h"
 #include "chrome/browser/supervised_user/child_accounts/child_account_service_factory.h"
@@ -106,7 +105,6 @@
 #include "components/session_manager/core/session_manager.h"
 #include "components/signin/core/account_id/account_id.h"
 #include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/signin_error_controller.h"
 #include "components/signin/core/browser/signin_manager_base.h"
 #include "components/user_manager/known_user.h"
 #include "components/user_manager/user.h"
@@ -847,15 +845,7 @@
   bool connection_error = false;
   switch (state) {
     case OAuth2LoginManager::SESSION_RESTORE_DONE:
-      // Session restore done does not always mean valid token because the
-      // merge session operation could be skipped when the first account in
-      // Gaia cookies matches the primary account in TokenService. However
-      // the token could still be invalid in some edge cases. See
-      // http://crbug.com/760610
-      user_status =
-          SigninErrorControllerFactory::GetForProfile(user_profile)->HasError()
-              ? user_manager::User::OAUTH2_TOKEN_STATUS_INVALID
-              : user_manager::User::OAUTH2_TOKEN_STATUS_VALID;
+      user_status = user_manager::User::OAUTH2_TOKEN_STATUS_VALID;
       break;
     case OAuth2LoginManager::SESSION_RESTORE_FAILED:
       user_status = user_manager::User::OAUTH2_TOKEN_STATUS_INVALID;
diff --git a/chrome/browser/chromeos/login/signin/auth_sync_observer.cc b/chrome/browser/chromeos/login/signin/auth_sync_observer.cc
index 48ad4c2..95a5cd0 100644
--- a/chrome/browser/chromeos/login/signin/auth_sync_observer.cc
+++ b/chrome/browser/chromeos/login/signin/auth_sync_observer.cc
@@ -10,97 +10,65 @@
 #include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
 #include "chrome/browser/chromeos/login/users/supervised_user_manager.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
-#include "chrome/browser/signin/signin_error_controller_factory.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "components/browser_sync/profile_sync_service.h"
-#include "components/signin/core/browser/signin_manager_base.h"
+#include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
 #include "components/user_manager/user_type.h"
+#include "google_apis/gaia/gaia_auth_util.h"
+
+class Profile;
+
+namespace browser_sync {
+class ProfileSyncService;
+}  // namespace browser_sync
 
 namespace chromeos {
 
-// static
-bool AuthSyncObserver::ShouldObserve(Profile* profile) {
-  const user_manager::User* const user =
-      ProfileHelper::Get()->GetUserByProfile(profile);
-  return user && (user->HasGaiaAccount() ||
-                  user->GetType() == user_manager::USER_TYPE_SUPERVISED);
+AuthSyncObserver::AuthSyncObserver(Profile* profile)
+    : profile_(profile) {
 }
 
-AuthSyncObserver::AuthSyncObserver(Profile* profile) : profile_(profile) {
-  DCHECK(ShouldObserve(profile));
+AuthSyncObserver::~AuthSyncObserver() {
 }
 
-AuthSyncObserver::~AuthSyncObserver() {}
-
 void AuthSyncObserver::StartObserving() {
-  browser_sync::ProfileSyncService* const sync_service =
+  browser_sync::ProfileSyncService* sync_service =
       ProfileSyncServiceFactory::GetForProfile(profile_);
   if (sync_service)
     sync_service->AddObserver(this);
-
-  SigninErrorController* const error_controller =
-      SigninErrorControllerFactory::GetForProfile(profile_);
-  if (error_controller) {
-    error_controller->AddObserver(this);
-    OnErrorChanged();
-  }
 }
 
 void AuthSyncObserver::Shutdown() {
-  browser_sync::ProfileSyncService* const sync_service =
+  browser_sync::ProfileSyncService* sync_service =
       ProfileSyncServiceFactory::GetForProfile(profile_);
   if (sync_service)
     sync_service->RemoveObserver(this);
-
-  SigninErrorController* const error_controller =
-      SigninErrorControllerFactory::GetForProfile(profile_);
-  if (error_controller)
-    error_controller->RemoveObserver(this);
 }
 
 void AuthSyncObserver::OnStateChanged(syncer::SyncService* sync) {
-  HandleAuthError(sync->GetAuthError());
-}
-
-void AuthSyncObserver::OnErrorChanged() {
-  SigninErrorController* const error_controller =
-      SigninErrorControllerFactory::GetForProfile(profile_);
-  const std::string error_account_id = error_controller->error_account_id();
-
-  const std::string primary_account_id =
-      SigninManagerFactory::GetForProfile(profile_)
-          ->GetAuthenticatedAccountId();
-
-  // Bail if there is an error account id and it is not the primary account id.
-  if (!error_account_id.empty() && error_account_id != primary_account_id)
-    return;
-
-  HandleAuthError(error_controller->auth_error());
-}
-
-void AuthSyncObserver::HandleAuthError(
-    const GoogleServiceAuthError& auth_error) {
-  const user_manager::User* const user =
+  DCHECK(user_manager::UserManager::Get()->IsLoggedInAsUserWithGaiaAccount() ||
+         user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser());
+  const user_manager::User* user =
       ProfileHelper::Get()->GetUserByProfile(profile_);
-  DCHECK(user->HasGaiaAccount() ||
-         user->GetType() == user_manager::USER_TYPE_SUPERVISED);
-
-  if (auth_error.IsPersistentError()) {
+  GoogleServiceAuthError::State state = sync->GetAuthError().state();
+  if (state != GoogleServiceAuthError::NONE &&
+      state != GoogleServiceAuthError::CONNECTION_FAILED &&
+      state != GoogleServiceAuthError::SERVICE_UNAVAILABLE &&
+      state != GoogleServiceAuthError::REQUEST_CANCELED) {
     // Invalidate OAuth2 refresh token to force Gaia sign-in flow. This is
     // needed because sign-out/sign-in solution is suggested to the user.
-    LOG(WARNING) << "Invalidate OAuth token because of an auth error: "
-                 << auth_error.ToString();
+    // TODO(nkostylev): Remove after crosbug.com/25978 is implemented.
+    LOG(WARNING) << "Invalidate OAuth token because of a sync error: "
+                 << sync->GetAuthError().ToString();
     const AccountId& account_id = user->GetAccountId();
     DCHECK(account_id.is_valid());
-
+    // TODO(nkostyelv): Change observer after active user has changed.
     user_manager::User::OAuthTokenStatus old_status =
         user->oauth_token_status();
     user_manager::UserManager::Get()->SaveUserOAuthStatus(
         account_id, user_manager::User::OAUTH2_TOKEN_STATUS_INVALID);
     RecordReauthReason(account_id, ReauthReason::SYNC_FAILED);
-
     if (user->GetType() == user_manager::USER_TYPE_SUPERVISED &&
         old_status != user_manager::User::OAUTH2_TOKEN_STATUS_INVALID) {
        // Attempt to restore token from file.
@@ -113,7 +81,7 @@
       base::RecordAction(
           base::UserMetricsAction("ManagedUsers_Chromeos_Sync_Invalidated"));
     }
-  } else if (auth_error.state() == GoogleServiceAuthError::NONE) {
+  } else if (state == GoogleServiceAuthError::NONE) {
     if (user->GetType() == user_manager::USER_TYPE_SUPERVISED &&
         user->oauth_token_status() ==
             user_manager::User::OAUTH2_TOKEN_STATUS_INVALID) {
diff --git a/chrome/browser/chromeos/login/signin/auth_sync_observer.h b/chrome/browser/chromeos/login/signin/auth_sync_observer.h
index 0647c35..a513a2b48 100644
--- a/chrome/browser/chromeos/login/signin/auth_sync_observer.h
+++ b/chrome/browser/chromeos/login/signin/auth_sync_observer.h
@@ -7,30 +7,23 @@
 
 #include <string>
 
+#include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "components/signin/core/browser/signin_error_controller.h"
 #include "components/sync/driver/sync_service_observer.h"
 
-class GoogleServiceAuthError;
 class Profile;
 
 namespace chromeos {
 
 // This class is responsible for detecting authentication problems reported
-// by sync service and SigninErrorController on a user profile.
+// by sync service and
 class AuthSyncObserver : public KeyedService,
-                         public syncer::SyncServiceObserver,
-                         public SigninErrorController::Observer {
+                         public syncer::SyncServiceObserver {
  public:
-  // Whether |profile| should be observed. Currently, this returns true only
-  // when |profile| is a user profile of a gaia user or a supervised user.
-  static bool ShouldObserve(Profile* profile);
-
-  explicit AuthSyncObserver(Profile* profile);
+  explicit AuthSyncObserver(Profile* user_profile);
   ~AuthSyncObserver() override;
 
-  // Starts to observe SyncService and SigninErrorController.
   void StartObserving();
 
  private:
@@ -42,16 +35,10 @@
   // syncer::SyncServiceObserver implementation.
   void OnStateChanged(syncer::SyncService* sync) override;
 
-  // SigninErrorController::Observer implementation.
-  void OnErrorChanged() override;
-
-  // Handles an auth error.
-  void HandleAuthError(const GoogleServiceAuthError& auth_error);
-
   // Called on attempt to restore supervised user token.
   void OnSupervisedTokenLoaded(const std::string& token);
 
-  Profile* const profile_;
+  Profile* profile_;
 
   DISALLOW_COPY_AND_ASSIGN(AuthSyncObserver);
 };
diff --git a/chrome/browser/chromeos/login/signin/auth_sync_observer_factory.cc b/chrome/browser/chromeos/login/signin/auth_sync_observer_factory.cc
index fd314e96..7bbd5b7 100644
--- a/chrome/browser/chromeos/login/signin/auth_sync_observer_factory.cc
+++ b/chrome/browser/chromeos/login/signin/auth_sync_observer_factory.cc
@@ -6,7 +6,6 @@
 
 #include "chrome/browser/chromeos/login/signin/auth_sync_observer.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/signin_error_controller_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 
@@ -17,7 +16,6 @@
         "AuthSyncObserver",
         BrowserContextDependencyManager::GetInstance()) {
   DependsOn(ProfileSyncServiceFactory::GetInstance());
-  DependsOn(SigninErrorControllerFactory::GetInstance());
 }
 
 AuthSyncObserverFactory::~AuthSyncObserverFactory() {
@@ -26,9 +24,6 @@
 // static
 AuthSyncObserver* AuthSyncObserverFactory::GetForProfile(
     Profile* profile) {
-  if (!AuthSyncObserver::ShouldObserve(profile))
-    return nullptr;
-
   return static_cast<AuthSyncObserver*>(
       GetInstance()->GetServiceForBrowserContext(profile, true));
 }
diff --git a/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc b/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
index eadc7dd..9983614 100644
--- a/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
+++ b/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <map>
-#include <memory>
 #include <string>
 #include <utility>
 
@@ -25,7 +23,6 @@
 #include "chrome/browser/extensions/chrome_extension_test_notification_observer.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
-#include "chrome/browser/signin/signin_error_controller_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/javascript_dialogs/javascript_dialog_tab_helper.h"
@@ -40,9 +37,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/account_id/account_id.h"
 #include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/fake_auth_status_provider.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
-#include "components/signin/core/browser/signin_error_controller.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/notification_service.h"
@@ -115,8 +110,8 @@
 
     waiting_for_state_ = true;
     login_manager->AddObserver(this);
-    run_loop_ = std::make_unique<base::RunLoop>();
-    run_loop_->Run();
+    runner_ = new content::MessageLoopRunner;
+    runner_->Run();
     login_manager->RemoveObserver(this);
   }
 
@@ -135,14 +130,14 @@
 
     final_state_ = state;
     waiting_for_state_ = false;
-    run_loop_->Quit();
+    runner_->Quit();
   }
 
   Profile* profile_;
   std::set<OAuth2LoginManager::SessionRestoreState> states_;
   bool waiting_for_state_;
   OAuth2LoginManager::SessionRestoreState final_state_;
-  std::unique_ptr<base::RunLoop> run_loop_;
+  scoped_refptr<content::MessageLoopRunner> runner_;
 
   DISALLOW_COPY_AND_ASSIGN(OAuth2LoginManagerStateWaiter);
 };
@@ -171,56 +166,12 @@
   DISALLOW_COPY_AND_ASSIGN(ThreadBlocker);
 };
 
-// Helper class that is added as a RequestMonitor of embedded test server to
-// wait for a request to happen and defer it until Unblock is called.
-class RequestDeferrer {
- public:
-  RequestDeferrer()
-      : blocking_event_(base::WaitableEvent::ResetPolicy::MANUAL,
-                        base::WaitableEvent::InitialState::NOT_SIGNALED),
-        start_event_(base::WaitableEvent::ResetPolicy::MANUAL,
-                     base::WaitableEvent::InitialState::NOT_SIGNALED) {}
-
-  void UnblockRequest() { blocking_event_.Signal(); }
-
-  void WaitForRequestToStart() {
-    // If we have already served the request, bail out.
-    if (start_event_.IsSignaled())
-      return;
-
-    run_loop_ = std::make_unique<base::RunLoop>();
-    run_loop_->Run();
-  }
-
-  void InterceptRequest(const HttpRequest& request) {
-    start_event_.Signal();
-    content::BrowserThread::PostTask(
-        content::BrowserThread::UI, FROM_HERE,
-        base::BindOnce(&RequestDeferrer::QuitRunnerOnUIThread,
-                       base::Unretained(this)));
-    blocking_event_.Wait();
-  }
-
- private:
-  void QuitRunnerOnUIThread() {
-    if (run_loop_)
-      run_loop_->Quit();
-  }
-
-  base::WaitableEvent blocking_event_;
-  base::WaitableEvent start_event_;
-  std::unique_ptr<base::RunLoop> run_loop_;
-
-  DISALLOW_COPY_AND_ASSIGN(RequestDeferrer);
-};
-
 }  // namespace
 
 class OAuth2Test : public OobeBaseTest {
  protected:
   OAuth2Test() {}
 
-  // OobeBaseTest overrides.
   void SetUpCommandLine(base::CommandLine* command_line) override {
     OobeBaseTest::SetUpCommandLine(command_line);
 
@@ -229,11 +180,6 @@
     command_line->AppendSwitch(switches::kDisableSync);
   }
 
-  void RegisterAdditionalRequestHandlers() override {
-    embedded_test_server()->RegisterRequestMonitor(
-        base::Bind(&OAuth2Test::InterceptRequest, base::Unretained(this)));
-  }
-
   void SetupGaiaServerForNewAccount() {
     FakeGaia::MergeSessionParams params;
     params.auth_sid_cookie = kTestAuthSIDCookie;
@@ -450,25 +396,7 @@
     return login_manager->restore_strategy_;
   }
 
-  void InterceptRequest(const HttpRequest& request) {
-    const GURL request_url =
-        GURL("http://localhost").Resolve(request.relative_url);
-    auto it = request_deferers_.find(request_url.path());
-    if (it == request_deferers_.end())
-      return;
-
-    it->second->InterceptRequest(request);
-  }
-
-  void AddRequestDeferer(const std::string& path,
-                         RequestDeferrer* request_deferer) {
-    DCHECK(request_deferers_.find(path) == request_deferers_.end());
-    request_deferers_[path] = request_deferer;
-  }
-
  private:
-  std::map<std::string, RequestDeferrer*> request_deferers_;
-
   DISALLOW_COPY_AND_ASSIGN(OAuth2Test);
 };
 
@@ -482,8 +410,8 @@
     content::BrowserThread::PostTask(
         content::BrowserThread::IO, FROM_HERE,
         base::BindOnce(&CookieReader::ReadCookiesOnIOThread, this));
-    run_loop_ = std::make_unique<base::RunLoop>();
-    run_loop_->Run();
+    runner_ = new content::MessageLoopRunner;
+    runner_->Run();
   }
 
   std::string GetCookieValue(const std::string& name) {
@@ -516,11 +444,13 @@
         base::BindOnce(&CookieReader::OnCookiesReadyOnUIThread, this));
   }
 
-  void OnCookiesReadyOnUIThread() { run_loop_->Quit(); }
+  void OnCookiesReadyOnUIThread() {
+    runner_->Quit();
+  }
 
   scoped_refptr<net::URLRequestContextGetter> context_;
   net::CookieList cookie_list_;
-  std::unique_ptr<base::RunLoop> run_loop_;
+  scoped_refptr<content::MessageLoopRunner> runner_;
 
   DISALLOW_COPY_AND_ASSIGN(CookieReader);
 };
@@ -686,72 +616,14 @@
   WaitForMergeSessionCompletion(OAuth2LoginManager::SESSION_RESTORE_FAILED);
 }
 
-// Sets up a new user with stored refresh token.
-IN_PROC_BROWSER_TEST_F(OAuth2Test, PRE_SetInvalidTokenStatus) {
-  StartNewUserSession(true);
-}
-
-// Tests that an auth error reported by SigninErrorController marks invalid auth
-// token status despite OAuth2LoginManager thinks merge session is done
-// successfully
-IN_PROC_BROWSER_TEST_F(OAuth2Test, SetInvalidTokenStatus) {
-  RequestDeferrer list_accounts_request_deferer;
-  AddRequestDeferer("/ListAccounts", &list_accounts_request_deferer);
-
-  SetupGaiaServerForUnexpiredAccount();
-  SimulateNetworkOnline();
-
-  // Waits for login screen to be ready.
-  content::WindowedNotificationObserver(
-      chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
-      content::NotificationService::AllSources())
-      .Wait();
-
-  // Signs in as the existing user created in pre test.
-  ExistingUserController* const controller =
-      ExistingUserController::current_controller();
-  UserContext user_context(
-      AccountId::FromUserEmailGaiaId(kTestEmail, kTestGaiaId));
-  user_context.SetKey(Key(kTestAccountPassword));
-  controller->Login(user_context, SigninSpecifics());
-
-  // Wait until /ListAccounts request happens so that an auth error can be
-  // generated after user profile is available but before merge session
-  // finishes.
-  list_accounts_request_deferer.WaitForRequestToStart();
-
-  // Make sure that merge session is not finished.
-  OAuth2LoginManager* const login_manager =
-      OAuth2LoginManagerFactory::GetInstance()->GetForProfile(profile());
-  ASSERT_NE(OAuth2LoginManager::SESSION_RESTORE_DONE, login_manager->state());
-
-  // Generate an auth error.
-  SigninErrorController* const error_controller =
-      SigninErrorControllerFactory::GetForProfile(profile());
-  FakeAuthStatusProvider auth_provider(error_controller);
-  auth_provider.SetAuthError(
-      kTestEmail, GoogleServiceAuthError(
-                      GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS));
-
-  // Let go /ListAccounts request.
-  list_accounts_request_deferer.UnblockRequest();
-
-  // Wait for the session merge to finish with success.
-  WaitForMergeSessionCompletion(OAuth2LoginManager::SESSION_RESTORE_DONE);
-
-  // User oauth2 token status should be marked as invalid because of auth error
-  // and regardless of the merge session outcome.
-  EXPECT_EQ(GetOAuthStatusFromLocalState(kTestEmail),
-            user_manager::User::OAUTH2_TOKEN_STATUS_INVALID);
-}
-
-constexpr char kGooglePageContent[] =
+const char kGooglePageContent[] =
     "<html><title>Hello!</title><script>alert('hello');</script>"
     "<body>Hello Google!</body></html>";
-constexpr char kRandomPageContent[] =
+const char kRandomPageContent[] =
     "<html><title>SomthingElse</title><body>I am SomethingElse</body></html>";
-constexpr char kHelloPagePath[] = "/hello_google";
-constexpr char kRandomPagePath[] = "/non_google_page";
+const char kHelloPagePath[] = "/hello_google";
+const char kRandomPagePath[] = "/non_google_page";
+
 
 // FakeGoogle serves content of http://www.google.com/hello_google page for
 // merge session tests.
@@ -800,29 +672,78 @@
     if (start_event_.IsSignaled())
       return;
 
-    run_loop_ = std::make_unique<base::RunLoop>();
-    run_loop_->Run();
+    runner_ = new content::MessageLoopRunner;
+    runner_->Run();
   }
 
  private:
   void QuitRunnerOnUIThread() {
-    if (run_loop_)
-      run_loop_->Quit();
+    if (runner_.get())
+      runner_->Quit();
   }
   // This event will tell us when we actually see HTTP request on the server
   // side. It should be signalled only after the page/XHR throttle had been
   // removed (after merge session completes).
   base::WaitableEvent start_event_;
-  std::unique_ptr<base::RunLoop> run_loop_;
+  scoped_refptr<content::MessageLoopRunner> runner_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeGoogle);
 };
 
+// FakeGaia specialization that can delay /MergeSession handler until
+// we explicitly call DelayedFakeGaia::UnblockMergeSession().
+class DelayedFakeGaia : public FakeGaia {
+ public:
+  DelayedFakeGaia()
+      : blocking_event_(base::WaitableEvent::ResetPolicy::MANUAL,
+                        base::WaitableEvent::InitialState::NOT_SIGNALED),
+        start_event_(base::WaitableEvent::ResetPolicy::MANUAL,
+                     base::WaitableEvent::InitialState::NOT_SIGNALED) {}
+
+  void UnblockMergeSession() {
+    blocking_event_.Signal();
+  }
+
+  void WaitForMergeSessionToStart() {
+    // If we have already served the request, bail out.
+    if (start_event_.IsSignaled())
+      return;
+
+    runner_ = new content::MessageLoopRunner;
+    runner_->Run();
+  }
+
+ private:
+  // FakeGaia overrides.
+  void HandleMergeSession(const HttpRequest& request,
+                          BasicHttpResponse* http_response) override {
+    start_event_.Signal();
+    content::BrowserThread::PostTask(
+        content::BrowserThread::UI, FROM_HERE,
+        base::BindOnce(&DelayedFakeGaia::QuitRunnerOnUIThread,
+                       base::Unretained(this)));
+    blocking_event_.Wait();
+    FakeGaia::HandleMergeSession(request, http_response);
+  }
+
+  void QuitRunnerOnUIThread() {
+    if (runner_.get())
+      runner_->Quit();
+  }
+
+  base::WaitableEvent blocking_event_;
+  base::WaitableEvent start_event_;
+  scoped_refptr<content::MessageLoopRunner> runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(DelayedFakeGaia);
+};
+
 class MergeSessionTest : public OAuth2Test {
  protected:
-  MergeSessionTest() = default;
+  MergeSessionTest() : delayed_fake_gaia_(new DelayedFakeGaia()) {
+    fake_gaia_.reset(delayed_fake_gaia_);
+  }
 
-  // OAuth2Test overrides.
   void SetUpCommandLine(base::CommandLine* command_line) override {
     OAuth2Test::SetUpCommandLine(command_line);
 
@@ -839,19 +760,19 @@
     non_google_page_url_ = non_google_url.Resolve(kRandomPagePath);
   }
 
-  void RegisterAdditionalRequestHandlers() override {
-    OAuth2Test::RegisterAdditionalRequestHandlers();
-    AddRequestDeferer("/MergeSession", &merge_session_deferer_);
-
+  void SetUp() override {
     embedded_test_server()->RegisterRequestHandler(base::Bind(
         &FakeGoogle::HandleRequest, base::Unretained(&fake_google_)));
+    OAuth2Test::SetUp();
   }
 
  protected:
-  void UnblockMergeSession() { merge_session_deferer_.UnblockRequest(); }
+  void UnblockMergeSession() {
+    delayed_fake_gaia_->UnblockMergeSession();
+  }
 
   void WaitForMergeSessionToStart() {
-    merge_session_deferer_.WaitForRequestToStart();
+    delayed_fake_gaia_->WaitForMergeSessionToStart();
   }
 
   void JsExpect(content::WebContents* contents,
@@ -888,7 +809,7 @@
   }
 
   FakeGoogle fake_google_;
-  RequestDeferrer merge_session_deferer_;
+  DelayedFakeGaia* delayed_fake_gaia_;
   GURL fake_google_page_url_;
   GURL non_google_page_url_;
 
diff --git a/chrome/browser/chromeos/login/signin/oauth2_login_manager.cc b/chrome/browser/chromeos/login/signin/oauth2_login_manager.cc
index 196ede7a..5b039da 100644
--- a/chrome/browser/chromeos/login/signin/oauth2_login_manager.cc
+++ b/chrome/browser/chromeos/login/signin/oauth2_login_manager.cc
@@ -10,18 +10,15 @@
 #include "base/command_line.h"
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/account_tracker_service_factory.h"
 #include "chrome/browser/signin/chrome_signin_client_factory.h"
 #include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
 #include "chromeos/chromeos_switches.h"
 #include "components/signin/core/account_id/account_id.h"
-#include "components/signin/core/browser/account_tracker_service.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "components/signin/core/browser/signin_client.h"
 #include "components/signin/core/browser/signin_manager.h"
-#include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "google_apis/gaia/gaia_urls.h"
diff --git a/chrome/browser/chromeos/login/signin/oauth2_login_manager_factory.cc b/chrome/browser/chromeos/login/signin/oauth2_login_manager_factory.cc
index 976b939e..f8d441e 100644
--- a/chrome/browser/chromeos/login/signin/oauth2_login_manager_factory.cc
+++ b/chrome/browser/chromeos/login/signin/oauth2_login_manager_factory.cc
@@ -6,11 +6,9 @@
 
 #include "chrome/browser/chromeos/login/signin/oauth2_login_manager.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/account_tracker_service_factory.h"
 #include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
-#include "chrome/browser/ui/global_error/global_error_service_factory.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 
 namespace chromeos {
@@ -19,8 +17,6 @@
     : BrowserContextKeyedServiceFactory(
         "OAuth2LoginManager",
         BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(AccountTrackerServiceFactory::GetInstance());
-  DependsOn(GlobalErrorServiceFactory::GetInstance());
   DependsOn(SigninManagerFactory::GetInstance());
   DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
   DependsOn(GaiaCookieManagerServiceFactory::GetInstance());
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 35098f6..906a446 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
@@ -496,11 +496,9 @@
           ManagerPasswordServiceFactory::GetForProfile(profile);
 
         if (!profile->IsOffTheRecord()) {
-          if (AuthSyncObserver::ShouldObserve(profile)) {
-            AuthSyncObserver* sync_observer =
-                AuthSyncObserverFactory::GetInstance()->GetForProfile(profile);
-            sync_observer->StartObserving();
-          }
+          AuthSyncObserver* sync_observer =
+              AuthSyncObserverFactory::GetInstance()->GetForProfile(profile);
+          sync_observer->StartObserving();
           multi_profile_user_controller_->StartObserving(profile);
         }
       }
diff --git a/chrome/browser/chromeos/system/timezone_resolver_manager.cc b/chrome/browser/chromeos/system/timezone_resolver_manager.cc
index fca87dd..73fb7366 100644
--- a/chrome/browser/chromeos/system/timezone_resolver_manager.cc
+++ b/chrome/browser/chromeos/system/timezone_resolver_manager.cc
@@ -122,6 +122,9 @@
 }  // anonymous namespace.
 
 TimeZoneResolverManager::TimeZoneResolverManager() : weak_factory_(this) {
+  local_state_initialized_ =
+      g_browser_process->local_state()->GetInitializationStatus() ==
+      PrefService::INITIALIZATION_STATUS_SUCCESS;
   g_browser_process->local_state()->AddPrefInitObserver(
       base::Bind(&TimeZoneResolverManager::OnLocalStateInitialized,
                  weak_factory_.GetWeakPtr()));
diff --git a/chrome/browser/devtools/BUILD.gn b/chrome/browser/devtools/BUILD.gn
index 9b9a022..e5f6ebd 100644
--- a/chrome/browser/devtools/BUILD.gn
+++ b/chrome/browser/devtools/BUILD.gn
@@ -29,6 +29,41 @@
   args += [ rebase_path(blink_protocol, root_build_dir) ]
 }
 
+if (!is_android) {
+  _inspector_protocol = "//third_party/inspector_protocol"
+  import("$_inspector_protocol/inspector_protocol.gni")
+
+  _protocol_generated = [
+    "protocol/forward.h",
+    "protocol/page.cc",
+    "protocol/page.h",
+    "protocol/protocol.cc",
+    "protocol/protocol.h",
+  ]
+
+  inspector_protocol_generate("protocol_generated_sources") {
+    inspector_protocol_dir = _inspector_protocol
+    visibility = [ ":*" ]  # Only targets in this file can depend on this.
+    deps = [
+      "//third_party/WebKit/Source/core/inspector:protocol_version",
+    ]
+    out_dir = target_gen_dir
+    config_file = "inspector_protocol_config.json"
+
+    _blink_protocol_path =
+        rebase_path("$root_gen_dir/blink/core/inspector/protocol.json",
+                    root_build_dir)
+    config_values = [ "protocol.path=$_blink_protocol_path" ]
+
+    inputs = [
+      "$root_gen_dir/blink/core/inspector/protocol.json",
+      "inspector_protocol_config.json",
+    ]
+
+    outputs = _protocol_generated
+  }
+}
+
 static_library("devtools") {
   # Note: new sources and deps should be generally added in (!is_android) below.
   sources = [
@@ -67,6 +102,8 @@
     sources += [
       "chrome_devtools_manager_delegate.cc",
       "chrome_devtools_manager_delegate.h",
+      "chrome_devtools_session.cc",
+      "chrome_devtools_session.h",
       "device/adb/adb_client_socket.cc",
       "device/adb/adb_client_socket.h",
       "device/adb/adb_device_provider.cc",
@@ -130,6 +167,17 @@
     }
   }
 
+  if (!is_android) {
+    deps += [ ":protocol_generated_sources" ]
+    sources += [
+      "protocol/page_handler.cc",
+      "protocol/page_handler.h",
+      "protocol_string.cc",
+      "protocol_string.h",
+    ]
+    sources += rebase_path(_protocol_generated, ".", target_gen_dir)
+  }
+
   if (enable_extensions) {
     deps += [ "//chrome/common/extensions/api" ]
   }
diff --git a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
index fb9618a..cf962f9 100644
--- a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
+++ b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
@@ -10,6 +10,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
+#include "chrome/browser/devtools/chrome_devtools_session.h"
 #include "chrome/browser/devtools/device/android_device_manager.h"
 #include "chrome/browser/devtools/device/tcp_device_provider.h"
 #include "chrome/browser/devtools/devtools_protocol.h"
@@ -18,7 +19,6 @@
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/subresource_filter/chrome_subresource_filter_client.h"
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/browser_window.h"
@@ -110,15 +110,6 @@
   return false;
 }
 
-void ToggleAdBlocking(bool enabled, content::DevToolsAgentHost* agent_host) {
-  if (content::WebContents* web_contents = agent_host->GetWebContents()) {
-    if (auto* client =
-            ChromeSubresourceFilterClient::FromWebContents(web_contents)) {
-      client->ToggleForceActivationInCurrentWebContents(enabled);
-    }
-  }
-}
-
 std::string ToString(std::unique_ptr<base::DictionaryValue> value) {
   std::string json;
   base::JSONWriter::Write(*value, &json);
@@ -292,27 +283,6 @@
 }
 
 std::unique_ptr<base::DictionaryValue>
-ChromeDevToolsManagerDelegate::SetAdBlockingEnabled(
-    content::DevToolsAgentHost* agent_host,
-    int id,
-    base::DictionaryValue* params) {
-  if (!page_enable_)
-    return DevToolsProtocol::CreateErrorResponse(id, "Page domain is disabled");
-  bool enabled = false;
-  params->GetBoolean("enabled", &enabled);
-  ToggleAdBlocking(enabled, agent_host);
-  return DevToolsProtocol::CreateSuccessResponse(id, nullptr);
-}
-
-void ChromeDevToolsManagerDelegate::TogglePageEnable(
-    bool enable,
-    content::DevToolsAgentHost* agent_host) {
-  page_enable_ = enable;
-  if (!page_enable_)
-    ToggleAdBlocking(false /* enable */, agent_host);
-}
-
-std::unique_ptr<base::DictionaryValue>
 ChromeDevToolsManagerDelegate::HandleBrowserCommand(
     int id,
     std::string method,
@@ -364,18 +334,6 @@
   if (!DevToolsProtocol::ParseCommand(command_dict, &id, &method, &params))
     return false;
 
-  // Do not actually handle the enable/disable commands, just keep track of the
-  // enable state.
-  if (method == chrome::devtools::Page::enable::kName) {
-    TogglePageEnable(true /* enable */, agent_host);
-    return false;
-  }
-
-  if (method == chrome::devtools::Page::disable::kName) {
-    TogglePageEnable(false /* enable */, agent_host);
-    return false;
-  }
-
   auto result = HandleBrowserCommand(id, method, params);
   if (result) {
     agent_host->SendProtocolMessageToClient(session_id,
@@ -383,14 +341,6 @@
     return true;
   }
 
-  if (method == chrome::devtools::Page::setAdBlockingEnabled::kName) {
-    result = SetAdBlockingEnabled(agent_host, id, params);
-    DCHECK(result);
-    agent_host->SendProtocolMessageToClient(session_id,
-                                            ToString(std::move(result)));
-    return true;
-  }
-
   if (method == chrome::devtools::Target::setRemoteLocations::kName) {
     result = SetRemoteLocations(agent_host, id, params);
     DCHECK(result);
@@ -399,7 +349,10 @@
     return true;
   }
 
-  return false;
+  DCHECK(sessions_.find(session_id) != sessions_.end());
+  auto response = sessions_[session_id]->dispatcher()->dispatch(
+      protocol::toProtocolValue(command_dict, 1000));
+  return response != protocol::DispatchResponse::Status::kFallThrough;
 }
 
 std::string ChromeDevToolsManagerDelegate::GetTargetType(
@@ -425,6 +378,20 @@
   return extension_name;
 }
 
+void ChromeDevToolsManagerDelegate::SessionCreated(
+    content::DevToolsAgentHost* agent_host,
+    int session_id) {
+  DCHECK(sessions_.find(session_id) == sessions_.end());
+  sessions_[session_id] =
+      std::make_unique<ChromeDevToolsSession>(agent_host, session_id);
+}
+
+void ChromeDevToolsManagerDelegate::SessionDestroyed(
+    content::DevToolsAgentHost* agent_host,
+    int session_id) {
+  sessions_.erase(session_id);
+}
+
 scoped_refptr<DevToolsAgentHost>
 ChromeDevToolsManagerDelegate::CreateNewTarget(const GURL& url) {
   chrome::NavigateParams params(ProfileManager::GetLastUsedProfile(),
@@ -454,8 +421,6 @@
 
 void ChromeDevToolsManagerDelegate::DevToolsAgentHostDetached(
     content::DevToolsAgentHost* agent_host) {
-  ToggleAdBlocking(false /* enable */, agent_host);
-
   // This class is created lazily, so it may not know about some attached hosts.
   if (host_data_.find(agent_host) != host_data_.end()) {
     host_data_.erase(agent_host);
diff --git a/chrome/browser/devtools/chrome_devtools_manager_delegate.h b/chrome/browser/devtools/chrome_devtools_manager_delegate.h
index 3c1b1d1..c0e2026 100644
--- a/chrome/browser/devtools/chrome_devtools_manager_delegate.h
+++ b/chrome/browser/devtools/chrome_devtools_manager_delegate.h
@@ -13,10 +13,14 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "chrome/browser/devtools/device/devtools_device_discovery.h"
+#include "chrome/browser/devtools/protocol/forward.h"
+#include "chrome/browser/devtools/protocol/protocol.h"
 #include "content/public/browser/devtools_agent_host_observer.h"
 #include "content/public/browser/devtools_manager_delegate.h"
 #include "net/base/host_port_pair.h"
 
+class ChromeDevToolsSession;
+
 class ChromeDevToolsManagerDelegate :
     public content::DevToolsManagerDelegate,
     public content::DevToolsAgentHostObserver {
@@ -39,6 +43,10 @@
                      base::DictionaryValue* command_dict) override;
   std::string GetTargetType(content::WebContents* web_contents) override;
   std::string GetTargetTitle(content::WebContents* web_contents) override;
+  void SessionCreated(content::DevToolsAgentHost* agent_host,
+                      int session_id) override;
+  void SessionDestroyed(content::DevToolsAgentHost* agent_host,
+                        int session_id) override;
   scoped_refptr<content::DevToolsAgentHost> CreateNewTarget(
       const GURL& url) override;
   std::string GetDiscoveryPageHTML() override;
@@ -72,22 +80,16 @@
   static std::unique_ptr<base::DictionaryValue> SetWindowBounds(
       int id,
       base::DictionaryValue* params);
-  std::unique_ptr<base::DictionaryValue> SetAdBlockingEnabled(
-      content::DevToolsAgentHost* agent_host,
-      int id,
-      base::DictionaryValue* params);
-
-  void TogglePageEnable(bool enable, content::DevToolsAgentHost* agent_host);
 
   std::map<content::DevToolsAgentHost*, std::unique_ptr<HostData>> host_data_;
 
+  std::map<int, std::unique_ptr<ChromeDevToolsSession>> sessions_;
+
   std::unique_ptr<AndroidDeviceManager> device_manager_;
   std::unique_ptr<DevToolsDeviceDiscovery> device_discovery_;
   content::DevToolsAgentHost::List remote_agent_hosts_;
   RemoteLocations remote_locations_;
 
-  bool page_enable_ = false;
-
   DISALLOW_COPY_AND_ASSIGN(ChromeDevToolsManagerDelegate);
 };
 
diff --git a/chrome/browser/devtools/chrome_devtools_session.cc b/chrome/browser/devtools/chrome_devtools_session.cc
new file mode 100644
index 0000000..45058687
--- /dev/null
+++ b/chrome/browser/devtools/chrome_devtools_session.cc
@@ -0,0 +1,36 @@
+// Copyright 2017 The Chromium 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/devtools/chrome_devtools_session.h"
+
+#include "chrome/browser/devtools/protocol/page_handler.h"
+#include "content/public/browser/devtools_agent_host.h"
+
+ChromeDevToolsSession::ChromeDevToolsSession(
+    content::DevToolsAgentHost* agent_host,
+    int session_id)
+    : agent_host_(agent_host),
+      session_id_(session_id),
+      dispatcher_(std::make_unique<protocol::UberDispatcher>(this)) {
+  dispatcher_->setFallThroughForNotFound(true);
+  if (agent_host->GetWebContents()) {
+    page_handler_ = std::make_unique<PageHandler>(agent_host->GetWebContents(),
+                                                  dispatcher_.get());
+  }
+}
+
+ChromeDevToolsSession::~ChromeDevToolsSession() = default;
+
+void ChromeDevToolsSession::sendProtocolResponse(
+    int call_id,
+    std::unique_ptr<protocol::Serializable> message) {
+  agent_host_->SendProtocolMessageToClient(session_id_, message->serialize());
+}
+
+void ChromeDevToolsSession::sendProtocolNotification(
+    std::unique_ptr<protocol::Serializable> message) {
+  agent_host_->SendProtocolMessageToClient(session_id_, message->serialize());
+}
+
+void ChromeDevToolsSession::flushProtocolNotifications() {}
diff --git a/chrome/browser/devtools/chrome_devtools_session.h b/chrome/browser/devtools/chrome_devtools_session.h
new file mode 100644
index 0000000..63f2aeb
--- /dev/null
+++ b/chrome/browser/devtools/chrome_devtools_session.h
@@ -0,0 +1,44 @@
+// Copyright 2017 The Chromium 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_DEVTOOLS_CHROME_DEVTOOLS_SESSION_H_
+#define CHROME_BROWSER_DEVTOOLS_CHROME_DEVTOOLS_SESSION_H_
+
+#include <memory>
+
+#include "chrome/browser/devtools/protocol/forward.h"
+#include "chrome/browser/devtools/protocol/protocol.h"
+
+namespace content {
+class DevToolsAgentHost;
+}
+
+class PageHandler;
+
+class ChromeDevToolsSession : public protocol::FrontendChannel {
+ public:
+  ChromeDevToolsSession(content::DevToolsAgentHost* agent_host, int session_id);
+  ~ChromeDevToolsSession() override;
+
+  protocol::UberDispatcher* dispatcher() { return dispatcher_.get(); }
+
+ private:
+  // protocol::FrontendChannel:
+  void sendProtocolResponse(
+      int call_id,
+      std::unique_ptr<protocol::Serializable> message) override;
+  void sendProtocolNotification(
+      std::unique_ptr<protocol::Serializable> message) override;
+  void flushProtocolNotifications() override;
+
+  content::DevToolsAgentHost* const agent_host_;
+  const int session_id_;
+
+  std::unique_ptr<protocol::UberDispatcher> dispatcher_;
+  std::unique_ptr<PageHandler> page_handler_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChromeDevToolsSession);
+};
+
+#endif  // CHROME_BROWSER_DEVTOOLS_CHROME_DEVTOOLS_SESSION_H_
diff --git a/chrome/browser/devtools/inspector_protocol_config.json b/chrome/browser/devtools/inspector_protocol_config.json
new file mode 100644
index 0000000..58272d8
--- /dev/null
+++ b/chrome/browser/devtools/inspector_protocol_config.json
@@ -0,0 +1,22 @@
+{
+    "use_snake_file_names": true,
+    "use_title_case_methods": true,
+    "protocol": {
+        "package": "chrome/browser/devtools/protocol",
+        "output": "protocol",
+        "namespace": [ "protocol" ],
+        "options": [
+            {
+                "domain": "Page",
+                "include": [ "enable", "disable", "setAdBlockingEnabled" ],
+                "include_types": [],
+                "include_events": []
+            }
+        ]
+    },
+    "lib": {
+        "package": "chrome/browser/devtools/protocol",
+        "output": "protocol",
+        "string_header": "chrome/browser/devtools/protocol_string.h"
+    }
+}
diff --git a/chrome/browser/devtools/protocol/page_handler.cc b/chrome/browser/devtools/protocol/page_handler.cc
new file mode 100644
index 0000000..1020609c
--- /dev/null
+++ b/chrome/browser/devtools/protocol/page_handler.cc
@@ -0,0 +1,49 @@
+// Copyright 2017 The Chromium 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/devtools/protocol/page_handler.h"
+
+#include "chrome/browser/subresource_filter/chrome_subresource_filter_client.h"
+
+PageHandler::PageHandler(content::WebContents* web_contents,
+                         protocol::UberDispatcher* dispatcher)
+    : content::WebContentsObserver(web_contents) {
+  DCHECK(web_contents);
+  protocol::Page::Dispatcher::wire(dispatcher, this);
+}
+
+PageHandler::~PageHandler() {
+  ToggleAdBlocking(false /* enabled */);
+}
+
+void PageHandler::ToggleAdBlocking(bool enabled) {
+  if (!web_contents())
+    return;
+  if (auto* client =
+          ChromeSubresourceFilterClient::FromWebContents(web_contents())) {
+    client->ToggleForceActivationInCurrentWebContents(enabled);
+  }
+}
+
+protocol::Response PageHandler::Enable() {
+  enabled_ = true;
+  // Do not mark the command as handled. Let it fall through instead, so that
+  // the handler in content gets a chance to process the command.
+  return protocol::Response::FallThrough();
+}
+
+protocol::Response PageHandler::Disable() {
+  enabled_ = false;
+  ToggleAdBlocking(false /* enable */);
+  // Do not mark the command as handled. Let it fall through instead, so that
+  // the handler in content gets a chance to process the command.
+  return protocol::Response::FallThrough();
+}
+
+protocol::Response PageHandler::SetAdBlockingEnabled(bool enabled) {
+  if (!enabled_)
+    return protocol::Response::Error("Page domain is disabled.");
+  ToggleAdBlocking(enabled);
+  return protocol::Response::OK();
+}
diff --git a/chrome/browser/devtools/protocol/page_handler.h b/chrome/browser/devtools/protocol/page_handler.h
new file mode 100644
index 0000000..bc0d62cc
--- /dev/null
+++ b/chrome/browser/devtools/protocol/page_handler.h
@@ -0,0 +1,36 @@
+// Copyright 2017 The Chromium 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_DEVTOOLS_PROTOCOL_PAGE_HANDLER_H_
+#define CHROME_BROWSER_DEVTOOLS_PROTOCOL_PAGE_HANDLER_H_
+
+#include "chrome/browser/devtools/protocol/forward.h"
+#include "chrome/browser/devtools/protocol/page.h"
+#include "content/public/browser/web_contents_observer.h"
+
+namespace content {
+class WebContents;
+}
+
+class PageHandler : public protocol::Page::Backend,
+                    public content::WebContentsObserver {
+ public:
+  PageHandler(content::WebContents* web_contents,
+              protocol::UberDispatcher* dispatcher);
+  ~PageHandler() override;
+
+  void ToggleAdBlocking(bool enabled);
+
+  // Page::Backend:
+  protocol::Response Enable() override;
+  protocol::Response Disable() override;
+  protocol::Response SetAdBlockingEnabled(bool enabled) override;
+
+ private:
+  bool enabled_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(PageHandler);
+};
+
+#endif  // CHROME_BROWSER_DEVTOOLS_PROTOCOL_PAGE_HANDLER_H_
diff --git a/chrome/browser/devtools/protocol_string.cc b/chrome/browser/devtools/protocol_string.cc
new file mode 100644
index 0000000..833628cf
--- /dev/null
+++ b/chrome/browser/devtools/protocol_string.cc
@@ -0,0 +1,165 @@
+// 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/devtools/protocol_string.h"
+
+#include "base/json/json_reader.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/devtools/protocol/protocol.h"
+
+namespace protocol {
+
+std::unique_ptr<protocol::Value> toProtocolValue(const base::Value* value,
+                                                 int depth) {
+  if (!value || !depth)
+    return nullptr;
+  if (value->IsType(base::Value::Type::NONE))
+    return protocol::Value::null();
+  if (value->IsType(base::Value::Type::BOOLEAN)) {
+    bool inner;
+    value->GetAsBoolean(&inner);
+    return protocol::FundamentalValue::create(inner);
+  }
+  if (value->IsType(base::Value::Type::INTEGER)) {
+    int inner;
+    value->GetAsInteger(&inner);
+    return protocol::FundamentalValue::create(inner);
+  }
+  if (value->IsType(base::Value::Type::DOUBLE)) {
+    double inner;
+    value->GetAsDouble(&inner);
+    return protocol::FundamentalValue::create(inner);
+  }
+  if (value->IsType(base::Value::Type::STRING)) {
+    std::string inner;
+    value->GetAsString(&inner);
+    return protocol::StringValue::create(inner);
+  }
+  if (value->IsType(base::Value::Type::LIST)) {
+    const base::ListValue* list = nullptr;
+    value->GetAsList(&list);
+    std::unique_ptr<protocol::ListValue> result = protocol::ListValue::create();
+    for (size_t i = 0; i < list->GetSize(); i++) {
+      const base::Value* item = nullptr;
+      list->Get(i, &item);
+      std::unique_ptr<protocol::Value> converted =
+          toProtocolValue(item, depth - 1);
+      if (converted)
+        result->pushValue(std::move(converted));
+    }
+    return std::move(result);
+  }
+  if (value->IsType(base::Value::Type::DICTIONARY)) {
+    const base::DictionaryValue* dictionary = nullptr;
+    value->GetAsDictionary(&dictionary);
+    std::unique_ptr<protocol::DictionaryValue> result =
+        protocol::DictionaryValue::create();
+    for (base::DictionaryValue::Iterator it(*dictionary); !it.IsAtEnd();
+         it.Advance()) {
+      std::unique_ptr<protocol::Value> converted =
+          toProtocolValue(&it.value(), depth - 1);
+      if (converted)
+        result->setValue(it.key(), std::move(converted));
+    }
+    return std::move(result);
+  }
+  return nullptr;
+}
+
+std::unique_ptr<base::Value> toBaseValue(protocol::Value* value, int depth) {
+  if (!value || !depth)
+    return nullptr;
+  if (value->type() == protocol::Value::TypeNull)
+    return base::MakeUnique<base::Value>();
+  if (value->type() == protocol::Value::TypeBoolean) {
+    bool inner;
+    value->asBoolean(&inner);
+    return base::WrapUnique(new base::Value(inner));
+  }
+  if (value->type() == protocol::Value::TypeInteger) {
+    int inner;
+    value->asInteger(&inner);
+    return base::WrapUnique(new base::Value(inner));
+  }
+  if (value->type() == protocol::Value::TypeDouble) {
+    double inner;
+    value->asDouble(&inner);
+    return base::WrapUnique(new base::Value(inner));
+  }
+  if (value->type() == protocol::Value::TypeString) {
+    std::string inner;
+    value->asString(&inner);
+    return base::WrapUnique(new base::Value(inner));
+  }
+  if (value->type() == protocol::Value::TypeArray) {
+    protocol::ListValue* list = protocol::ListValue::cast(value);
+    std::unique_ptr<base::ListValue> result(new base::ListValue());
+    for (size_t i = 0; i < list->size(); i++) {
+      std::unique_ptr<base::Value> converted =
+          toBaseValue(list->at(i), depth - 1);
+      if (converted)
+        result->Append(std::move(converted));
+    }
+    return std::move(result);
+  }
+  if (value->type() == protocol::Value::TypeObject) {
+    protocol::DictionaryValue* dict = protocol::DictionaryValue::cast(value);
+    std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
+    for (size_t i = 0; i < dict->size(); i++) {
+      protocol::DictionaryValue::Entry entry = dict->at(i);
+      std::unique_ptr<base::Value> converted =
+          toBaseValue(entry.second, depth - 1);
+      if (converted)
+        result->SetWithoutPathExpansion(entry.first, std::move(converted));
+    }
+    return std::move(result);
+  }
+  return nullptr;
+}
+
+// static
+std::unique_ptr<protocol::Value> StringUtil::parseJSON(
+    const std::string& json) {
+  std::unique_ptr<base::Value> value = base::JSONReader::Read(json);
+  return toProtocolValue(value.get(), 1000);
+}
+
+StringBuilder::StringBuilder() {}
+
+StringBuilder::~StringBuilder() {}
+
+void StringBuilder::append(const std::string& s) {
+  string_ += s;
+}
+
+void StringBuilder::append(char c) {
+  string_ += c;
+}
+
+void StringBuilder::append(const char* characters, size_t length) {
+  string_.append(characters, length);
+}
+
+// static
+void StringUtil::builderAppendQuotedString(StringBuilder& builder,
+                                           const String& str) {
+  builder.append('"');
+  base::string16 str16 = base::UTF8ToUTF16(str);
+  escapeWideStringForJSON(reinterpret_cast<const uint16_t*>(&str16[0]),
+                          str16.length(), &builder);
+  builder.append('"');
+}
+
+std::string StringBuilder::toString() {
+  return string_;
+}
+
+void StringBuilder::reserveCapacity(size_t capacity) {
+  string_.reserve(capacity);
+}
+
+}  // namespace protocol
diff --git a/chrome/browser/devtools/protocol_string.h b/chrome/browser/devtools/protocol_string.h
new file mode 100644
index 0000000..cc9db0d
--- /dev/null
+++ b/chrome/browser/devtools/protocol_string.h
@@ -0,0 +1,89 @@
+// Copyright 2017 The Chromium 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_DEVTOOLS_PROTOCOL_STRING_H_
+#define CHROME_BROWSER_DEVTOOLS_PROTOCOL_STRING_H_
+
+#include <memory>
+#include <string>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/string_number_conversions.h"
+
+namespace base {
+class Value;
+}
+
+namespace protocol {
+
+class Value;
+
+using String = std::string;
+
+class StringBuilder {
+ public:
+  StringBuilder();
+  ~StringBuilder();
+  void append(const String&);
+  void append(char);
+  void append(const char*, size_t);
+  String toString();
+  void reserveCapacity(size_t);
+
+ private:
+  std::string string_;
+};
+
+class StringUtil {
+ public:
+  static String substring(const String& s, unsigned pos, unsigned len) {
+    return s.substr(pos, len);
+  }
+  static String fromInteger(int number) { return base::IntToString(number); }
+  static String fromDouble(double number) {
+    String s = base::DoubleToString(number);
+    if (!s.empty() && s[0] == '.')
+      s = "0" + s;
+    return s;
+  }
+  static double toDouble(const char* s, size_t len, bool* ok) {
+    double v = 0.0;
+    *ok = base::StringToDouble(std::string(s, len), &v);
+    return *ok ? v : 0.0;
+  }
+  static size_t find(const String& s, const char* needle) {
+    return s.find(needle);
+  }
+  static size_t find(const String& s, const String& needle) {
+    return s.find(needle);
+  }
+  static const size_t kNotFound = static_cast<size_t>(-1);
+  static void builderAppend(StringBuilder& builder, const String& s) {
+    builder.append(s);
+  }
+  static void builderAppend(StringBuilder& builder, char c) {
+    builder.append(c);
+  }
+  static void builderAppend(StringBuilder& builder, const char* s, size_t len) {
+    builder.append(s, len);
+  }
+  static void builderAppendQuotedString(StringBuilder& builder,
+                                        const String& str);
+  static void builderReserve(StringBuilder& builder, unsigned capacity) {
+    builder.reserveCapacity(capacity);
+  }
+  static String builderToString(StringBuilder& builder) {
+    return builder.toString();
+  }
+  static std::unique_ptr<protocol::Value> parseJSON(const String&);
+};
+
+std::unique_ptr<protocol::Value> toProtocolValue(const base::Value* value,
+                                                 int depth);
+std::unique_ptr<base::Value> toBaseValue(protocol::Value* value, int depth);
+
+}  // namespace protocol
+
+#endif  // CHROME_BROWSER_DEVTOOLS_PROTOCOL_STRING_H_
diff --git a/chrome/browser/media/android/router/OWNERS b/chrome/browser/media/android/router/OWNERS
index afc407e..98e51df 100644
--- a/chrome/browser/media/android/router/OWNERS
+++ b/chrome/browser/media/android/router/OWNERS
@@ -1,6 +1,8 @@
 avayvod@chromium.org
+imcheng@chromium.org
 mfoltz@chromium.org
 mlamouri@chromium.org
+zqzhang@chromium.org
 
 # TEAM: media-dev@chromium.org
 # COMPONENT: Blink>PresentationAPI
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index 3927401..7cd3e6c 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -1311,6 +1311,20 @@
   SendCopyCommandAndCheckCopyPasteClipboard("L");
 }
 
+// Verifies that an <embed> of size zero will still instantiate a guest and post
+// message to the <embed> is correctly forwarded to the extension. This is for
+// catching future regression in docs/ and slides/ pages (see
+// https://crbug.com/763812).
+IN_PROC_BROWSER_TEST_F(PDFExtensionTest, PostMessageForZeroSizedEmbed) {
+  content::DOMMessageQueue queue;
+  GURL url(embedded_test_server()->GetURL(
+      "/pdf/post_message_zero_sized_embed.html"));
+  ui_test_utils::NavigateToURL(browser(), url);
+  std::string message;
+  EXPECT_TRUE(queue.WaitForMessage(&message));
+  EXPECT_EQ("\"POST_MESSAGE_OK\"", message);
+}
+
 #if defined(OS_MACOSX)
 // Test that "smart zoom" (double-tap with two fingers on Mac trackpad)
 // is disabled for the PDF viewer. This prevents the viewer's controls from
diff --git a/chrome/browser/resources/download_internals/compiled_resources2.gyp b/chrome/browser/resources/download_internals/compiled_resources2.gyp
new file mode 100644
index 0000000..cd273cde
--- /dev/null
+++ b/chrome/browser/resources/download_internals/compiled_resources2.gyp
@@ -0,0 +1,25 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+  'targets': [
+    {
+      'target_name': 'download_internals',
+      'dependencies': [
+        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
+        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
+        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:util',
+        'download_internals_browser_proxy',
+      ],
+      'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
+    },
+    {
+      'target_name': 'download_internals_browser_proxy',
+      'dependencies': [
+        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+      ],
+      'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
+    },
+  ],
+}
diff --git a/chrome/browser/resources/download_internals/download_internals.css b/chrome/browser/resources/download_internals/download_internals.css
new file mode 100644
index 0000000..9e05534
--- /dev/null
+++ b/chrome/browser/resources/download_internals/download_internals.css
@@ -0,0 +1,13 @@
+/* Copyright 2017 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+h1 {
+  color: rgb(74, 142, 230);
+  margin: 0;
+  padding: 0;
+}
+
+.status {
+  font-size: 15px;
+}
diff --git a/chrome/browser/resources/download_internals/download_internals.html b/chrome/browser/resources/download_internals/download_internals.html
new file mode 100644
index 0000000..407157c6
--- /dev/null
+++ b/chrome/browser/resources/download_internals/download_internals.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<html lang="en" dir="ltr">
+  <head>
+    <meta charset="utf-8">
+    <title>Download Internals</title>
+    <meta name="viewport" content="width=device-width">
+    <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
+    <link rel="stylesheet" href="download_internals.css">
+
+    <link rel="import" href="chrome://resources/html/cr.html">
+    <script src="chrome://resources/js/load_time_data.js"></script>
+    <script src="chrome://resources/js/util.js"></script>
+    <script src="strings.js"></script>
+    <script src="download_internals_browser_proxy.js"></script>
+    <script src="download_internals.js"></script>
+  </head>
+  <body>
+    <h1>Download Internals</h1>
+    <div>
+      State: <span id="service-state" class="status"></span>
+      Model: <span id="service-status-model" class="status"></span>
+      Driver: <span id="service-status-driver" class="status"></span>
+      File Monitor: <span id="service-status-file" class="status"></span>
+    </div>
+  </body>
+</html>
diff --git a/chrome/browser/resources/download_internals/download_internals.js b/chrome/browser/resources/download_internals/download_internals.js
new file mode 100644
index 0000000..4dd8d885
--- /dev/null
+++ b/chrome/browser/resources/download_internals/download_internals.js
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('downloadInternals', function() {
+  'use strict';
+
+  /** @type {!downloadInternals.DownloadInternalsBrowserProxy} */
+  var browserProxy =
+      downloadInternals.DownloadInternalsBrowserProxyImpl.getInstance();
+
+  function onServiceStatusChanged(state) {
+    $('service-state').textContent = state.serviceState;
+    $('service-status-model').textContent = state.modelStatus;
+    $('service-status-driver').textContent = state.driverStatus;
+    $('service-status-file').textContent = state.fileMonitorStatus;
+  }
+
+  function initialize() {
+    // Register all event listeners.
+    cr.addWebUIListener('service-status-changed', onServiceStatusChanged);
+
+    // Kick off requests for the current system state.
+    browserProxy.getServiceStatus().then(onServiceStatusChanged);
+  }
+
+  return {
+    initialize: initialize,
+  };
+});
+
+document.addEventListener('DOMContentLoaded', downloadInternals.initialize);
\ No newline at end of file
diff --git a/chrome/browser/resources/download_internals/download_internals_browser_proxy.js b/chrome/browser/resources/download_internals/download_internals_browser_proxy.js
new file mode 100644
index 0000000..a2b1fe2
--- /dev/null
+++ b/chrome/browser/resources/download_internals/download_internals_browser_proxy.js
@@ -0,0 +1,42 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @typedef {{
+ *   serviceState: string,
+ *   modelStatus: string,
+ *   driverStatus: string,
+ *   fileMonitorStatus: string
+ * }}
+ */
+var ServiceStatus;
+
+cr.define('downloadInternals', function() {
+  /** @interface */
+  class DownloadInternalsBrowserProxy {
+    /**
+     * Gets the current status of the Download Service.
+     * @return {!Promise<ServiceStatus>} A promise firing when the service
+     *     status is fetched.
+     */
+    getServiceStatus() {}
+  }
+
+  /**
+   * @implements {downloadInternals.DownloadInternalsBrowserProxy}
+   */
+  class DownloadInternalsBrowserProxyImpl {
+    /** @override */
+    getServiceStatus() {
+      return cr.sendWithPromise('getServiceStatus');
+    }
+  }
+
+  cr.addSingletonGetter(DownloadInternalsBrowserProxyImpl);
+
+  return {
+    DownloadInternalsBrowserProxy: DownloadInternalsBrowserProxy,
+    DownloadInternalsBrowserProxyImpl: DownloadInternalsBrowserProxyImpl
+  };
+});
\ No newline at end of file
diff --git a/chrome/browser/ssl/captive_portal_blocking_page.cc b/chrome/browser/ssl/captive_portal_blocking_page.cc
index 17961f5..f74ff8d 100644
--- a/chrome/browser/ssl/captive_portal_blocking_page.cc
+++ b/chrome/browser/ssl/captive_portal_blocking_page.cc
@@ -37,6 +37,14 @@
 #include "net/ssl/ssl_info.h"
 #include "ui/base/l10n/l10n_util.h"
 
+#if defined(OS_ANDROID)
+#include "base/android/jni_android.h"
+#include "chrome/browser/ssl/captive_portal_helper_android.h"
+#include "content/public/common/referrer.h"
+#include "net/android/network_library.h"
+#include "ui/base/window_open_disposition.h"
+#endif
+
 namespace {
 
 const char kMetricsName[] = "captive_portal";
@@ -75,8 +83,6 @@
       login_url_(login_url),
       ssl_info_(ssl_info),
       callback_(callback) {
-  DCHECK(login_url_.is_valid());
-
   if (ssl_cert_reporter) {
     cert_report_helper_.reset(new CertReportHelper(
         std::move(ssl_cert_reporter), web_contents, request_url, ssl_info,
@@ -118,6 +124,8 @@
     return std::string();
 #elif defined(OS_LINUX)
   ssid = net::GetWifiSSID();
+#elif defined(OS_ANDROID)
+  ssid = net::android::GetWifiSSID();
 #endif
   // TODO(meacer): Handle non UTF8 SSIDs.
   if (!base::IsStringUTF8(ssid))
@@ -155,10 +163,15 @@
   load_time_data->SetString("heading", tab_title);
 
   base::string16 paragraph;
-  if (login_url_.spec() == captive_portal::CaptivePortalDetector::kDefaultURL) {
-    // Captive portal may intercept requests without HTTP redirects, in which
-    // case the login url would be the same as the captive portal detection url.
-    // Don't show the login url in that case.
+  if (login_url_.is_empty() ||
+      login_url_.spec() == captive_portal::CaptivePortalDetector::kDefaultURL) {
+    // Don't show the login url when it's empty or is the portal detection URL.
+    // login_url_ can be empty when:
+    // - The captive portal intercepted requests without HTTP redirects, in
+    // which case the login url would be the same as the captive portal
+    // detection url.
+    // - The captive portal was detected via Captive portal certificate list.
+    // - The captive portal was reported by the OS.
     if (wifi_ssid.empty()) {
       paragraph = l10n_util::GetStringUTF16(
           is_wifi ? IDS_CAPTIVE_PORTAL_PRIMARY_PARAGRAPH_NO_LOGIN_URL_WIFI
@@ -216,7 +229,21 @@
     case security_interstitials::CMD_OPEN_LOGIN:
       captive_portal::CaptivePortalMetrics::LogCaptivePortalBlockingPageEvent(
           captive_portal::CaptivePortalMetrics::OPEN_LOGIN_PAGE);
+#if defined(OS_ANDROID)
+      {
+        // CaptivePortalTabHelper is not available on Android. Simply open the
+        // login URL in a new tab. login_url_ is also always empty on Android,
+        // use the platform's portal detection URL.
+        const std::string url = chrome::android::GetCaptivePortalServerUrl(
+            base::android::AttachCurrentThread());
+        content::OpenURLParams params(GURL(url), content::Referrer(),
+                                      WindowOpenDisposition::NEW_FOREGROUND_TAB,
+                                      ui::PAGE_TRANSITION_LINK, false);
+        web_contents()->OpenURL(params);
+      }
+#else
       CaptivePortalTabHelper::OpenLoginTabForWebContents(web_contents(), true);
+#endif
       break;
     case security_interstitials::CMD_DO_REPORT:
       controller()->SetReportingPreference(true);
diff --git a/chrome/browser/ssl/captive_portal_blocking_page.h b/chrome/browser/ssl/captive_portal_blocking_page.h
index c4c234a..dd6cab3 100644
--- a/chrome/browser/ssl/captive_portal_blocking_page.h
+++ b/chrome/browser/ssl/captive_portal_blocking_page.h
@@ -16,10 +16,6 @@
 #include "net/ssl/ssl_info.h"
 #include "url/gurl.h"
 
-#if !BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
-#error This file must be built with ENABLE_CAPTIVE_PORTAL_DETECTION flag.
-#endif
-
 namespace content {
 class NavigationEntry;
 class WebContents;
@@ -77,6 +73,8 @@
 
  private:
   // URL of the login page, opened when the user clicks the "Connect" button.
+  // If empty, the default captive portal detection URL for the platform will be
+  // used.
   const GURL login_url_;
   std::unique_ptr<CertReportHelper> cert_report_helper_;
   const net::SSLInfo ssl_info_;
diff --git a/chrome/browser/ssl/captive_portal_helper_android.cc b/chrome/browser/ssl/captive_portal_helper_android.cc
new file mode 100644
index 0000000..53f49607
--- /dev/null
+++ b/chrome/browser/ssl/captive_portal_helper_android.cc
@@ -0,0 +1,37 @@
+// Copyright 2017 The Chromium 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/android/jni_string.h"
+#include "base/logging.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/ssl/ssl_error_handler.h"
+#include "content/public/browser/browser_thread.h"
+#include "jni/CaptivePortalHelper_jni.h"
+
+namespace chrome {
+namespace android {
+
+void AddCaptivePortalCertificateForTesting(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jclass>& jcaller,
+    const base::android::JavaParamRef<jstring>& jhash) {
+  const std::string hash = ConvertJavaStringToUTF8(env, jhash);
+  auto config_proto =
+      base::MakeUnique<chrome_browser_ssl::SSLErrorAssistantConfig>();
+  config_proto->set_version_id(INT_MAX);
+  config_proto->add_captive_portal_cert()->set_sha256_hash(hash);
+
+  content::BrowserThread::PostTask(
+      content::BrowserThread::UI, FROM_HERE,
+      base::BindOnce(SSLErrorHandler::SetErrorAssistantProto,
+                     std::move(config_proto)));
+}
+
+std::string GetCaptivePortalServerUrl(JNIEnv* env) {
+  return base::android::ConvertJavaStringToUTF8(
+      Java_CaptivePortalHelper_getCaptivePortalServerUrl(env));
+}
+
+}  // namespace android
+}  // namespace chrome
diff --git a/chrome/browser/ssl/captive_portal_helper_android.h b/chrome/browser/ssl/captive_portal_helper_android.h
new file mode 100644
index 0000000..351f7e3
--- /dev/null
+++ b/chrome/browser/ssl/captive_portal_helper_android.h
@@ -0,0 +1,20 @@
+// Copyright 2017 The Chromium 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_SSL_CAPTIVE_PORTAL_HELPER_ANDROID_H_
+#define CHROME_BROWSER_SSL_CAPTIVE_PORTAL_HELPER_ANDROID_H_
+
+#include <jni.h>
+#include <stddef.h>
+#include <string>
+
+namespace chrome {
+namespace android {
+
+std::string GetCaptivePortalServerUrl(JNIEnv* env);
+
+}  // namespace android
+}  // namespace chrome
+
+#endif  // CHROME_BROWSER_SSL_CAPTIVE_PORTAL_HELPER_ANDROID_H_
diff --git a/chrome/browser/ssl/ssl_error_handler.cc b/chrome/browser/ssl/ssl_error_handler.cc
index 7698873..3792635 100644
--- a/chrome/browser/ssl/ssl_error_handler.cc
+++ b/chrome/browser/ssl/ssl_error_handler.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ssl/bad_clock_blocking_page.h"
+#include "chrome/browser/ssl/captive_portal_blocking_page.h"
 #include "chrome/browser/ssl/mitm_software_blocking_page.h"
 #include "chrome/browser/ssl/ssl_blocking_page.h"
 #include "chrome/browser/ssl/ssl_cert_reporter.h"
@@ -46,7 +47,6 @@
 #include "chrome/browser/captive_portal/captive_portal_service.h"
 #include "chrome/browser/captive_portal/captive_portal_service_factory.h"
 #include "chrome/browser/captive_portal/captive_portal_tab_helper.h"
-#include "chrome/browser/ssl/captive_portal_blocking_page.h"
 #endif
 
 #if defined(OS_WIN)
@@ -63,10 +63,10 @@
 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
 const base::Feature kCaptivePortalInterstitial{
     "CaptivePortalInterstitial", base::FEATURE_ENABLED_BY_DEFAULT};
+#endif
 
 const base::Feature kCaptivePortalCertificateList{
     "CaptivePortalCertificateList", base::FEATURE_DISABLED_BY_DEFAULT};
-#endif
 
 const base::Feature kSSLCommonNameMismatchHandling{
     "SSLCommonNameMismatchHandling", base::FEATURE_ENABLED_BY_DEFAULT};
@@ -187,6 +187,7 @@
 bool IsCaptivePortalInterstitialEnabled() {
   return base::FeatureList::IsEnabled(kCaptivePortalInterstitial);
 }
+#endif
 
 std::unique_ptr<std::unordered_set<std::string>> LoadCaptivePortalCertHashes(
     const chrome_browser_ssl::SSLErrorAssistantConfig& proto) {
@@ -197,7 +198,6 @@
   }
   return hashes;
 }
-#endif
 
 bool IsMITMSoftwareInterstitialEnabled() {
   return base::FeatureList::IsEnabled(kMITMSoftwareInterstitial);
@@ -269,12 +269,10 @@
   base::Clock* clock() const;
   network_time::NetworkTimeTracker* network_time_tracker() const;
 
-#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
   // Returns true if any of the SHA256 hashes in |ssl_info| is of a captive
   // portal certificate. The set of captive portal hashes is loaded on first
   // use.
   bool IsKnownCaptivePortalCert(const net::SSLInfo& ssl_info);
-#endif
 
   // Returns the name of a known MITM software provider that matches the
   // certificate passed in as the |cert| parameter. Returns empty string if
@@ -290,6 +288,7 @@
   void SetClockForTesting(base::Clock* clock);
   void SetNetworkTimeTrackerForTesting(
       network_time::NetworkTimeTracker* tracker);
+
   void SetErrorAssistantProto(
       std::unique_ptr<chrome_browser_ssl::SSLErrorAssistantConfig>
           error_assistant_proto);
@@ -325,12 +324,10 @@
   };
   EnterpriseManaged is_enterprise_managed_for_testing_;
 
-#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
   // SPKI hashes belonging to certs treated as captive portals. Null until the
   // first time IsKnownCaptivePortalCert() or SetErrorAssistantProto()
   // is called.
   std::unique_ptr<std::unordered_set<std::string>> captive_portal_spki_hashes_;
-#endif
 };
 
 ConfigSingleton::ConfigSingleton()
@@ -366,10 +363,7 @@
   error_assistant_proto_.reset();
   mitm_software_list_.reset();
   is_enterprise_managed_for_testing_ = ENTERPRISE_MANAGED_STATUS_NOT_SET;
-
-#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
   captive_portal_spki_hashes_.reset();
-#endif
 }
 
 void ConfigSingleton::SetInterstitialDelayForTesting(
@@ -429,7 +423,6 @@
     return true;
   }
 #endif  // #if defined(OS_WIN)
-
   return false;
 }
 
@@ -457,13 +450,10 @@
 
   mitm_software_list_ = LoadMITMSoftwareList(*error_assistant_proto_);
 
-#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
   captive_portal_spki_hashes_ =
       LoadCaptivePortalCertHashes(*error_assistant_proto_);
-#endif  // ENABLE_CAPTIVE_PORTAL_DETECTION
 }
 
-#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
 bool ConfigSingleton::IsKnownCaptivePortalCert(const net::SSLInfo& ssl_info) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (!captive_portal_spki_hashes_) {
@@ -484,7 +474,6 @@
   }
   return false;
 }
-#endif
 
 bool RegexMatchesAny(const std::vector<std::string>& organization_names,
                      const std::string& pattern) {
@@ -655,15 +644,11 @@
 
 void SSLErrorHandlerDelegateImpl::ShowCaptivePortalInterstitial(
     const GURL& landing_url) {
-#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
   // Show captive portal blocking page. The interstitial owns the blocking page.
   (new CaptivePortalBlockingPage(web_contents_, request_url_, landing_url,
                                  std::move(ssl_cert_reporter_), ssl_info_,
                                  callback_))
       ->Show();
-#else
-  NOTREACHED();
-#endif
 }
 
 void SSLErrorHandlerDelegateImpl::ShowMITMSoftwareInterstitial(
@@ -821,7 +806,6 @@
   const bool only_error_is_name_mismatch =
       IsOnlyCertError(net::CERT_STATUS_COMMON_NAME_INVALID);
 
-#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
   // Check known captive portal certificate list if the only error is
   // name-mismatch. If there are multiple errors, it indicates that the captive
   // portal landing page itself will have SSL errors, and so it's not a very
@@ -830,11 +814,9 @@
       only_error_is_name_mismatch &&
       g_config.Pointer()->IsKnownCaptivePortalCert(ssl_info_)) {
     RecordUMA(CAPTIVE_PORTAL_CERT_FOUND);
-    ShowCaptivePortalInterstitial(
-        GURL(captive_portal::CaptivePortalDetector::kDefaultURL));
+    ShowCaptivePortalInterstitial(GURL());
     return;
   }
-#endif
 
   // The MITM software interstitial is displayed if and only if:
   // - the error thrown is not overridable
@@ -913,18 +895,15 @@
 }
 
 void SSLErrorHandler::ShowCaptivePortalInterstitial(const GURL& landing_url) {
-#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
   // Show captive portal blocking page. The interstitial owns the blocking page.
   RecordUMA(delegate_->IsErrorOverridable()
                 ? SHOW_CAPTIVE_PORTAL_INTERSTITIAL_OVERRIDABLE
                 : SHOW_CAPTIVE_PORTAL_INTERSTITIAL_NONOVERRIDABLE);
   delegate_->ShowCaptivePortalInterstitial(landing_url);
+
   // Once an interstitial is displayed, no need to keep the handler around.
   // This is the equivalent of "delete this". It also destroys the timer.
   web_contents_->RemoveUserData(UserDataKey());
-#else
-  NOTREACHED();
-#endif
 }
 
 void SSLErrorHandler::ShowMITMSoftwareInterstitial(
diff --git a/chrome/browser/ssl/ssl_error_handler_unittest.cc b/chrome/browser/ssl/ssl_error_handler_unittest.cc
index 6808c22d..4c319d82 100644
--- a/chrome/browser/ssl/ssl_error_handler_unittest.cc
+++ b/chrome/browser/ssl/ssl_error_handler_unittest.cc
@@ -437,7 +437,9 @@
 
     RunCaptivePortalTest();
 
-    // Timer should start for captive portal detection.
+#if !defined(OS_ANDROID)
+    // On non-Android platforms (except for iOS where this code is disabled),
+    // timer should start for captive portal detection.
     EXPECT_TRUE(error_handler()->IsTimerRunningForTesting());
     EXPECT_TRUE(delegate()->captive_portal_checked());
     EXPECT_FALSE(delegate()->ssl_interstitial_shown());
@@ -447,10 +449,30 @@
     base::RunLoop().RunUntilIdle();
 
     EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
+
+    // Captive portal should be checked on non-Android platforms.
     EXPECT_TRUE(delegate()->captive_portal_checked());
     EXPECT_TRUE(delegate()->ssl_interstitial_shown());
     EXPECT_FALSE(delegate()->captive_portal_interstitial_shown());
     EXPECT_FALSE(delegate()->suggested_url_checked());
+#else
+    // On Android there is no custom captive portal detection logic, so the
+    // timer should not start and an SSL interstitial should be shown
+    // immediately.
+    EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
+    EXPECT_FALSE(delegate()->captive_portal_checked());
+    EXPECT_TRUE(delegate()->ssl_interstitial_shown());
+    EXPECT_FALSE(delegate()->captive_portal_interstitial_shown());
+    EXPECT_FALSE(delegate()->suggested_url_checked());
+
+    base::RunLoop().RunUntilIdle();
+
+    EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
+    EXPECT_FALSE(delegate()->captive_portal_checked());
+    EXPECT_TRUE(delegate()->ssl_interstitial_shown());
+    EXPECT_FALSE(delegate()->captive_portal_interstitial_shown());
+    EXPECT_FALSE(delegate()->suggested_url_checked());
+#endif
 
     // Check that the histogram for the captive portal cert was recorded.
     histograms.ExpectTotalCount(SSLErrorHandler::GetHistogramNameForTesting(),
@@ -1030,8 +1052,6 @@
   ASSERT_TRUE(test_server()->ShutdownAndWaitUntilComplete());
 }
 
-#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
-
 // Tests that a certificate marked as a known captive portal certificate causes
 // the captive portal interstitial to be shown.
 TEST_F(SSLErrorAssistantTest, CaptivePortal_FeatureEnabled) {
@@ -1125,42 +1145,6 @@
   TestNoCaptivePortalInterstitial();
 }
 
-#else
-
-TEST_F(SSLErrorAssistantTest, CaptivePortal_DisabledByBuild) {
-  SetCaptivePortalFeatureEnabled(true);
-
-  // Default error for SSLErrorHandlerNameMismatchTest tests is name mismatch,
-  // but the feature is disabled by build so a generic SSL interstitial will be
-  // displayed.
-  base::HistogramTester histograms;
-
-  RunCaptivePortalTest();
-
-  EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
-  EXPECT_FALSE(delegate()->captive_portal_checked());
-  EXPECT_TRUE(delegate()->ssl_interstitial_shown());
-  EXPECT_FALSE(delegate()->captive_portal_interstitial_shown());
-  EXPECT_FALSE(delegate()->suggested_url_checked());
-
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
-  EXPECT_FALSE(delegate()->captive_portal_checked());
-  EXPECT_TRUE(delegate()->ssl_interstitial_shown());
-  EXPECT_FALSE(delegate()->captive_portal_interstitial_shown());
-  EXPECT_FALSE(delegate()->suggested_url_checked());
-
-  histograms.ExpectTotalCount(SSLErrorHandler::GetHistogramNameForTesting(), 2);
-  histograms.ExpectBucketCount(SSLErrorHandler::GetHistogramNameForTesting(),
-                               SSLErrorHandler::HANDLE_ALL, 1);
-  histograms.ExpectBucketCount(
-      SSLErrorHandler::GetHistogramNameForTesting(),
-      SSLErrorHandler::SHOW_SSL_INTERSTITIAL_OVERRIDABLE, 1);
-}
-
-#endif  // BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
-
 // Tests that if a certificate matches the issuer common name regex of a MITM
 // software entry but not the issuer organization name a MITM software
 // interstitial will not be displayed.
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index a96757b..a1eb13a 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -347,6 +347,10 @@
     "webui/device_log_ui.h",
     "webui/domain_reliability_internals_ui.cc",
     "webui/domain_reliability_internals_ui.h",
+    "webui/download_internals/download_internals_ui.cc",
+    "webui/download_internals/download_internals_ui.h",
+    "webui/download_internals/download_internals_ui_message_handler.cc",
+    "webui/download_internals/download_internals_ui_message_handler.h",
     "webui/engagement/site_engagement_ui.cc",
     "webui/engagement/site_engagement_ui.h",
     "webui/favicon_source.cc",
diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc
index 473fb51..10b0de50 100644
--- a/chrome/browser/ui/browser_navigator.cc
+++ b/chrome/browser/ui/browser_navigator.cc
@@ -632,7 +632,8 @@
   // chrome://extensions is on the list because it redirects to
   // chrome://settings.
   if (url.scheme() == content::kChromeUIScheme &&
-      (url.host_piece() == chrome::kChromeUISettingsHost ||
+      (url.host_piece() == chrome::kChromeUIDownloadInternalsHost ||
+       url.host_piece() == chrome::kChromeUISettingsHost ||
        url.host_piece() == chrome::kChromeUIHelpHost ||
        url.host_piece() == chrome::kChromeUIHistoryHost ||
        url.host_piece() == chrome::kChromeUIExtensionsHost ||
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 c6200fe0..eeb92c5 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -29,6 +29,7 @@
 #include "chrome/browser/ui/webui/crashes_ui.h"
 #include "chrome/browser/ui/webui/device_log_ui.h"
 #include "chrome/browser/ui/webui/domain_reliability_internals_ui.h"
+#include "chrome/browser/ui/webui/download_internals/download_internals_ui.h"
 #include "chrome/browser/ui/webui/engagement/site_engagement_ui.h"
 #include "chrome/browser/ui/webui/flags_ui.h"
 #include "chrome/browser/ui/webui/flash_ui.h"
@@ -330,6 +331,11 @@
     return &NewWebUI<chromeos::DeviceLogUI>;
   if (url.host_piece() == chrome::kChromeUIDomainReliabilityInternalsHost)
     return &NewWebUI<DomainReliabilityInternalsUI>;
+  // TODO(dtrainor): Remove the OffTheRecord check once crbug.com/766363 is
+  // fixed.
+  if (url.host_piece() == chrome::kChromeUIDownloadInternalsHost &&
+      !profile->IsOffTheRecord())
+    return &NewWebUI<DownloadInternalsUI>;
   if (url.host_piece() == chrome::kChromeUIFlagsHost)
     return &NewWebUI<FlagsUI>;
   if (url.host_piece() == chrome::kChromeUIGCMInternalsHost)
diff --git a/chrome/browser/ui/webui/download_internals/download_internals_ui.cc b/chrome/browser/ui/webui/download_internals/download_internals_ui.cc
new file mode 100644
index 0000000..6f6ef33f
--- /dev/null
+++ b/chrome/browser/ui/webui/download_internals/download_internals_ui.cc
@@ -0,0 +1,44 @@
+// Copyright 2017 The Chromium 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/download_internals/download_internals_ui.h"
+
+#include <utility>
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/download_internals/download_internals_ui_message_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+DownloadInternalsUI::DownloadInternalsUI(content::WebUI* web_ui)
+    : content::WebUIController(web_ui) {
+  // chrome://download-internals source.
+  content::WebUIDataSource* html_source =
+      content::WebUIDataSource::Create(chrome::kChromeUIDownloadInternalsHost);
+
+  // Required resources.
+  html_source->SetJsonPath("strings.js");
+  html_source->AddResourcePath("download_internals.css",
+                               IDR_DOWNLOAD_INTERNALS_CSS);
+  html_source->AddResourcePath("download_internals.js",
+                               IDR_DOWNLOAD_INTERNALS_JS);
+  html_source->AddResourcePath("download_internals_browser_proxy.js",
+                               IDR_DOWNLOAD_INTERNALS_BROWSER_PROXY_JS);
+  html_source->SetDefaultResource(IDR_DOWNLOAD_INTERNALS_HTML);
+  html_source->UseGzip();
+
+  Profile* profile = Profile::FromWebUI(web_ui);
+  html_source->AddBoolean("isIncognito", profile->IsOffTheRecord());
+
+  content::WebUIDataSource::Add(profile, html_source);
+
+  web_ui->AddMessageHandler(
+      std::make_unique<
+          download_internals::DownloadInternalsUIMessageHandler>());
+}
+
+DownloadInternalsUI::~DownloadInternalsUI() = default;
diff --git a/chrome/browser/ui/webui/download_internals/download_internals_ui.h b/chrome/browser/ui/webui/download_internals/download_internals_ui.h
new file mode 100644
index 0000000..1785e63
--- /dev/null
+++ b/chrome/browser/ui/webui/download_internals/download_internals_ui.h
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium 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_DOWNLOAD_INTERNALS_DOWNLOAD_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_DOWNLOAD_INTERNALS_DOWNLOAD_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The WebUI for chrome://download-internals.
+class DownloadInternalsUI : public content::WebUIController {
+ public:
+  explicit DownloadInternalsUI(content::WebUI* web_ui);
+  ~DownloadInternalsUI() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DownloadInternalsUI);
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_DOWNLOAD_INTERNALS_DOWNLOAD_INTERNALS_UI_H_
diff --git a/chrome/browser/ui/webui/download_internals/download_internals_ui_message_handler.cc b/chrome/browser/ui/webui/download_internals/download_internals_ui_message_handler.cc
new file mode 100644
index 0000000..bd055cb
--- /dev/null
+++ b/chrome/browser/ui/webui/download_internals/download_internals_ui_message_handler.cc
@@ -0,0 +1,53 @@
+// Copyright 2017 The Chromium 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/download_internals/download_internals_ui_message_handler.h"
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "chrome/browser/download/download_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/download/public/download_service.h"
+#include "content/public/browser/web_ui.h"
+
+namespace download_internals {
+
+DownloadInternalsUIMessageHandler::DownloadInternalsUIMessageHandler()
+    : download_service_(nullptr), weak_ptr_factory_(this) {}
+
+DownloadInternalsUIMessageHandler::~DownloadInternalsUIMessageHandler() {
+  if (download_service_)
+    download_service_->GetLogger()->RemoveObserver(this);
+}
+
+void DownloadInternalsUIMessageHandler::RegisterMessages() {
+  web_ui()->RegisterMessageCallback(
+      "getServiceStatus",
+      base::Bind(&DownloadInternalsUIMessageHandler::HandleGetServiceStatus,
+                 weak_ptr_factory_.GetWeakPtr()));
+
+  Profile* profile = Profile::FromWebUI(web_ui());
+  download_service_ = DownloadServiceFactory::GetForBrowserContext(profile);
+  download_service_->GetLogger()->AddObserver(this);
+}
+
+void DownloadInternalsUIMessageHandler::OnServiceStatusChanged(
+    const base::Value& service_status) {
+  if (!IsJavascriptAllowed())
+    return;
+
+  FireWebUIListener("service-status-changed", service_status);
+}
+
+void DownloadInternalsUIMessageHandler::HandleGetServiceStatus(
+    const base::ListValue* args) {
+  AllowJavascript();
+  const base::Value* callback_id;
+  CHECK(args->Get(0, &callback_id));
+
+  ResolveJavascriptCallback(*callback_id,
+                            download_service_->GetLogger()->GetServiceStatus());
+}
+
+}  // namespace download_internals
diff --git a/chrome/browser/ui/webui/download_internals/download_internals_ui_message_handler.h b/chrome/browser/ui/webui/download_internals/download_internals_ui_message_handler.h
new file mode 100644
index 0000000..aadb255d
--- /dev/null
+++ b/chrome/browser/ui/webui/download_internals/download_internals_ui_message_handler.h
@@ -0,0 +1,45 @@
+// Copyright 2017 The Chromium 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_DOWNLOAD_INTERNALS_DOWNLOAD_INTERNALS_UI_MESSAGE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_DOWNLOAD_INTERNALS_DOWNLOAD_INTERNALS_UI_MESSAGE_HANDLER_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/download/public/logger.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace download {
+class DownloadService;
+}
+
+namespace download_internals {
+
+// Class acting as a controller of the chrome://download-internals WebUI.
+class DownloadInternalsUIMessageHandler : public content::WebUIMessageHandler,
+                                          public download::Logger::Observer {
+ public:
+  DownloadInternalsUIMessageHandler();
+  ~DownloadInternalsUIMessageHandler() override;
+
+  // content::WebUIMessageHandler implementation.
+  void RegisterMessages() override;
+
+  // download::Logger::Observer implementation.
+  void OnServiceStatusChanged(const base::Value& service_status) override;
+
+ private:
+  // Get the current DownloadService and sub component statuses.
+  void HandleGetServiceStatus(const base::ListValue* args);
+
+  download::DownloadService* download_service_;
+
+  base::WeakPtrFactory<DownloadInternalsUIMessageHandler> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(DownloadInternalsUIMessageHandler);
+};
+
+}  // namespace download_internals
+
+#endif  // CHROME_BROWSER_UI_WEBUI_DOWNLOAD_INTERNALS_DOWNLOAD_INTERNALS_UI_MESSAGE_HANDLER_H_
diff --git a/chrome/browser/ui/webui/interstitials/interstitial_ui_browsertest.cc b/chrome/browser/ui/webui/interstitials/interstitial_ui_browsertest.cc
index 9c0450c..91b8ee2c 100644
--- a/chrome/browser/ui/webui/interstitials/interstitial_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/interstitials/interstitial_ui_browsertest.cc
@@ -12,6 +12,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/test/test_navigation_observer.h"
 
 class InterstitialUITest : public InProcessBrowserTest {
  public:
@@ -121,13 +122,27 @@
                    "Connect to Wi-Fi");
 }
 
+// Tests that back button works after opening an interstitial from
+// chrome://interstitials.
+IN_PROC_BROWSER_TEST_F(InterstitialUITest, InterstitialBackButton) {
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ui_test_utils::NavigateToURL(browser(), GURL("chrome://interstitials"));
+  ui_test_utils::NavigateToURL(browser(), GURL("chrome://interstitials/ssl"));
+  content::TestNavigationObserver navigation_observer(web_contents);
+  chrome::GoBack(browser(), WindowOpenDisposition::CURRENT_TAB);
+  navigation_observer.Wait();
+  base::string16 title;
+  ui_test_utils::GetCurrentTabTitle(browser(), &title);
+  EXPECT_EQ(title, base::ASCIIToUTF16("Interstitials"));
+}
+
 // Checks that the interstitial page uses correct web contents. If not, closing
 // the tab might result in a freed web contents pointer and cause a crash.
 // See https://crbug.com/611706 for details.
 IN_PROC_BROWSER_TEST_F(InterstitialUITest, UseCorrectWebContents) {
   int current_tab = browser()->tab_strip_model()->active_index();
   ui_test_utils::NavigateToURL(browser(), GURL("chrome://interstitials/ssl"));
-
   // Duplicate the tab and close it.
   chrome::DuplicateTab(browser());
   EXPECT_NE(current_tab, browser()->tab_strip_model()->active_index());
diff --git a/chrome/child/BUILD.gn b/chrome/child/BUILD.gn
index 8126ea6..2762e703 100644
--- a/chrome/child/BUILD.gn
+++ b/chrome/child/BUILD.gn
@@ -18,6 +18,7 @@
     "//base",
     "//content/public/child",
     "//gin",
+    "//tools/v8_context_snapshot",
     "//v8",
   ]
 
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 2184ba9c..06d20421 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -436,6 +436,7 @@
   if (is_win) {
     deps = [
       "//chrome/common/win:eventlog_messages",
+      "//chrome_elf:chrome_elf_main_include",
       "//components/crash/content/app:crash_export_thunk_include",
     ]
 
diff --git a/chrome/common/DEPS b/chrome/common/DEPS
index 6c7a6d5..44c5b4c 100644
--- a/chrome/common/DEPS
+++ b/chrome/common/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+chrome/grit",
   "+chrome/install_static",
+  "+chrome_elf/chrome_elf_main.h",
   "+chromeos",  # For chromeos_switches.h
   "+components/autofill/content/common",
   "+components/autofill/core/common",
diff --git a/chrome/common/child_process_logging_win.cc b/chrome/common/child_process_logging_win.cc
index 242c455..7d748de 100644
--- a/chrome/common/child_process_logging_win.cc
+++ b/chrome/common/child_process_logging_win.cc
@@ -13,6 +13,7 @@
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/crash_keys.h"
 #include "chrome/installer/util/google_update_settings.h"
+#include "chrome_elf/chrome_elf_main.h"
 #include "components/crash/content/app/crash_export_thunks.h"
 #include "components/metrics/client_info.h"
 
@@ -35,7 +36,6 @@
 void Init() {
   base::debug::SetCrashKeyReportingFunctions(&SetCrashKeyValueTrampoline,
                                              &ClearCrashKeyValueTrampoline);
-
   // This would be handled by BreakpadClient::SetCrashClientIdFromGUID(), but
   // because of the aforementioned issue, crash keys aren't ready yet at the
   // time of Breakpad initialization, load the client id backed up in Google
@@ -45,34 +45,12 @@
   std::unique_ptr<metrics::ClientInfo> client_info =
       GoogleUpdateSettings::LoadMetricsClientInfo();
 
-  // Set the client id in chrome_elf if it is loaded. We should not be
-  // registering crash keys in this case as that would already have been
-  // done by chrome_elf.
-  HMODULE elf_module = GetModuleHandle(chrome::kChromeElfDllName);
-  if (elf_module) {
-// TODO(ananta)
-// Remove this when the change to not require crash key registration lands.
-// Please note that we are registering the crash keys twice if chrome_elf is
-// loaded. Once in chrome_elf and once in the current module. Alternatively
-// we could implement a crash key lookup trampoline which defers to
-// chrome_elf. We decided to go with the duplicate key registration for
-// simplicity.
 #if !defined(COMPONENT_BUILD)
-    crash_keys::RegisterChromeCrashKeys();
+  crash_keys::RegisterChromeCrashKeys();
 #endif
-    using SetMetricsClientIdFunction = void (*)(const char* client_id);
-    SetMetricsClientIdFunction set_metrics_id_fn =
-        reinterpret_cast<SetMetricsClientIdFunction>(
-            ::GetProcAddress(elf_module, "SetMetricsClientId"));
-    DCHECK(set_metrics_id_fn);
-    set_metrics_id_fn(client_info ? client_info->client_id.c_str() : nullptr);
-  } else {
-    // TODO(ananta)
-    // Remove this when the change to not require crash key registration lands.
-    crash_keys::RegisterChromeCrashKeys();
-    if (client_info)
-      crash_keys::SetMetricsClientIdFromGUID(client_info->client_id);
-  }
+
+  // Set the client id chrome_elf (in tests this is stubbed).
+  SetMetricsClientId(client_info ? client_info->client_id.c_str() : nullptr);
 }
 
 }  // namespace child_process_logging
diff --git a/chrome/common/chrome_constants.cc b/chrome/common/chrome_constants.cc
index 70ae393d..d4e0823a 100644
--- a/chrome/common/chrome_constants.cc
+++ b/chrome/common/chrome_constants.cc
@@ -122,7 +122,6 @@
 
 #if defined(OS_WIN)
 const base::FilePath::CharType kBrowserResourcesDll[] = FPL("chrome.dll");
-const base::FilePath::CharType kChromeElfDllName[] = FPL("chrome_elf.dll");
 const base::FilePath::CharType kStatusTrayWindowClass[] =
     FPL("Chrome_StatusTrayWindow");
 #endif  // defined(OS_WIN)
diff --git a/chrome/common/chrome_constants.h b/chrome/common/chrome_constants.h
index 5eae966..6b072cc 100644
--- a/chrome/common/chrome_constants.h
+++ b/chrome/common/chrome_constants.h
@@ -33,7 +33,6 @@
 #endif  // OS_MACOSX
 #if defined(OS_WIN)
 extern const base::FilePath::CharType kBrowserResourcesDll[];
-extern const base::FilePath::CharType kChromeElfDllName[];
 extern const base::FilePath::CharType kStatusTrayWindowClass[];
 #endif  // defined(OS_WIN)
 
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index f08dcd6b..46fc8129 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -195,6 +195,7 @@
 const char kChromeUIDomainReliabilityInternalsHost[] =
     "domain-reliability-internals";
 const char kChromeUIDownloadsHost[] = "downloads";
+const char kChromeUIDownloadInternalsHost[] = "download-internals";
 const char kChromeUIDriveInternalsHost[] = "drive-internals";
 const char kChromeUIExtensionIconHost[] = "extension-icon";
 const char kChromeUIExtensionsFrameHost[] = "extensions-frame";
@@ -654,6 +655,7 @@
     kChromeUIDeviceEmulatorHost,
 #endif
     kChromeUIDeviceLogHost,
+    kChromeUIDownloadInternalsHost,
     kChromeUIFlagsHost,
     kChromeUIGCMInternalsHost,
     kChromeUIHistoryHost,
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index 8797e1c2..786fade 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -182,6 +182,7 @@
 extern const char kChromeUIDNSHost[];
 extern const char kChromeUIDomainReliabilityInternalsHost[];
 extern const char kChromeUIDownloadsHost[];
+extern const char kChromeUIDownloadInternalsHost[];
 extern const char kChromeUIDriveInternalsHost[];
 extern const char kChromeUIExtensionIconHost[];
 extern const char kChromeUIExtensionsFrameHost[];
diff --git a/chrome/installer/linux/BUILD.gn b/chrome/installer/linux/BUILD.gn
index 2fe606f..af7487ed 100644
--- a/chrome/installer/linux/BUILD.gn
+++ b/chrome/installer/linux/BUILD.gn
@@ -156,7 +156,7 @@
   deps = [
     "//chrome",
   ]
-  script = "strip_chrome_binary.py"
+  script = "//build/gn_run_binary.py"
   sources = [
     prog_name,
   ]
@@ -166,9 +166,11 @@
   ]
   args = [
     rebase_path("//third_party/eu-strip/bin/eu-strip", root_build_dir),
-    rebase_path(prog_name, root_build_dir),
-    rebase_path(debug_file, root_build_dir),
+    "-o",
     rebase_path(stripped_file, root_build_dir),
+    "-f",
+    rebase_path(debug_file, root_build_dir),
+    rebase_path(prog_name, root_build_dir),
   ]
 }
 
@@ -212,6 +214,7 @@
 copy("common_packaging_files") {
   visibility = [ ":*" ]
   sources = [
+    "//chrome/app/resources/manpage.1.in",
     "common/apt.include",
     "common/default-app-block.template",
     "common/default-app.template",
@@ -365,7 +368,7 @@
   deb_target_name = "${target_name}_deb"
   action(deb_target_name) {
     visibility = [ ":*" ]
-    script = "make_package.py"
+    script = "//build/gn_run_binary.py"
 
     if (current_cpu == "x86") {
       deb_arch = "i386"
@@ -415,7 +418,7 @@
     rpm_target_name = "${target_name}_rpm"
     action(rpm_target_name) {
       visibility = [ ":*" ]
-      script = "make_package.py"
+      script = "//build/gn_run_binary.py"
 
       if (current_cpu == "x86") {
         rpm_arch = "i386"
diff --git a/chrome/installer/linux/common/installer.include b/chrome/installer/linux/common/installer.include
index d2d98bfb..0bc8bd89 100644
--- a/chrome/installer/linux/common/installer.include
+++ b/chrome/installer/linux/common/installer.include
@@ -284,8 +284,9 @@
   chmod 644 "${STAGEDIR}${INSTALLDIR}/default-app-block"
 
   # documentation
-  install -m 644 "${BUILDDIR}/${PROGNAME}.1" \
+  process_template "${BUILDDIR}/installer/common/manpage.1.in" \
     "${STAGEDIR}/usr/share/man/man1/${PACKAGE}.1"
+  chmod 644 "${STAGEDIR}/usr/share/man/man1/${PACKAGE}.1"
 
   # Check to make sure all the ELF binaries are stripped.
   UNSTRIPPED=$(find "${STAGEDIR}/${INSTALLDIR}/" -type f | xargs file |
@@ -295,6 +296,16 @@
     exit 1
   fi
 
+  # Make sure ELF binaries live in INSTALLDIR exclusively.
+  ELF_OUTSIDE_INSTALLDIR=$(find "${STAGEDIR}/" -not -path \
+    "${STAGEDIR}${INSTALLDIR}/*" -type f | xargs file -b |
+    grep -ce "^ELF" || true)
+  if [ "${ELF_OUTSIDE_INSTALLDIR}" -ne 0 ]; then
+    echo "ERROR: Found ${ELF_OUTSIDE_INSTALLDIR} ELF binaries" \
+      "outside of ${INSTALLDIR}" 1>&2
+    exit 1
+  fi
+
   # Verify file permissions.
   for file in $(find "${STAGEDIR}" -mindepth 1); do
     local actual_perms=$(stat -c "%a" "${file}")
diff --git a/chrome/installer/linux/make_package.py b/chrome/installer/linux/make_package.py
deleted file mode 100644
index f64bd33..0000000
--- a/chrome/installer/linux/make_package.py
+++ /dev/null
@@ -1,14 +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.
-
-# Usage:
-#   make_package.py <shell_script> [<args_to_script>*]
-
-import subprocess
-import sys
-
-if len(sys.argv) < 2:
-  print "Incorrect args."
-  sys.exit(1)
-sys.exit(subprocess.call(["bash"] + sys.argv[1:]))
diff --git a/chrome/installer/linux/strip_chrome_binary.py b/chrome/installer/linux/strip_chrome_binary.py
deleted file mode 100755
index 8fdc42f..0000000
--- a/chrome/installer/linux/strip_chrome_binary.py
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2017 The Chromium 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 subprocess
-import sys
-
-if len(sys.argv) != 5:
-  print ("Usage: strip_chrome_binary.py /path/to/eu-strip /path/to/chrome"
-         "/path/to/chrome.debug /path/to/chrome.stripped")
-  sys.exit(1)
-eu_strip = sys.argv[1]
-chrome = sys.argv[2]
-chrome_debug = sys.argv[3]
-chrome_stripped = sys.argv[4]
-sys.exit(subprocess.call([eu_strip, '-o', chrome_stripped, '-f', chrome_debug,
-                          chrome]))
diff --git a/chrome/installer/mini_installer/BUILD.gn b/chrome/installer/mini_installer/BUILD.gn
index d8b700f..d8249e6 100644
--- a/chrome/installer/mini_installer/BUILD.gn
+++ b/chrome/installer/mini_installer/BUILD.gn
@@ -176,7 +176,7 @@
       "//chrome/browser/extensions/default_extensions",
       "//chrome/installer/setup",
       "//third_party/icu:icudata",
-      "//tools/v8_context_snapshot:v8_context_snapshot",
+      "//tools/v8_context_snapshot",
       chrome_dll_target,
     ]
 
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index 82318fb..d3a748f 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -159,6 +159,7 @@
     "//third_party/icu",
     "//third_party/re2",
     "//third_party/widevine/cdm:headers",
+    "//tools/v8_context_snapshot",
     "//ui/surface",
     "//v8:v8",
   ]
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/TabTitleObserver.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/TabTitleObserver.java
index b1a1c8dd..b140b05 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/TabTitleObserver.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/TabTitleObserver.java
@@ -14,7 +14,8 @@
 import java.util.concurrent.TimeoutException;
 
 /**
- * A utility class to get notified of title change in a tab.
+ * A utility class to get notified of title change in a tab. Subclasses can
+ * override doesTitleMatch() to customize title matching.
  */
 public class TabTitleObserver extends EmptyTabObserver {
     private final String mExpectedTitle;
@@ -51,7 +52,7 @@
     }
 
     private boolean notifyCallbackIfTitleMatches(Tab tab) {
-        if (TextUtils.equals(tab.getTitle(), mExpectedTitle)) {
+        if (doesTitleMatch(mExpectedTitle, tab.getTitle())) {
             mCallback.notifyCalled();
             return true;
         }
@@ -62,4 +63,9 @@
     public void onTitleUpdated(Tab tab) {
         notifyCallbackIfTitleMatches(tab);
     }
+
+    /** @return Whether the title matches the expected condition. */
+    protected boolean doesTitleMatch(String expectedTitle, String actualTitle) {
+        return TextUtils.equals(expectedTitle, actualTitle);
+    }
 }
\ No newline at end of file
diff --git a/chrome/test/data/pdf/post_message_zero_sized_embed.html b/chrome/test/data/pdf/post_message_zero_sized_embed.html
new file mode 100644
index 0000000..1f8e8d7
--- /dev/null
+++ b/chrome/test/data/pdf/post_message_zero_sized_embed.html
@@ -0,0 +1,31 @@
+<html>
+<body>
+<script>
+/*
+  This test page will attach an <embed> of size 0x0 and posts message to it to
+  verify that the BrowserPlugin and MimeHandlerViewContainer are created and
+  the extension properly loaded. This is for a regression in slides/ pages where
+  post messaging to <embed> should pop up print preview (only on Chrome). For
+  for context see https://crbug.com/763812.
+*/
+  window.addEventListener('load', attachPdfAndPostMessage);
+  window.addEventListener('message', onMessageReceived);
+
+  function attachPdfAndPostMessage() {
+    var e = document.createElement('embed');
+    e.style.width = '0px';
+    e.style.height = '0px';
+    e.type = 'application/pdf';
+    e.src = 'test.pdf';
+    document.body.appendChild(e);
+    e.postMessage({type: 'getSelectedText'});
+  }
+
+  function onMessageReceived(msg) {
+    if (msg.data.type && msg.data.type === 'getSelectedTextReply')
+      window.domAutomationController.send('POST_MESSAGE_OK');
+  }
+
+</script>
+</body>
+</html>
diff --git a/chrome/test/v8/wasm_trap_handler.cc b/chrome/test/v8/wasm_trap_handler.cc
index c5afb9a1..b3e3634 100644
--- a/chrome/test/v8/wasm_trap_handler.cc
+++ b/chrome/test/v8/wasm_trap_handler.cc
@@ -15,6 +15,17 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
 
+namespace {
+// kIsTrapHandlerSupported indicates whether the trap handler is supported
+// (i.e. allowed to be enabled) on the currently platform. Currently we only
+// support non-Android, Linux x64. In the future more platforms will be
+// supported.
+#if defined(OS_LINUX) && defined(ARCH_CPU_X86_64) && !defined(OS_ANDROID)
+constexpr bool kIsTrapHandlerSupported = true;
+#else
+constexpr bool kIsTrapHandlerSupported = false;
+#endif
+
 class WasmTrapHandler : public InProcessBrowserTest {
  public:
   WasmTrapHandler() {}
@@ -29,13 +40,7 @@
   }
 
   void RunJSTestAndEnsureTrapHandlerRan(const std::string& js) const {
-#if defined(OS_LINUX) && defined(ARCH_CPU_X86_64) && !defined(OS_ANDROID)
-    const bool is_trap_handler_supported = true;
-#else
-    const bool is_trap_handler_supported = false;
-#endif
-
-    if (is_trap_handler_supported &&
+    if (kIsTrapHandlerSupported &&
         base::FeatureList::IsEnabled(features::kWebAssemblyTrapHandler)) {
       const auto* get_fault_count =
           "domAutomationController.send(%GetWasmRecoveredTrapCount())";
@@ -95,3 +100,16 @@
   ASSERT_NO_FATAL_FAILURE(RunJSTestAndEnsureTrapHandlerRan(
       "poke_out_of_bounds_grow_memory_wasm()"));
 }
+
+IN_PROC_BROWSER_TEST_F(WasmTrapHandler, TrapHandlerCorrectlyConfigured) {
+  const char* script =
+      "domAutomationController.send(%IsWasmTrapHandlerEnabled())";
+  bool is_trap_handler_enabled = false;
+  auto* const tab = browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(tab, script,
+                                                   &is_trap_handler_enabled));
+  ASSERT_EQ(is_trap_handler_enabled,
+            kIsTrapHandlerSupported && base::FeatureList::IsEnabled(
+                                           features::kWebAssemblyTrapHandler));
+}
+}  // namespace
diff --git a/chrome/tools/build/linux/sed.py b/chrome/tools/build/linux/sed.py
deleted file mode 100644
index e7a6ad5..0000000
--- a/chrome/tools/build/linux/sed.py
+++ /dev/null
@@ -1,21 +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.
-
-# Usage sed.py <infile> <outfile> [<other args>]
-#
-# Does the equivalent of
-#   sed <options> infile > outfile
-
-import sys
-import subprocess
-
-if len(sys.argv) <= 3:
-  print "Need at least 3 arguments to sed.py"
-  sys.exit(1)
-
-infile = sys.argv[1]
-outfile = sys.argv[2]
-
-sys.exit(subprocess.call('sed "' + '" "'.join(sys.argv[3:]) + '" ' + infile +
-                         ' > ' + outfile, shell=True))
diff --git a/chrome_elf/BUILD.gn b/chrome_elf/BUILD.gn
index f8ac4823..6d2491b3 100644
--- a/chrome_elf/BUILD.gn
+++ b/chrome_elf/BUILD.gn
@@ -28,6 +28,14 @@
   ]
 }
 
+# Users of chrome_elf exports can depend on this target, which doesn't
+# pin them to linking either chrome_elf.dll or test_stubs.
+source_set("chrome_elf_main_include") {
+  sources = [
+    "chrome_elf_main.h",
+  ]
+}
+
 # We should move chrome_result_codes.h to another target which does not bring
 # in the world.
 shared_library("chrome_elf") {
diff --git a/chrome_elf/chrome_elf_main.cc b/chrome_elf/chrome_elf_main.cc
index 2df2f56..50912fe1 100644
--- a/chrome_elf/chrome_elf_main.cc
+++ b/chrome_elf/chrome_elf_main.cc
@@ -69,3 +69,7 @@
 void DumpProcessWithoutCrash() {
   elf_crash::DumpWithoutCrashing();
 }
+
+void SetMetricsClientId(const char* client_id) {
+  elf_crash::SetMetricsClientIdImpl(client_id);
+}
diff --git a/chrome_elf/chrome_elf_main.h b/chrome_elf/chrome_elf_main.h
index 71eb69b..c313294 100644
--- a/chrome_elf/chrome_elf_main.h
+++ b/chrome_elf/chrome_elf_main.h
@@ -6,7 +6,7 @@
 #define CHROME_ELF_CHROME_ELF_MAIN_H_
 
 // These functions are the cross-module import interface to chrome_elf.dll.
-// It is used chrome.exe, chrome.dll and other clients of chrome_elf.
+// It is used by chrome.exe, chrome.dll and other clients of chrome_elf.
 // In tests, these functions are stubbed by implementations in
 // chrome_elf_test_stubs.cc.
 extern "C" {
@@ -25,6 +25,9 @@
 void SignalInitializeCrashReporting();
 void SignalChromeElf();
 
+// Sets the metrics client ID in crash keys.
+void SetMetricsClientId(const char* client_id);
+
 }  // extern "C"
 
 #endif  // CHROME_ELF_CHROME_ELF_MAIN_H_
diff --git a/chrome_elf/chrome_elf_test_stubs.cc b/chrome_elf/chrome_elf_test_stubs.cc
index bec3eea7..240c6b229 100644
--- a/chrome_elf/chrome_elf_test_stubs.cc
+++ b/chrome_elf/chrome_elf_test_stubs.cc
@@ -33,3 +33,5 @@
 
   return !user_data_dir_path.empty();
 }
+
+void SetMetricsClientId(const char* client_id) {}
diff --git a/chrome_elf/crash/crash_helper.cc b/chrome_elf/crash/crash_helper.cc
index e1bede4..e8e27dc 100644
--- a/chrome_elf/crash/crash_helper.cc
+++ b/chrome_elf/crash/crash_helper.cc
@@ -118,29 +118,11 @@
   crash_reporter::DumpWithoutCrashing();
 }
 
-}  // namespace elf_crash
-
-//------------------------------------------------------------------------------
-// Exported crash APIs for the rest of the process.
-//------------------------------------------------------------------------------
-
-// This helper is invoked by code in chrome.dll to retrieve the crash reports.
-// See CrashUploadListCrashpad. Note that we do not pass a std::vector here,
-// because we do not want to allocate/free in different modules. The returned
-// pointer is read-only.
-//
-// NOTE: Since the returned pointer references read-only memory that will be
-// cleaned up when this DLL unloads, be careful not to reference the memory
-// beyond that point (e.g. during tests).
-extern "C" {
-
-// This helper is invoked by debugging code in chrome to register the client
-// id.
-void SetMetricsClientId(const char* client_id) {
+void SetMetricsClientIdImpl(const char* client_id) {
   if (!g_crash_helper_enabled)
     return;
   if (client_id)
     crash_keys::SetMetricsClientIdFromGUID(client_id);
 }
 
-}  // extern "C"
+}  // namespace elf_crash
diff --git a/chrome_elf/crash/crash_helper.h b/chrome_elf/crash/crash_helper.h
index c1bc1ce..b15d67e8 100644
--- a/chrome_elf/crash/crash_helper.h
+++ b/chrome_elf/crash/crash_helper.h
@@ -31,6 +31,9 @@
 // Generate a crash dump by calling into crashpad.
 void DumpWithoutCrashing();
 
+// Set the metrics client ID in crash keys.
+void SetMetricsClientIdImpl(const char* client_id);
+
 }  // namespace elf_crash
 
 #endif  // CHROME_ELF_CRASH_CRASH_HELPER_H_
diff --git a/chromecast/media/cdm/cast_cdm.cc b/chromecast/media/cdm/cast_cdm.cc
index 53036590b..627b0eea 100644
--- a/chromecast/media/cdm/cast_cdm.cc
+++ b/chromecast/media/cdm/cast_cdm.cc
@@ -12,8 +12,10 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chromecast/media/base/decrypt_context_impl.h"
+#include "chromecast/media/base/media_caps.h"
 #include "chromecast/media/base/media_resource_tracker.h"
 #include "media/base/cdm_key_information.h"
+#include "media/base/cdm_promise.h"
 #include "media/base/decryptor.h"
 #include "media/cdm/player_tracker_impl.h"
 #include "url/gurl.h"
@@ -62,6 +64,34 @@
   DISALLOW_COPY_AND_ASSIGN(CastCdmContextImpl);
 };
 
+// Returns the HDCP version multiplied by ten.
+int HdcpVersionX10(::media::HdcpVersion hdcp_version) {
+  switch (hdcp_version) {
+    case ::media::HdcpVersion::kHdcpVersionNone:
+      return 0;
+    case ::media::HdcpVersion::kHdcpVersion1_0:
+      return 10;
+    case ::media::HdcpVersion::kHdcpVersion1_1:
+      return 11;
+    case ::media::HdcpVersion::kHdcpVersion1_2:
+      return 12;
+    case ::media::HdcpVersion::kHdcpVersion1_3:
+      return 13;
+    case ::media::HdcpVersion::kHdcpVersion1_4:
+      return 14;
+    case ::media::HdcpVersion::kHdcpVersion2_0:
+      return 20;
+    case ::media::HdcpVersion::kHdcpVersion2_1:
+      return 21;
+    case ::media::HdcpVersion::kHdcpVersion2_2:
+      return 22;
+
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
+
 }  // namespace
 
 CastCdm::CastCdm(MediaResourceTracker* media_resource_tracker)
@@ -111,6 +141,17 @@
   return cast_cdm_context_.get();
 }
 
+void CastCdm::GetStatusForPolicy(
+    ::media::HdcpVersion min_hdcp_version,
+    std::unique_ptr<::media::KeyStatusCdmPromise> promise) {
+  int min_hdcp_x10 = HdcpVersionX10(min_hdcp_version);
+  int cur_hdcp_x10 = MediaCapabilities::GetHdcpVersion();
+  promise->resolve(
+      cur_hdcp_x10 >= min_hdcp_x10
+          ? ::media::CdmKeyInformation::KeyStatus::USABLE
+          : ::media::CdmKeyInformation::KeyStatus::OUTPUT_RESTRICTED);
+}
+
 void CastCdm::OnSessionMessage(const std::string& session_id,
                                const std::vector<uint8_t>& message,
                                ::media::CdmMessageType message_type) {
diff --git a/chromecast/media/cdm/cast_cdm.h b/chromecast/media/cdm/cast_cdm.h
index 321f33f..9f274cd0 100644
--- a/chromecast/media/cdm/cast_cdm.h
+++ b/chromecast/media/cdm/cast_cdm.h
@@ -72,6 +72,11 @@
   // ::media::ContentDecryptionModule implementation.
   ::media::CdmContext* GetCdmContext() override;
 
+  // Cast video products always provide HDCP or equivalent content protection.
+  void GetStatusForPolicy(
+      ::media::HdcpVersion min_hdcp_version,
+      std::unique_ptr<::media::KeyStatusCdmPromise> promise) final;
+
  protected:
   ~CastCdm() override;
 
diff --git a/components/download/content/factory/download_service_factory.cc b/components/download/content/factory/download_service_factory.cc
index 8ccce889..5fcaf6d 100644
--- a/components/download/content/factory/download_service_factory.cc
+++ b/components/download/content/factory/download_service_factory.cc
@@ -12,6 +12,7 @@
 #include "components/download/internal/download_service_impl.h"
 #include "components/download/internal/download_store.h"
 #include "components/download/internal/file_monitor_impl.h"
+#include "components/download/internal/logger_impl.h"
 #include "components/download/internal/model_impl.h"
 #include "components/download/internal/proto/entry.pb.h"
 #include "components/download/internal/scheduler/scheduler_impl.h"
@@ -52,12 +53,16 @@
       task_scheduler.get(), config.get(), client_set.get());
   auto file_monitor = base::MakeUnique<FileMonitorImpl>(
       files_storage_dir, background_task_runner, config->file_keep_alive_time);
+  auto logger = base::MakeUnique<LoggerImpl>();
   auto controller = base::MakeUnique<ControllerImpl>(
-      config.get(), std::move(client_set), std::move(driver), std::move(model),
-      std::move(device_status_listener), navigation_monitor,
+      config.get(), logger.get(), std::move(client_set), std::move(driver),
+      std::move(model), std::move(device_status_listener), navigation_monitor,
       std::move(scheduler), std::move(task_scheduler), std::move(file_monitor),
       files_storage_dir);
-  return new DownloadServiceImpl(std::move(config), std::move(controller));
+  logger->SetLogSource(controller.get());
+
+  return new DownloadServiceImpl(std::move(config), std::move(logger),
+                                 std::move(controller));
 }
 
 }  // namespace download
diff --git a/components/download/internal/BUILD.gn b/components/download/internal/BUILD.gn
index 5d51e76..22d2542 100644
--- a/components/download/internal/BUILD.gn
+++ b/components/download/internal/BUILD.gn
@@ -37,6 +37,10 @@
     "file_monitor.h",
     "file_monitor_impl.cc",
     "file_monitor_impl.h",
+    "log_sink.h",
+    "log_source.h",
+    "logger_impl.cc",
+    "logger_impl.h",
     "model.h",
     "model_impl.cc",
     "model_impl.h",
diff --git a/components/download/internal/controller_impl.cc b/components/download/internal/controller_impl.cc
index 38e1c0d0..0c43117 100644
--- a/components/download/internal/controller_impl.cc
+++ b/components/download/internal/controller_impl.cc
@@ -16,6 +16,7 @@
 #include "components/download/internal/entry.h"
 #include "components/download/internal/entry_utils.h"
 #include "components/download/internal/file_monitor.h"
+#include "components/download/internal/log_sink.h"
 #include "components/download/internal/model.h"
 #include "components/download/internal/scheduler/scheduler.h"
 #include "components/download/internal/stats.h"
@@ -75,6 +76,7 @@
 
 ControllerImpl::ControllerImpl(
     Configuration* config,
+    LogSink* log_sink,
     std::unique_ptr<ClientSet> clients,
     std::unique_ptr<DownloadDriver> driver,
     std::unique_ptr<Model> model,
@@ -85,6 +87,7 @@
     std::unique_ptr<FileMonitor> file_monitor,
     const base::FilePath& download_file_dir)
     : config_(config),
+      log_sink_(log_sink),
       download_file_dir_(download_file_dir),
       clients_(std::move(clients)),
       driver_(std::move(driver)),
@@ -95,7 +98,10 @@
       task_scheduler_(std::move(task_scheduler)),
       file_monitor_(std::move(file_monitor)),
       controller_state_(State::CREATED),
-      weak_ptr_factory_(this) {}
+      weak_ptr_factory_(this) {
+  DCHECK(config_);
+  DCHECK(log_sink_);
+}
 
 ControllerImpl::~ControllerImpl() = default;
 
@@ -522,6 +528,14 @@
   // TODO(dtrainor): If failed, clean up any download state accordingly.
 }
 
+Controller::State ControllerImpl::GetControllerState() {
+  return controller_state_;
+}
+
+const StartupStatus& ControllerImpl::GetStartupStatus() {
+  return startup_status_;
+}
+
 void ControllerImpl::OnDeviceStatusChanged(const DeviceStatus& device_status) {
   if (controller_state_ != State::READY)
     return;
@@ -534,6 +548,10 @@
   DCHECK(controller_state_ == State::INITIALIZING ||
          controller_state_ == State::RECOVERING);
 
+  // Always notify the LogSink no matter what path this function takes.
+  base::ScopedClosureRunner state_notifier(base::BindOnce(
+      &LogSink::OnServiceStatusChanged, base::Unretained(log_sink_)));
+
   if (!startup_status_.Complete())
     return;
 
diff --git a/components/download/internal/controller_impl.h b/components/download/internal/controller_impl.h
index 1d4c1d5..6483153 100644
--- a/components/download/internal/controller_impl.h
+++ b/components/download/internal/controller_impl.h
@@ -16,6 +16,7 @@
 #include "components/download/internal/controller.h"
 #include "components/download/internal/download_driver.h"
 #include "components/download/internal/entry.h"
+#include "components/download/internal/log_source.h"
 #include "components/download/internal/model.h"
 #include "components/download/internal/scheduler/device_status_listener.h"
 #include "components/download/internal/startup_status.h"
@@ -30,6 +31,7 @@
 class ClientSet;
 class DownloadDriver;
 class FileMonitor;
+class LogSink;
 class Model;
 class NavigationMonitor;
 class Scheduler;
@@ -44,10 +46,13 @@
                        public DownloadDriver::Client,
                        public Model::Client,
                        public DeviceStatusListener::Observer,
-                       public NavigationMonitor::Observer {
+                       public NavigationMonitor::Observer,
+                       public LogSource {
  public:
-  // |config| is externally owned and must be guaranteed to outlive this class.
+  // |config| and |log_sink| are externally owned and must be guaranteed to
+  // outlive this class.
   ControllerImpl(Configuration* config,
+                 LogSink* log_sink,
                  std::unique_ptr<ClientSet> clients,
                  std::unique_ptr<DownloadDriver> driver,
                  std::unique_ptr<Model> model,
@@ -97,6 +102,10 @@
                      DownloadClient client,
                      const std::string& guid) override;
 
+  // LogSource implementation.
+  Controller::State GetControllerState() override;
+  const StartupStatus& GetStartupStatus() override;
+
   // Called when the file monitor and download file directory are initialized.
   void OnFileMonitorReady(bool success);
 
@@ -213,6 +222,7 @@
   void KillTimedOutDownloads();
 
   Configuration* config_;
+  LogSink* log_sink_;
 
   // The directory in which the downloaded files are stored.
   const base::FilePath download_file_dir_;
diff --git a/components/download/internal/controller_impl_unittest.cc b/components/download/internal/controller_impl_unittest.cc
index a0a7978..ba0ba56 100644
--- a/components/download/internal/controller_impl_unittest.cc
+++ b/components/download/internal/controller_impl_unittest.cc
@@ -24,6 +24,7 @@
 #include "components/download/internal/navigation_monitor_impl.h"
 #include "components/download/internal/scheduler/scheduler.h"
 #include "components/download/internal/stats.h"
+#include "components/download/internal/test/black_hole_log_sink.h"
 #include "components/download/internal/test/entry_utils.h"
 #include "components/download/internal/test/test_device_status_listener.h"
 #include "components/download/internal/test/test_download_driver.h"
@@ -153,6 +154,8 @@
     config_->max_concurrent_downloads = 5;
     config_->max_running_downloads = 5;
 
+    log_sink_ = base::MakeUnique<test::BlackHoleLogSink>();
+
     client_ = client.get();
     driver_ = driver.get();
     store_ = store.get();
@@ -178,8 +181,8 @@
     file_monitor_ = file_monitor.get();
 
     controller_ = base::MakeUnique<ControllerImpl>(
-        config_.get(), std::move(client_set), std::move(driver),
-        std::move(model), std::move(device_status_listener),
+        config_.get(), log_sink_.get(), std::move(client_set),
+        std::move(driver), std::move(model), std::move(device_status_listener),
         &navigation_monitor, std::move(scheduler), std::move(task_scheduler),
         std::move(file_monitor), download_file_dir);
   }
@@ -213,6 +216,7 @@
 
   std::unique_ptr<ControllerImpl> controller_;
   std::unique_ptr<Configuration> config_;
+  std::unique_ptr<LogSink> log_sink_;
   NavigationMonitorImpl navigation_monitor;
   test::MockClient* client_;
   test::TestDownloadDriver* driver_;
diff --git a/components/download/internal/download_service_impl.cc b/components/download/internal/download_service_impl.cc
index c48b240..3f1d22e 100644
--- a/components/download/internal/download_service_impl.cc
+++ b/components/download/internal/download_service_impl.cc
@@ -7,14 +7,17 @@
 #include "base/bind.h"
 #include "base/strings/string_util.h"
 #include "components/download/internal/controller.h"
+#include "components/download/internal/logger_impl.h"
 #include "components/download/internal/startup_status.h"
 #include "components/download/internal/stats.h"
 
 namespace download {
 
 DownloadServiceImpl::DownloadServiceImpl(std::unique_ptr<Configuration> config,
+                                         std::unique_ptr<Logger> logger,
                                          std::unique_ptr<Controller> controller)
     : config_(std::move(config)),
+      logger_(std::move(logger)),
       controller_(std::move(controller)),
       service_config_(config_.get()),
       startup_completed_(false) {
@@ -133,6 +136,10 @@
   }
 }
 
+Logger* DownloadServiceImpl::GetLogger() {
+  return logger_.get();
+}
+
 void DownloadServiceImpl::OnControllerInitialized() {
   while (!pending_actions_.empty()) {
     auto callback = pending_actions_.front();
diff --git a/components/download/internal/download_service_impl.h b/components/download/internal/download_service_impl.h
index 808f2efb..913788f 100644
--- a/components/download/internal/download_service_impl.h
+++ b/components/download/internal/download_service_impl.h
@@ -18,6 +18,8 @@
 namespace download {
 
 class Controller;
+class Logger;
+
 struct DownloadParams;
 struct SchedulingParams;
 
@@ -25,6 +27,7 @@
 class DownloadServiceImpl : public DownloadService {
  public:
   DownloadServiceImpl(std::unique_ptr<Configuration> config,
+                      std::unique_ptr<Logger> logger,
                       std::unique_ptr<Controller> controller);
   ~DownloadServiceImpl() override;
 
@@ -40,6 +43,7 @@
   void CancelDownload(const std::string& guid) override;
   void ChangeDownloadCriteria(const std::string& guid,
                               const SchedulingParams& params) override;
+  Logger* GetLogger() override;
 
  private:
   void OnControllerInitialized();
@@ -48,6 +52,7 @@
   // hold onto references to it.
   std::unique_ptr<Configuration> config_;
 
+  std::unique_ptr<Logger> logger_;
   std::unique_ptr<Controller> controller_;
   ServiceConfigImpl service_config_;
 
diff --git a/components/download/internal/download_service_impl_unittest.cc b/components/download/internal/download_service_impl_unittest.cc
index ed6e9b66..17af31cd 100644
--- a/components/download/internal/download_service_impl_unittest.cc
+++ b/components/download/internal/download_service_impl_unittest.cc
@@ -16,6 +16,7 @@
 #include "components/download/internal/stats.h"
 #include "components/download/internal/test/download_params_utils.h"
 #include "components/download/internal/test/mock_controller.h"
+#include "components/download/public/test/empty_logger.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -35,10 +36,11 @@
 
   void SetUp() override {
     auto config = base::MakeUnique<Configuration>();
+    auto logger = base::MakeUnique<test::EmptyLogger>();
     auto controller = base::MakeUnique<test::MockController>();
     controller_ = controller.get();
-    service_ = base::MakeUnique<DownloadServiceImpl>(std::move(config),
-                                                     std::move(controller));
+    service_ = base::MakeUnique<DownloadServiceImpl>(
+        std::move(config), std::move(logger), std::move(controller));
   }
 
  protected:
diff --git a/components/download/internal/log_sink.h b/components/download/internal/log_sink.h
new file mode 100644
index 0000000..42c722b
--- /dev/null
+++ b/components/download/internal/log_sink.h
@@ -0,0 +1,24 @@
+// Copyright 2017 The Chromium 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_DOWNLOAD_INTERNAL_LOG_SINK_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_LOG_SINK_H_
+
+#include "components/download/internal/controller.h"
+#include "components/download/internal/startup_status.h"
+
+namespace download {
+
+// A destination for all interesting events from internal components.
+class LogSink {
+ public:
+  virtual ~LogSink() = default;
+
+  // To be called whenever the StartupStatus/Controller::State changes.
+  virtual void OnServiceStatusChanged() = 0;
+};
+
+}  // namespace download
+
+#endif  // COMPONENTS_DOWNLOAD_INTERNAL_LOG_SINK_H_
diff --git a/components/download/internal/log_source.h b/components/download/internal/log_source.h
new file mode 100644
index 0000000..d06989c0
--- /dev/null
+++ b/components/download/internal/log_source.h
@@ -0,0 +1,29 @@
+// Copyright 2017 The Chromium 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_DOWNLOAD_INTERNAL_LOG_SOURCE_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_LOG_SOURCE_H_
+
+#include "components/download/internal/controller.h"
+
+namespace download {
+
+struct StartupStatus;
+
+// A source for all relevant logging data.  LoggerImpl will pull from an
+// instance of LogSource to push relevant log information to observers.
+class LogSource {
+ public:
+  virtual ~LogSource() = default;
+
+  // Returns the state of the Controller (see Controller::State).
+  virtual Controller::State GetControllerState() = 0;
+
+  // Returns the current StartupStatus of the service.
+  virtual const StartupStatus& GetStartupStatus() = 0;
+};
+
+}  // namespace download
+
+#endif  // COMPONENTS_DOWNLOAD_INTERNAL_LOG_SOURCE_H_
diff --git a/components/download/internal/logger_impl.cc b/components/download/internal/logger_impl.cc
new file mode 100644
index 0000000..c3a06ed
--- /dev/null
+++ b/components/download/internal/logger_impl.cc
@@ -0,0 +1,84 @@
+// Copyright 2017 The Chromium 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/download/internal/logger_impl.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "components/download/internal/log_source.h"
+
+namespace download {
+namespace {
+
+std::string ControllerStateToString(Controller::State state) {
+  switch (state) {
+    case Controller::State::CREATED:
+      return "CREATED";
+    case Controller::State::INITIALIZING:
+      return "INITIALIZING";
+    case Controller::State::READY:
+      return "READY";
+    case Controller::State::RECOVERING:
+      return "RECOVERING";
+    case Controller::State::UNAVAILABLE:  // Intentional fallthrough.
+    default:
+      return "UNAVAILABLE";
+  }
+}
+
+std::string OptBoolToString(base::Optional<bool> value) {
+  if (value.has_value())
+    return value.value() ? "OK" : "BAD";
+
+  return "UNKNOWN";
+}
+
+}  // namespace
+
+LoggerImpl::LoggerImpl() : log_source_(nullptr) {}
+LoggerImpl::~LoggerImpl() = default;
+
+void LoggerImpl::SetLogSource(LogSource* log_source) {
+  log_source_ = log_source;
+}
+
+void LoggerImpl::AddObserver(Observer* observer) {
+  DCHECK(!observers_.HasObserver(observer));
+  observers_.AddObserver(observer);
+}
+
+void LoggerImpl::RemoveObserver(Observer* observer) {
+  DCHECK(observers_.HasObserver(observer));
+  observers_.RemoveObserver(observer);
+}
+
+base::Value LoggerImpl::GetServiceStatus() {
+  base::DictionaryValue service_status;
+
+  if (!log_source_)
+    return std::move(service_status);
+
+  Controller::State state = log_source_->GetControllerState();
+  const StartupStatus& status = log_source_->GetStartupStatus();
+
+  service_status.SetString("serviceState", ControllerStateToString(state));
+  service_status.SetString("modelStatus", OptBoolToString(status.model_ok));
+  service_status.SetString("driverStatus", OptBoolToString(status.driver_ok));
+  service_status.SetString("fileMonitorStatus",
+                           OptBoolToString(status.file_monitor_ok));
+
+  return std::move(service_status);
+}
+
+void LoggerImpl::OnServiceStatusChanged() {
+  if (!observers_.might_have_observers())
+    return;
+
+  base::Value service_status = GetServiceStatus();
+
+  for (auto& observer : observers_)
+    observer.OnServiceStatusChanged(service_status);
+}
+
+}  // namespace download
diff --git a/components/download/internal/logger_impl.h b/components/download/internal/logger_impl.h
new file mode 100644
index 0000000..11f24e7c
--- /dev/null
+++ b/components/download/internal/logger_impl.h
@@ -0,0 +1,50 @@
+// Copyright 2017 The Chromium 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_DOWNLOAD_INTERNAL_LOGGER_IMPL_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_LOGGER_IMPL_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/download/internal/log_sink.h"
+#include "components/download/public/logger.h"
+
+namespace base {
+class Value;
+}
+
+namespace download {
+
+class LogSource;
+
+// The internal Logger implementation.  Note that this Logger will not do any
+// actual work in response to LogSink requests if there are no Observers
+// registered.  Any calls to the Logger API will still be honored though.
+class LoggerImpl : public Logger, public LogSink {
+ public:
+  LoggerImpl();
+  ~LoggerImpl() override;
+
+  void SetLogSource(LogSource* log_source);
+
+ private:
+  // Logger implementation.
+  void AddObserver(Observer* observer) override;
+  void RemoveObserver(Observer* observer) override;
+  base::Value GetServiceStatus() override;
+
+  // LogSink implementation.
+  void OnServiceStatusChanged() override;
+
+  LogSource* log_source_;
+  base::ObserverList<Observer> observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(LoggerImpl);
+};
+
+}  // namespace download
+
+#endif  // COMPONENTS_DOWNLOAD_INTERNAL_LOGGER_IMPL_H_
diff --git a/components/download/internal/test/BUILD.gn b/components/download/internal/test/BUILD.gn
index e70cdbf0..3d3648a 100644
--- a/components/download/internal/test/BUILD.gn
+++ b/components/download/internal/test/BUILD.gn
@@ -9,12 +9,16 @@
   testonly = true
 
   sources = [
+    "black_hole_log_sink.cc",
+    "black_hole_log_sink.h",
     "download_params_utils.cc",
     "download_params_utils.h",
     "entry_utils.cc",
     "entry_utils.h",
     "mock_controller.cc",
     "mock_controller.h",
+    "mock_log_sink.cc",
+    "mock_log_sink.h",
     "mock_model_client.cc",
     "mock_model_client.h",
     "noop_store.cc",
diff --git a/components/download/internal/test/black_hole_log_sink.cc b/components/download/internal/test/black_hole_log_sink.cc
new file mode 100644
index 0000000..65b6274
--- /dev/null
+++ b/components/download/internal/test/black_hole_log_sink.cc
@@ -0,0 +1,13 @@
+// Copyright 2017 The Chromium 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/download/internal/test/black_hole_log_sink.h"
+
+namespace download {
+namespace test {
+
+void BlackHoleLogSink::OnServiceStatusChanged() {}
+
+}  // namespace test
+}  // namespace download
diff --git a/components/download/internal/test/black_hole_log_sink.h b/components/download/internal/test/black_hole_log_sink.h
new file mode 100644
index 0000000..94ea13c
--- /dev/null
+++ b/components/download/internal/test/black_hole_log_sink.h
@@ -0,0 +1,24 @@
+// Copyright 2017 The Chromium 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/download/internal/log_sink.h"
+
+namespace download {
+namespace test {
+
+// A LogSink that does nothing with the calls to the interface.
+class BlackHoleLogSink : public LogSink {
+ public:
+  BlackHoleLogSink() = default;
+  ~BlackHoleLogSink() override = default;
+
+  // LogSink implementation.
+  void OnServiceStatusChanged() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BlackHoleLogSink);
+};
+
+}  // namespace test
+}  // namespace download
diff --git a/components/download/internal/test/mock_log_sink.cc b/components/download/internal/test/mock_log_sink.cc
new file mode 100644
index 0000000..da5b77cf
--- /dev/null
+++ b/components/download/internal/test/mock_log_sink.cc
@@ -0,0 +1,14 @@
+// Copyright 2017 The Chromium 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/download/internal/test/mock_log_sink.h"
+
+namespace download {
+namespace test {
+
+MockLogSink::MockLogSink() = default;
+MockLogSink::~MockLogSink() = default;
+
+}  // namespace test
+}  // namespace download
diff --git a/components/download/internal/test/mock_log_sink.h b/components/download/internal/test/mock_log_sink.h
new file mode 100644
index 0000000..9f3cf098
--- /dev/null
+++ b/components/download/internal/test/mock_log_sink.h
@@ -0,0 +1,22 @@
+// Copyright 2017 The Chromium 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/download/internal/log_sink.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace download {
+namespace test {
+
+class MockLogSink : public LogSink {
+ public:
+  MockLogSink();
+  ~MockLogSink() override;
+
+  // LogSink implementation.
+  MOCK_METHOD0(OnServiceStatusChanged, void());
+};
+
+}  // namespace test
+}  // namespace download
diff --git a/components/download/public/BUILD.gn b/components/download/public/BUILD.gn
index d252bc1f..3543a1a 100644
--- a/components/download/public/BUILD.gn
+++ b/components/download/public/BUILD.gn
@@ -19,6 +19,7 @@
     "download_task_types.h",
     "features.cc",
     "features.h",
+    "logger.h",
     "navigation_monitor.h",
     "service_config.h",
     "task_scheduler.h",
diff --git a/components/download/public/download_service.h b/components/download/public/download_service.h
index 8954b75f..2de7add 100644
--- a/components/download/public/download_service.h
+++ b/components/download/public/download_service.h
@@ -20,9 +20,11 @@
 namespace download {
 
 class Client;
+class Logger;
+class ServiceConfig;
+
 struct DownloadParams;
 struct SchedulingParams;
-class ServiceConfig;
 
 using TaskFinishedCallback = base::Callback<void(bool)>;
 
@@ -94,6 +96,10 @@
   virtual void ChangeDownloadCriteria(const std::string& guid,
                                       const SchedulingParams& params) = 0;
 
+  // Returns a Logger instance that is meant to be used by logging and debug UI
+  // components in the larger system.
+  virtual Logger* GetLogger() = 0;
+
  protected:
   DownloadService() = default;
 
diff --git a/components/download/public/logger.h b/components/download/public/logger.h
new file mode 100644
index 0000000..321c3c8
--- /dev/null
+++ b/components/download/public/logger.h
@@ -0,0 +1,56 @@
+// Copyright 2017 The Chromium 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_DOWNLOAD_PUBLIC_LOGGER_H_
+#define COMPONENTS_DOWNLOAD_PUBLIC_LOGGER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+
+namespace base {
+class Value;
+}
+
+namespace download {
+
+// A helper class to expose internals of the downloads system to a logging
+// component and/or debug UI.
+class Logger {
+ public:
+  // An Observer to be notified of any DownloadService changes.
+  class Observer {
+   public:
+    virtual ~Observer() = default;
+
+    // Called whenever the status of the DownloadService changes.  This will
+    // have the same data as |GetServiceStatus()|.
+    virtual void OnServiceStatusChanged(const base::Value& service_status) = 0;
+  };
+
+  virtual ~Logger() = default;
+
+  virtual void AddObserver(Observer* observer) = 0;
+  virtual void RemoveObserver(Observer* observer) = 0;
+
+  // Returns the current status of the Download Service.  The serialized format
+  // will be:
+  // {
+  //   serviceState: string [CREATED,INITIALIZING,READY,RECOVERING,UNAVAILABLE],
+  //   modelStatus: string [OK,BAD,UNKNOWN],
+  //   driverStatus: string [OK,BAD,UNKNOWN],
+  //   fileMonitorStatus: string [OK,BAD,UNKNOWN]
+  // }
+  virtual base::Value GetServiceStatus() = 0;
+
+ protected:
+  Logger() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Logger);
+};
+
+}  // namespace download
+
+#endif  // COMPONENTS_DOWNLOAD_PUBLIC_LOGGER_H_
diff --git a/components/download/public/test/BUILD.gn b/components/download/public/test/BUILD.gn
index d73e9207..9aa28f4 100644
--- a/components/download/public/test/BUILD.gn
+++ b/components/download/public/test/BUILD.gn
@@ -13,6 +13,8 @@
   sources = [
     "empty_client.cc",
     "empty_client.h",
+    "empty_logger.cc",
+    "empty_logger.h",
     "mock_client.cc",
     "mock_client.h",
     "mock_download_service.cc",
diff --git a/components/download/public/test/empty_logger.cc b/components/download/public/test/empty_logger.cc
new file mode 100644
index 0000000..dcc0e26
--- /dev/null
+++ b/components/download/public/test/empty_logger.cc
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium 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/download/public/test/empty_logger.h"
+
+#include "base/values.h"
+
+namespace download {
+namespace test {
+
+void EmptyLogger::AddObserver(Observer* observer) {}
+
+void EmptyLogger::RemoveObserver(Observer* observer) {}
+
+base::Value EmptyLogger::GetServiceStatus() {
+  return base::Value();
+}
+
+}  // namespace test
+}  // namespace download
diff --git a/components/download/public/test/empty_logger.h b/components/download/public/test/empty_logger.h
new file mode 100644
index 0000000..0ed86ad
--- /dev/null
+++ b/components/download/public/test/empty_logger.h
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium 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_DOWNLOAD_PUBLIC_TEST_EMPTY_LOGGER_H_
+#define COMPONENTS_DOWNLOAD_PUBLIC_TEST_EMPTY_LOGGER_H_
+
+#include "components/download/public/logger.h"
+
+namespace download {
+namespace test {
+
+// A Logger that does nothing.
+class EmptyLogger : public Logger {
+ public:
+  EmptyLogger() = default;
+  ~EmptyLogger() override = default;
+
+  // Logger implementation.
+  void AddObserver(Observer* observer) override;
+  void RemoveObserver(Observer* observer) override;
+  base::Value GetServiceStatus() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(EmptyLogger);
+};
+
+}  // namespace test
+}  // namespace download
+
+#endif  // COMPONENTS_DOWNLOAD_PUBLIC_TEST_EMPTY_LOGGER_H_
diff --git a/components/download/public/test/test_download_service.cc b/components/download/public/test/test_download_service.cc
index 8384444..2529625 100644
--- a/components/download/public/test/test_download_service.cc
+++ b/components/download/public/test/test_download_service.cc
@@ -11,6 +11,7 @@
 #include "components/download/public/download_params.h"
 #include "components/download/public/download_service.h"
 #include "components/download/public/service_config.h"
+#include "components/download/public/test/empty_logger.h"
 
 namespace download {
 namespace test {
@@ -23,6 +24,7 @@
   TestServiceConfig() = default;
   ~TestServiceConfig() override = default;
 
+  // ServiceConfig implementation.
   uint32_t GetMaxScheduledDownloadsPerClient() const override { return 0; }
   const base::TimeDelta& GetFileKeepAliveTime() const override {
     return time_delta_;
@@ -30,14 +32,17 @@
 
  private:
   base::TimeDelta time_delta_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestServiceConfig);
 };
 
 }  // namespace
 
 TestDownloadService::TestDownloadService()
-    : is_ready_(false),
+    : service_config_(base::MakeUnique<TestServiceConfig>()),
+      logger_(base::MakeUnique<EmptyLogger>()),
+      is_ready_(false),
       fail_at_start_(false),
-      service_config_(base::MakeUnique<TestServiceConfig>()),
       file_size_(123456789u),
       client_(nullptr) {}
 
@@ -96,6 +101,10 @@
     const std::string& guid,
     const SchedulingParams& params) {}
 
+Logger* TestDownloadService::GetLogger() {
+  return logger_.get();
+}
+
 base::Optional<DownloadParams> TestDownloadService::GetDownload(
     const std::string& guid) const {
   for (const auto& download : downloads_) {
diff --git a/components/download/public/test/test_download_service.h b/components/download/public/test/test_download_service.h
index f00b3b7..2143c2d 100644
--- a/components/download/public/test/test_download_service.h
+++ b/components/download/public/test/test_download_service.h
@@ -36,6 +36,7 @@
   void CancelDownload(const std::string& guid) override;
   void ChangeDownloadCriteria(const std::string& guid,
                               const SchedulingParams& params) override;
+  Logger* GetLogger() override;
 
   base::Optional<DownloadParams> GetDownload(const std::string& guid) const;
 
@@ -59,10 +60,12 @@
   void OnDownloadFailed(const std::string& guid,
                         Client::FailureReason failure_reason);
 
+  std::unique_ptr<ServiceConfig> service_config_;
+  std::unique_ptr<Logger> logger_;
+
   bool is_ready_;
   std::string failed_download_id_;
   bool fail_at_start_;
-  std::unique_ptr<ServiceConfig> service_config_;
   uint64_t file_size_;
 
   Client* client_;
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index c1e6ed58..33b1a0ec 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -158,9 +158,20 @@
 #   An additional flag 'device_only' (optional, defaults to False) indicates
 #   that this policy is only supported as a device-level Cloud Policy. In that
 #   case no entry in the UserPolicy Protobuf is generated and it is assumed that
-#   it will be added to the DevicePolicy Protobuf manually.  Device policy only
+#   it will be added to the DevicePolicy Protobuf manually. Device policy only
 #   exists on Chrome OS.
 #
+# Management Type:
+#   Chrome OS devices can either be managed through the Google cloud or through
+#   Active Directory. Most policies are supported for both management types, but
+#   some are not. To indicate supported management types, use
+#     'supported_chrome_os_management': ['google_cloud', 'active_directory'],
+#   where
+#     'google_cloud' = Policy is supported for Google cloud management.
+#     'active_directory' = Policy is supported for Active Directory management.
+#   This setting applies to Chrome OS only. If the setting is missing, both
+#   types are assumed. The array must not be empty.
+#
 # Enterprise defaults:
 #   For managed users on Chrome OS (i.e. users receiving user policy from the
 #   cloud), if the optional key 'default_for_enterprise_users' is set, its value
@@ -2143,8 +2154,7 @@
           'type': 'string',
           'schema': { 'type': 'string' },
           'supported_on': ['chrome.*:9-','android:46-','chrome_os:62-'],
-          # TODO(ljusten): Uncomment, once that property is in:
-          # 'supported_cros_management': ['ad'],
+          'supported_chrome_os_management': ['active_directory'],
           'features': {
             'dynamic_refresh': False,
             'per_profile': False,
@@ -2164,8 +2174,7 @@
           'type': 'main',
           'schema': { 'type': 'boolean' },
           'supported_on': ['chrome.*:9-','android:46-','chrome_os:62-'],
-          # TODO(ljusten): Uncomment, once that property is in:
-          # 'supported_cros_management': ['ad'],
+          'supported_chrome_os_management': ['active_directory'],
           'features': {
             'dynamic_refresh': False,
             'per_profile': False,
@@ -2185,8 +2194,7 @@
           'type': 'main',
           'schema': { 'type': 'boolean' },
           'supported_on': ['chrome.*:9-','chrome_os:62-'],
-          # TODO(ljusten): Uncomment, once that property is in:
-          # 'supported_cros_management': ['ad'],
+          'supported_chrome_os_management': ['active_directory'],
           'features': {
             'dynamic_refresh': False,
             'per_profile': False,
@@ -2206,8 +2214,7 @@
           'type': 'string',
           'schema': { 'type': 'string' },
           'supported_on': ['chrome.*:9-','android:46-','webview_android:49-','chrome_os:62-'],
-          # TODO(ljusten): Uncomment, once that property is in:
-          # 'supported_cros_management': ['ad'],
+          'supported_chrome_os_management': ['active_directory'],
           'features': {
             'dynamic_refresh': False,
             'per_profile': False,
@@ -2227,8 +2234,7 @@
           'type': 'string',
           'schema': { 'type': 'string' },
           'supported_on': ['chrome.*:9-','android:46-','chrome_os:62-'],
-          # TODO(ljusten): Uncomment, once that property is in:
-          # 'supported_cros_management': ['ad'],
+          'supported_chrome_os_management': ['active_directory'],
           'features': {
             'dynamic_refresh': False,
             'per_profile': False,
@@ -2282,8 +2288,7 @@
           'type': 'main',
           'schema': { 'type': 'boolean' },
           'supported_on': ['chrome.*:13-','chrome_os:62-'],
-          # TODO(ljusten): Uncomment, once that property is in:
-          # 'supported_cros_management': ['ad'],
+          'supported_chrome_os_management': ['active_directory'],
           'features': {
             'dynamic_refresh': True,
             'per_profile': False,
@@ -5032,6 +5037,7 @@
       'type': 'main',
       'schema': { 'type': 'boolean' },
       'supported_on': ['chrome_os:18-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -5050,6 +5056,7 @@
       'type': 'main',
       'schema': { 'type': 'boolean' },
       'supported_on': ['chrome_os:18-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -5068,6 +5075,7 @@
       'type': 'main',
       'schema': { 'type': 'boolean' },
       'supported_on': ['chrome_os:18-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -5086,6 +5094,7 @@
       'type': 'main',
       'schema': { 'type': 'boolean' },
       'supported_on': ['chrome_os:20-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -5105,6 +5114,7 @@
       'type': 'main',
       'schema': { 'type': 'boolean' },
       'supported_on': ['chrome_os:29-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -5123,6 +5133,7 @@
       'type': 'main',
       'schema': { 'type': 'boolean' },
       'supported_on': ['chrome_os:32-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -5141,6 +5152,7 @@
       'type': 'main',
       'schema': { 'type': 'boolean' },
       'supported_on': ['chrome_os:42-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -5160,6 +5172,7 @@
       'type': 'main',
       'schema': { 'type': 'boolean' },
       'supported_on': ['chrome_os:42-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -5181,6 +5194,7 @@
       'type': 'int',
       'schema': { 'type': 'integer', 'minimum': 60000 },
       'supported_on': ['chrome_os:42-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -5200,6 +5214,7 @@
       'type': 'main',
       'schema': { 'type': 'boolean' },
       'supported_on': ['chrome_os:55-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'features': {
         'dynamic_refresh': True,
         'per_profile': True,
@@ -5221,6 +5236,7 @@
       'type': 'main',
       'schema': { 'type': 'boolean' },
       'supported_on': ['chrome_os:43-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -5241,6 +5257,7 @@
       'type': 'int',
       'schema': { 'type': 'integer', 'minimum': 30000 },
       'supported_on': ['chrome_os:43-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -5261,6 +5278,7 @@
       'type': 'main',
       'schema': { 'type': 'boolean' },
       'supported_on': ['chrome_os:46-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -5685,6 +5703,7 @@
         'items': { 'type': 'string' },
       },
       'supported_on': ['chrome_os:25-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -5702,6 +5721,7 @@
       'type': 'string',
       'schema': { 'type': 'string' },
       'supported_on': ['chrome_os:26-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -5721,6 +5741,7 @@
       'type': 'int',
       'schema': { 'type': 'integer' },
       'supported_on': ['chrome_os:26-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -5744,6 +5765,7 @@
       'type': 'main',
       'schema': { 'type': 'boolean' },
       'supported_on': ['chrome_os:28-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -5763,6 +5785,7 @@
       'type': 'main',
       'schema': { 'type': 'boolean' },
       'supported_on': ['chrome_os:33-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -7528,6 +7551,7 @@
       'type': 'int',
       'schema': { 'type': 'integer' },
       'supported_on': ['chrome_os:29-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -7624,6 +7648,7 @@
           'type': 'main',
           'schema': { 'type': 'boolean' },
           'supported_on': ['chrome_os:28-'],
+          'supported_chrome_os_management': ['google_cloud'],
           'features': {
             'dynamic_refresh': True,
           },
@@ -7678,6 +7703,7 @@
           'type': 'main',
           'schema': { 'type': 'boolean' },
           'supported_on': ['chrome_os:31-'],
+          'supported_chrome_os_management': ['google_cloud'],
           'features': {
             'dynamic_refresh': True,
           },
@@ -7810,6 +7836,7 @@
           'type': 'main',
           'schema': { 'type': 'boolean' },
           'supported_on': ['chrome_os:29-'],
+          'supported_chrome_os_management': ['google_cloud'],
           'device_only': True,
           'features': {
             'dynamic_refresh': False,
@@ -8077,6 +8104,7 @@
       'type': 'main',
       'schema': { 'type': 'boolean' },
       'supported_on': ['chrome_os:38-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -8465,6 +8493,7 @@
       'type': 'int',
       'schema': { 'type': 'integer' },
       'supported_on': ['chrome_os:43-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': False,
@@ -8482,6 +8511,7 @@
       'type': 'string',
       'schema': { 'type': 'string' },
       'supported_on': [ 'chrome_os:44-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -8776,6 +8806,7 @@
       'type': 'main',
       'schema': { 'type': 'boolean' },
       'supported_on': ['chrome_os:51-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -8815,6 +8846,7 @@
         },
       ],
       'supported_on': ['chrome_os:51-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -9029,6 +9061,7 @@
         'required': ['enabled', 'upload_rate_kbits', 'download_rate_kbits']
       },
       'supported_on': ['chrome_os:56-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'features': {
         'dynamic_refresh': True,
         'per_profile': False,
@@ -9045,6 +9078,7 @@
         'items': { 'type': 'string' },
       },
       'supported_on': ['chrome_os:52-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
@@ -10030,6 +10064,7 @@
         },
       ],
       'supported_on': ['chrome_os:61-'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': False,
@@ -10093,6 +10128,7 @@
         },
       ],
       'supported_on': ['chrome_os:60-60'],
+      'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
diff --git a/components/policy/tools/generate_policy_source.py b/components/policy/tools/generate_policy_source.py
index 040ace4..606a04a1 100755
--- a/components/policy/tools/generate_policy_source.py
+++ b/components/policy/tools/generate_policy_source.py
@@ -75,6 +75,9 @@
     self.is_deprecated = policy.get('deprecated', False)
     self.is_device_only = policy.get('device_only', False)
     self.is_future = policy.get('future', False)
+    self.supported_chrome_os_management = \
+        policy.get('supported_chrome_os_management',
+                   ['active_directory', 'google_cloud'])
     self.schema = policy.get('schema', {})
     self.has_enterprise_default = 'default_for_enterprise_users' in policy
     if self.has_enterprise_default:
@@ -957,6 +960,8 @@
 
 #------------------ policy protobufs -------------------------------#
 
+# This code applies to both Active Directory and Google cloud management.
+
 CHROME_SETTINGS_PROTO_HEAD = '''
 syntax = "proto2";
 
@@ -1110,6 +1115,8 @@
 
 #------------------ protobuf decoder -------------------------------#
 
+# This code applies to both Active Directory and Google cloud management.
+
 CLOUD_POLICY_DECODER_CPP_HEAD = '''
 #include <limits>
 #include <memory>
@@ -1256,17 +1263,31 @@
 
 #------------------ Chrome OS policy constants header --------------#
 
-# Returns a list of supported user policies by filtering |policies|.
-def _GetSupportedUserPolicies(policies):
-  return filter(lambda policy: policy.is_supported and
-                               not policy.is_device_only, policies)
+# This code applies to Active Directory management only.
 
+# Filter for _GetSupportedChromeOSPolicies().
+def _IsSupportedChromeOSPolicy(type, policy):
+  # Filter out unsupported policies.
+  if not policy.is_supported:
+    return False
+  # Filter out device policies if user policies are requested.
+  if type == 'user' and policy.is_device_only:
+    return False
+  # Filter out user policies if device policies are requested.
+  if type == 'device' and not policy.is_device_only:
+    return False
+  # Filter out non-Active-Directory policies.
+  if 'active_directory' not in policy.supported_chrome_os_management:
+    return False
+  return True
 
-# Returns a list of supported device policies by filtering |policies|.
-def _GetSupportedDevicePolicies(policies):
-  return filter(lambda policy: policy.is_supported and
-                               policy.is_device_only, policies)
+# Returns a list of supported user and/or device policies `by filtering
+# |policies|. |type| may be 'user', 'device' or 'both'.
+def _GetSupportedChromeOSPolicies(policies, type):
+  if (type not in ['user', 'device', 'both']):
+    raise RuntimeError('Unsupported type "%s"' % type)
 
+  return filter(partial(_IsSupportedChromeOSPolicy, type), policies)
 
 # Returns the set of all policy.policy_protobuf_type strings from |policies|.
 def _GetProtobufTypes(policies):
@@ -1294,7 +1315,7 @@
           '#define __BINDINGS_POLICY_CONSTANTS_H_\n\n')
 
   # Forward declarations.
-  supported_user_policies = _GetSupportedUserPolicies(policies)
+  supported_user_policies = _GetSupportedChromeOSPolicies(policies, 'user')
   protobuf_types = _GetProtobufTypes(supported_user_policies)
   f.write('namespace enterprise_management {\n'
           'class CloudPolicySettings;\n')
@@ -1305,15 +1326,16 @@
   f.write('namespace policy {\n\n')
 
   # Policy keys.
+  all_supported_policies = _GetSupportedChromeOSPolicies(policies, 'both')
   f.write('// Registry key names for user and device policies.\n'
           'namespace key {\n\n')
-  for policy in policies:
+  for policy in all_supported_policies:
     f.write('extern const char k' + policy.name + '[];\n')
   f.write('\n}  // namespace key\n\n')
 
   # Device policy keys.
   f.write('// NULL-terminated list of device policy registry key names.\n')
-  f.write('extern const char* kDevicePolicyKeys[];\n\n');
+  f.write('extern const char* kDevicePolicyKeys[];\n\n')
 
   # User policy proto pointers, one struct for each protobuf type.
   for protobuf_type in protobuf_types:
@@ -1348,20 +1370,21 @@
           'namespace policy {\n\n')
 
   # Policy keys.
+  all_supported_policies = _GetSupportedChromeOSPolicies(policies, 'both')
   f.write('namespace key {\n\n')
-  for policy in policies:
+  for policy in all_supported_policies:
     f.write('const char k{name}[] = "{name}";\n'.format(name=policy.name))
   f.write('\n}  // namespace key\n\n')
 
   # Device policy keys.
-  supported_device_policies = _GetSupportedDevicePolicies(policies)
+  supported_device_policies = _GetSupportedChromeOSPolicies(policies, 'device')
   f.write('const char* kDevicePolicyKeys[] = {\n\n');
   for policy in supported_device_policies:
     f.write('  key::k%s,\n' % policy.name)
   f.write('  nullptr};\n\n');
 
   # User policy proto pointers, one struct for each protobuf type.
-  supported_user_policies = _GetSupportedUserPolicies(policies)
+  supported_user_policies = _GetSupportedChromeOSPolicies(policies, 'user')
   protobuf_types = _GetProtobufTypes(supported_user_policies)
   for protobuf_type in protobuf_types:
     _WriteChromeOSPolicyAccessSource(supported_user_policies, f, protobuf_type)
diff --git a/components/policy/tools/syntax_check_policy_template_json.py b/components/policy/tools/syntax_check_policy_template_json.py
index ec811af..bcd528f 100755
--- a/components/policy/tools/syntax_check_policy_template_json.py
+++ b/components/policy/tools/syntax_check_policy_template_json.py
@@ -185,7 +185,8 @@
                      'supported_on', 'label', 'policies', 'items',
                      'example_value', 'features', 'deprecated', 'future',
                      'id', 'schema', 'max_size', 'tags',
-                     'default_for_enterprise_users', 'arc_support'):
+                     'default_for_enterprise_users', 'arc_support',
+                     'supported_chrome_os_management'):
         self.warning_count += 1
         print ('In policy %s: Warning: Unknown key: %s' %
                (policy.get('name'), key))
@@ -292,6 +293,27 @@
                           container_name='features',
                           identifier=policy.get('name'))
 
+      # Chrome OS policies may have a non-empty supported_chrome_os_management
+      # list with either 'active_directory' or 'google_cloud' or both.
+      supported_chrome_os_management = self._CheckContains(
+          policy, 'supported_chrome_os_management', list, True)
+      if supported_chrome_os_management is not None:
+        # Must be on Chrome OS.
+        if (supported_on is not None and
+            not any('chrome_os:' in str for str in supported_on)):
+          self._Error('"supported_chrome_os_management" is only supported on '
+                      'Chrome OS', 'policy', policy, supported_on)
+        # Must be non-empty.
+        if len(supported_chrome_os_management) == 0:
+          self._Error('"supported_chrome_os_management" must be non-empty',
+                      'policy', policy)
+        # Must be either 'active_directory' or 'google_cloud'.
+        if (any(str != 'google_cloud' and str != 'active_directory'
+                for str in supported_chrome_os_management)):
+          self._Error('Values in "supported_chrome_os_management" must be '
+                      'either "active_directory" or "google_cloud"', 'policy',
+                      policy, supported_chrome_os_management)
+
       # Each policy must have an 'example_value' of appropriate type.
       if policy_type == 'main':
         value_type = item_type = bool
diff --git a/components/policy/tools/template_writers/writers/adm_writer_unittest.py b/components/policy/tools/template_writers/writers/adm_writer_unittest.py
index a8911c75..f488c5d 100755
--- a/components/policy/tools/template_writers/writers/adm_writer_unittest.py
+++ b/components/policy/tools/template_writers/writers/adm_writer_unittest.py
@@ -31,7 +31,7 @@
     '''Compares the output of the adm_writer with its expected output.
 
     Args:
-      output: The output of the adm writer as returned by grit.
+      output: The output of the adm writer.
       expected_output: The expected output.
 
     Raises:
diff --git a/components/policy/tools/template_writers/writers/chromeos_adml_writer.py b/components/policy/tools/template_writers/writers/chromeos_adml_writer.py
index 32c2073c..de54eb9 100755
--- a/components/policy/tools/template_writers/writers/chromeos_adml_writer.py
+++ b/components/policy/tools/template_writers/writers/chromeos_adml_writer.py
@@ -13,4 +13,16 @@
   '''Factory method for creating ADMLWriter objects for the Chrome OS platform.
   See the constructor of TemplateWriter for description of arguments.
   '''
-  return adml_writer.ADMLWriter(['chrome_os'], config)
+  return ChromeOSADMLWriter(['chrome_os'], config)
+
+class ChromeOSADMLWriter(adml_writer.ADMLWriter):
+  ''' Class for generating Chrome OS ADML policy templates. It is used by the
+  PolicyTemplateGenerator to write the ADML file.
+  '''
+
+  # Overridden.
+  # These ADML files are used to generate GPO for Active Directory managed
+  # Chrome OS devices.
+  def IsPolicySupported(self, policy):
+    return self.IsCrOSManagementSupported(policy, 'active_directory') and \
+           super(ChromeOSADMLWriter, self).IsPolicySupported(policy)
diff --git a/components/policy/tools/template_writers/writers/chromeos_adml_writer_unittest.py b/components/policy/tools/template_writers/writers/chromeos_adml_writer_unittest.py
index 38f7114..e6c8819 100755
--- a/components/policy/tools/template_writers/writers/chromeos_adml_writer_unittest.py
+++ b/components/policy/tools/template_writers/writers/chromeos_adml_writer_unittest.py
@@ -47,6 +47,25 @@
       ]
     }))
 
+  def testOnlySupportsAdPolicies(self):
+    # Tests whether only Active Directory managed policies are supported (Google
+    # cloud only managed polices are not put in the ADMX file).
+    policy = {
+      'name': 'PolicyName',
+      'supported_on': [{
+        'product': 'chrome_os',
+        'platforms': ['chrome_os'],
+        'since_version': '8',
+        'until_version': '',
+      }],
+    }
+    self.assertTrue(self.writer.IsPolicySupported(policy))
+
+    policy['supported_chrome_os_management'] = ['google_cloud']
+    self.assertFalse(self.writer.IsPolicySupported(policy))
+
+    policy['supported_chrome_os_management'] = ['active_directory']
+    self.assertTrue(self.writer.IsPolicySupported(policy))
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/components/policy/tools/template_writers/writers/chromeos_admx_writer.py b/components/policy/tools/template_writers/writers/chromeos_admx_writer.py
index 8689f95..a0baa59 100755
--- a/components/policy/tools/template_writers/writers/chromeos_admx_writer.py
+++ b/components/policy/tools/template_writers/writers/chromeos_admx_writer.py
@@ -25,3 +25,10 @@
   def GetClass(self, policy):
     is_device_only = 'device_only' in policy and policy['device_only']
     return 'Machine' if is_device_only else 'User'
+
+  # Overridden.
+  # These ADMX templates are used to generate GPO for Active Directory managed
+  # Chrome OS devices.
+  def IsPolicySupported(self, policy):
+    return self.IsCrOSManagementSupported(policy, 'active_directory') and \
+           super(ChromeOSADMXWriter, self).IsPolicySupported(policy)
diff --git a/components/policy/tools/template_writers/writers/chromeos_admx_writer_unittest.py b/components/policy/tools/template_writers/writers/chromeos_admx_writer_unittest.py
index 2373ecac..6437502e 100755
--- a/components/policy/tools/template_writers/writers/chromeos_admx_writer_unittest.py
+++ b/components/policy/tools/template_writers/writers/chromeos_admx_writer_unittest.py
@@ -56,10 +56,10 @@
     }))
 
   def testUserPolicy(self):
-    self.doTestUserOrDevicePolicy(False);
+    self.doTestUserOrDevicePolicy(False)
 
   def testDevicePolicy(self):
-    self.doTestUserOrDevicePolicy(True);
+    self.doTestUserOrDevicePolicy(True)
 
   def doTestUserOrDevicePolicy(self, is_device_only):
     # Tests whether CLASS attribute is 'User' for user policies and 'Machine'
@@ -96,6 +96,25 @@
 
     self.AssertXMLEquals(output, expected_output)
 
+  def testOnlySupportsAdPolicies(self):
+    # Tests whether only Active Directory managed policies are supported (Google
+    # cloud only managed polices are not put in the ADMX file).
+    policy = {
+      'name': 'PolicyName',
+      'supported_on': [{
+        'product': 'chrome_os',
+        'platforms': ['chrome_os'],
+        'since_version': '8',
+        'until_version': '',
+      }],
+    }
+    self.assertTrue(self.writer.IsPolicySupported(policy))
+
+    policy['supported_chrome_os_management'] = ['google_cloud']
+    self.assertFalse(self.writer.IsPolicySupported(policy))
+
+    policy['supported_chrome_os_management'] = ['active_directory']
+    self.assertTrue(self.writer.IsPolicySupported(policy))
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/components/policy/tools/template_writers/writers/doc_writer.py b/components/policy/tools/template_writers/writers/doc_writer.py
index 1c1ff42..c9fc413 100755
--- a/components/policy/tools/template_writers/writers/doc_writer.py
+++ b/components/policy/tools/template_writers/writers/doc_writer.py
@@ -212,10 +212,10 @@
   def _GetRegistryKeyName(self, policy, is_win):
     use_recommended_key = self.CanBeRecommended(policy) and not \
                           self.CanBeMandatory(policy)
-    platform = 'win' if is_win else 'chrome_os';
+    platform = 'win' if is_win else 'chrome_os'
     key = 'reg_recommended_key_name' if use_recommended_key else \
           'reg_mandatory_key_name'
-    return self.config['win_config'][platform][key];
+    return self.config['win_config'][platform][key]
 
   def _AddListExampleAndroidLinux(self, parent, policy):
     '''Adds an example value for Android/Linux of a 'list' policy to a DOM node.
@@ -264,7 +264,8 @@
     examples = self._AddStyledElement(parent, 'dl', ['dd dl'])
     if self.IsPolicySupportedOnPlatform(policy, 'win'):
       self._AddListExampleWindowsChromeOS(examples, policy, True)
-    if self.IsPolicySupportedOnPlatform(policy, 'chrome_os'):
+    if self.IsPolicySupportedOnPlatform(policy, 'chrome_os',
+                                        management='active_directory'):
       self._AddListExampleWindowsChromeOS(examples, policy, False)
     if (self.IsPolicySupportedOnPlatform(policy, 'android') or
         self.IsPolicySupportedOnPlatform(policy, 'linux')):
@@ -381,7 +382,8 @@
     examples = self._AddStyledElement(parent, 'dl', ['dd dl'])
     if self.IsPolicySupportedOnPlatform(policy, 'win'):
       self._AddDictionaryExampleWindowsChromeOS(examples, policy, True)
-    if self.IsPolicySupportedOnPlatform(policy, 'chrome_os'):
+    if self.IsPolicySupportedOnPlatform(policy, 'chrome_os',
+                                        management='active_directory'):
       self._AddDictionaryExampleWindowsChromeOS(examples, policy, False)
     if (self.IsPolicySupportedOnPlatform(policy, 'android') or
         self.IsPolicySupportedOnPlatform(policy, 'linux')):
@@ -409,7 +411,8 @@
     if policy_type == 'main':
       pieces = []
       if self.IsPolicySupportedOnPlatform(policy, 'win') or \
-         self.IsPolicySupportedOnPlatform(policy, 'chrome_os'):
+         self.IsPolicySupportedOnPlatform(policy, 'chrome_os',
+                                          management='active_directory'):
         value = '0x00000001' if example_value else '0x00000000'
         pieces.append(value + ' (Windows)')
       if self.IsPolicySupportedOnPlatform(policy, 'linux'):
@@ -427,7 +430,8 @@
     elif policy_type in ('int', 'int-enum'):
       pieces = []
       if self.IsPolicySupportedOnPlatform(policy, 'win') or \
-         self.IsPolicySupportedOnPlatform(policy, 'chrome_os'):
+         self.IsPolicySupportedOnPlatform(policy, 'chrome_os',
+                                          management='active_directory'):
         pieces.append('0x%08x (Windows)' % example_value)
       if self.IsPolicySupportedOnPlatform(policy, 'linux'):
         pieces.append('%d (Linux)' % example_value)
@@ -519,7 +523,8 @@
       if policy['type'] in ('dict', 'external', 'list'):
         is_complex_policy = True
     if ((self.IsPolicySupportedOnPlatform(policy, 'win') or
-         self.IsPolicySupportedOnPlatform(policy, 'chrome_os')) and
+        self.IsPolicySupportedOnPlatform(
+            policy, 'chrome_os', management='active_directory')) and
         self._REG_TYPE_MAP.get(policy['type'], None)):
       qualified_types.append('Windows:%s' % self._REG_TYPE_MAP[policy['type']])
       if policy['type'] in ('dict', 'external'):
@@ -537,7 +542,8 @@
           'win_reg_loc',
           key_name + '\\' + policy['name'],
           ['.monospace'])
-    if self.IsPolicySupportedOnPlatform(policy, 'chrome_os'):
+    if self.IsPolicySupportedOnPlatform(policy, 'chrome_os',
+                                        management='active_directory'):
       key_name = self._GetRegistryKeyName(policy, False)
       self._AddPolicyAttribute(
           dl,
@@ -551,13 +557,13 @@
           'mac_linux_pref_name',
           policy['name'],
           ['.monospace'])
-    if self.IsPolicySupportedOnPlatform(policy, 'android', 'chrome'):
+    if self.IsPolicySupportedOnPlatform(policy, 'android', product='chrome'):
       self._AddPolicyAttribute(
           dl,
           'android_restriction_name',
           policy['name'],
           ['.monospace'])
-    if self.IsPolicySupportedOnPlatform(policy, 'android', 'webview'):
+    if self.IsPolicySupportedOnPlatform(policy, 'android', product='webview'):
       restriction_prefix = self.config['android_webview_restriction_prefix']
       self._AddPolicyAttribute(
           dl,
@@ -576,8 +582,10 @@
     if (self.IsPolicySupportedOnPlatform(policy, 'win') or
         self.IsPolicySupportedOnPlatform(policy, 'linux') or
         self.IsPolicySupportedOnPlatform(policy, 'android') or
-        self.IsPolicySupportedOnPlatform(policy, 'mac')):
-      # Don't add an example for ChromeOS-only policies.
+        self.IsPolicySupportedOnPlatform(policy, 'mac') or
+        self.IsPolicySupportedOnPlatform(policy, 'chrome_os',
+                                         management='active_directory')):
+      # Don't add an example for Google cloud managed ChromeOS policies.
       dd = self._AddPolicyAttribute(dl, 'example_value')
       self._AddExample(dd, policy)
 
diff --git a/components/policy/tools/template_writers/writers/doc_writer_unittest.py b/components/policy/tools/template_writers/writers/doc_writer_unittest.py
index 08d13ce7..65f9830 100755
--- a/components/policy/tools/template_writers/writers/doc_writer_unittest.py
+++ b/components/policy/tools/template_writers/writers/doc_writer_unittest.py
@@ -77,7 +77,7 @@
       'doc_note': {'text': '_test_note'},
       'doc_name_column_title': {'text': '_test_name_column_title'},
       'doc_not_supported': {'text': '_test_not_supported'},
-      'doc_since_version': {'text': '_test_since_version'},
+      'doc_since_version': {'text': '..$6..'},
       'doc_supported': {'text': '_test_supported'},
       'doc_supported_features': {'text': '_test_supported_features'},
       'doc_supported_on': {'text': '_test_supported_on'},
@@ -454,7 +454,6 @@
       'example_value': False,
       'arc_support': 'TestArcSupportNote'
     }
-    self.writer.messages['doc_since_version'] = {'text': '...$6...'}
     self.writer._AddPolicyDetails(self.doc_root, policy)
     self.assertEquals(
       self.doc_root.toxml(),
@@ -474,10 +473,10 @@
       '<dt style="style_dt;">_test_supported_on</dt>'
       '<dd>'
         '<ul style="style_ul;">'
-          '<li>Chrome (Windows, Mac, Linux) ...8...</li>'
-          '<li>Chrome (Android) ...30...</li>'
-          '<li>WebView (Android) ...47...</li>'
-          '<li>Chrome (Chrome OS) ...55...</li>'
+          '<li>Chrome (Windows, Mac, Linux) ..8..</li>'
+          '<li>Chrome (Android) ..30..</li>'
+          '<li>WebView (Android) ..47..</li>'
+          '<li>Chrome (Chrome OS) ..55..</li>'
         '</ul>'
       '</dd>'
       '<dt style="style_dt;">_test_supported_features</dt>'
@@ -507,7 +506,6 @@
       'features': {'dynamic_refresh': False},
       'example_value': False
     }
-    self.writer.messages['doc_since_version'] = {'text': '...$6...'}
     self.writer._AddPolicyDetails(self.doc_root, policy)
     self.assertEquals(
       self.doc_root.toxml(),
@@ -519,7 +517,7 @@
       '<dt style="style_dt;">_test_supported_on</dt>'
       '<dd>'
         '<ul style="style_ul;">'
-          '<li>Chrome (Linux) ...8...</li>'
+          '<li>Chrome (Linux) ..8..</li>'
         '</ul>'
       '</dd>'
       '<dt style="style_dt;">_test_supported_features</dt>'
@@ -547,7 +545,6 @@
       'features': {'dynamic_refresh': False},
       'example_value': { 'foo': 123 }
     }
-    self.writer.messages['doc_since_version'] = {'text': '...$6...'}
     self.writer._AddPolicyDetails(self.doc_root, policy)
     self.assertEquals(
       self.doc_root.toxml(),
@@ -563,7 +560,7 @@
       '<dt style="style_dt;">_test_supported_on</dt>'
       '<dd>'
         '<ul style="style_ul;">'
-          '<li>Chrome (Windows, Mac, Linux, Chrome OS) ...8...</li>'
+          '<li>Chrome (Windows, Mac, Linux, Chrome OS) ..8..</li>'
         '</ul>'
       '</dd>'
       '<dt style="style_dt;">_test_supported_features</dt>'
@@ -691,7 +688,6 @@
       },
       'example_value': False
     }
-    self.writer.messages['doc_since_version'] = {'text': '...$6...'}
     self.writer._AddPolicyDetails(self.doc_root, policy)
     self.assertEquals(
       self.doc_root.toxml(),
@@ -709,9 +705,9 @@
       '<dt style="style_dt;">_test_supported_on</dt>'
       '<dd>'
         '<ul style="style_ul;">'
-          '<li>Chrome (Windows, Mac, Linux) ...8...</li>'
-          '<li>Chrome (Android) ...30...</li>'
-          '<li>Chrome (Chrome OS) ...53...</li>'
+          '<li>Chrome (Windows, Mac, Linux) ..8..</li>'
+          '<li>Chrome (Android) ..30..</li>'
+          '<li>Chrome (Chrome OS) ..53..</li>'
         '</ul>'
       '</dd>'
       '<dt style="style_dt;">_test_supported_features</dt>'
@@ -788,7 +784,6 @@
       'features': {'dynamic_refresh': False},
       'example_value': 'False'
     }
-    self.writer.messages['doc_since_version'] = {'text': '..$6..'}
     self.writer._AddPolicySection(self.doc_root, policy)
     self.assertEquals(
       self.doc_root.toxml(),
@@ -850,7 +845,6 @@
       'features': {'dynamic_refresh': False},
       'example_value': 123
     }
-    self.writer.messages['doc_since_version'] = {'text': '..$6..'}
     self.writer._AddPolicySection(self.doc_root, policy)
     self.assertEquals(
       self.doc_root.toxml(),
@@ -895,7 +889,6 @@
       'features': {'dynamic_refresh': False},
       'example_value': 123
     }
-    self.writer.messages['doc_since_version'] = {'text': '..$6..'}
     self.writer._AddPolicySection(self.doc_root, policy)
     self.assertEquals(
       self.doc_root.toxml(),
@@ -940,7 +933,6 @@
       'features': {'dynamic_refresh': False},
       'example_value': 123
     }
-    self.writer.messages['doc_since_version'] = {'text': '..$6..'}
     self.writer._AddPolicySection(self.doc_root, policy)
     self.assertEquals(
       self.doc_root.toxml(),
@@ -985,7 +977,6 @@
       'features': {'dynamic_refresh': False},
       'example_value': 123
     }
-    self.writer.messages['doc_since_version'] = {'text': '..$6..'}
     self.writer._AddPolicySection(self.doc_root, policy)
     self.assertTrue(self.writer.IsPolicySupportedOnPlatform(policy, 'android'))
     self.assertEquals(
@@ -1145,5 +1136,96 @@
         self.doc_root.toxml(),
         '<root><p>Paragraph 1</p><p>Paragraph 2</p><p>Paragraph 3</p></root>')
 
+  def testGoogleCloudChromeOsPolicies(self):
+    # Tests whether Chrome OS policies with management type 'google_cloud'
+    # don't print example values etc. since they are managed through Google's
+    # CPanel/DPanel, not Active Directory GPO.
+    policy = {
+      'name': 'PolicyName',
+      'caption': 'PolicyCaption',
+      'desc': 'PolicyDesc',
+      'type': 'int',
+      'features': {},
+      'example_value': 42,
+      'supported_on': [{
+        'product': 'chrome_os',
+        'platforms': ['chrome_os'],
+        'since_version': '8',
+        'until_version': '',
+      }],
+      'supported_chrome_os_management': ['google_cloud']
+    }
+    self.writer._AddPolicySection(self.doc_root, policy)
+    self.assertEquals(
+      self.doc_root.toxml(),
+      '<root>'
+        '<div style="margin-left: 0px">'
+          '<h3><a name="PolicyName"/>PolicyName</h3>'
+          '<span>PolicyCaption</span>'
+          '<dl>'
+            '<dt style="style_dt;">_test_data_type</dt>'
+            '<dd>Integer</dd>'
+            '<dt style="style_dt;">_test_supported_on</dt>'
+            '<dd>'
+              '<ul style="style_ul;">'
+                '<li>Chrome OS (Chrome OS) ..8..</li>'
+              '</ul>'
+            '</dd>'
+            '<dt style="style_dt;">_test_supported_features</dt>'
+            '<dd></dd>'
+            '<dt style="style_dt;">_test_description</dt>'
+            '<dd><p>PolicyDesc</p></dd>'
+          '</dl>'
+          '<a href="#top">_test_back_to_top</a>'
+        '</div>'
+      '</root>')
+
+  def testActiveDirectoryChromeOsPolicies(self):
+    # Tests whether Chrome OS policies with management type 'active_directory'
+    # print example values etc.
+    policy = {
+      'name': 'PolicyName',
+      'caption': 'PolicyCaption',
+      'desc': 'PolicyDesc',
+      'type': 'int',
+      'features': {},
+      'example_value': 42,
+      'supported_on': [{
+        'product': 'chrome_os',
+        'platforms': ['chrome_os'],
+        'since_version': '8',
+        'until_version': '',
+      }],
+      'supported_chrome_os_management': ['active_directory']
+    }
+    self.writer._AddPolicySection(self.doc_root, policy)
+    self.assertEquals(
+      self.doc_root.toxml(),
+      '<root>'
+        '<div style="margin-left: 0px">'
+          '<h3><a name="PolicyName"/>PolicyName</h3>'
+          '<span>PolicyCaption</span>'
+          '<dl>'
+            '<dt style="style_dt;">_test_data_type</dt>'
+            '<dd>Integer [Windows:REG_DWORD]</dd>'
+            '<dt style="style_dt;">_test_chrome_os_reg_loc</dt>'
+            '<dd style="style_.monospace;">MockKeyCrOS\\PolicyName</dd>'
+            '<dt style="style_dt;">_test_supported_on</dt>'
+            '<dd>'
+              '<ul style="style_ul;">'
+                '<li>Chrome OS (Chrome OS) ..8..</li>'
+              '</ul>'
+            '</dd>'
+            '<dt style="style_dt;">_test_supported_features</dt>'
+            '<dd></dd>'
+            '<dt style="style_dt;">_test_description</dt>'
+            '<dd><p>PolicyDesc</p></dd>'
+            '<dt style="style_dt;">_test_example_value</dt>'
+            '<dd>0x0000002a (Windows)</dd>'
+          '</dl>'
+          '<a href="#top">_test_back_to_top</a>'
+        '</div>'
+      '</root>')
+
 if __name__ == '__main__':
   unittest.main()
diff --git a/components/policy/tools/template_writers/writers/json_writer_unittest.py b/components/policy/tools/template_writers/writers/json_writer_unittest.py
index a828dc3..c64d226f 100755
--- a/components/policy/tools/template_writers/writers/json_writer_unittest.py
+++ b/components/policy/tools/template_writers/writers/json_writer_unittest.py
@@ -46,7 +46,7 @@
     '''Compares the output of the json_writer with its expected output.
 
     Args:
-      output: The output of the json writer as returned by grit.
+      output: The output of the json writer.
       expected_output: The expected output.
 
     Raises:
diff --git a/components/policy/tools/template_writers/writers/reg_writer_unittest.py b/components/policy/tools/template_writers/writers/reg_writer_unittest.py
index adb9b8f..23a4b4e1 100755
--- a/components/policy/tools/template_writers/writers/reg_writer_unittest.py
+++ b/components/policy/tools/template_writers/writers/reg_writer_unittest.py
@@ -26,7 +26,7 @@
     '''Compares the output of the reg_writer with its expected output.
 
     Args:
-      output: The output of the reg writer as returned by grit.
+      output: The output of the reg writer.
       expected_output: The expected output.
 
     Raises:
diff --git a/components/policy/tools/template_writers/writers/template_writer.py b/components/policy/tools/template_writers/writers/template_writer.py
index 4a6b77b..4cb64844 100755
--- a/components/policy/tools/template_writers/writers/template_writer.py
+++ b/components/policy/tools/template_writers/writers/template_writer.py
@@ -91,21 +91,44 @@
     '''Checks if the given policy can be mandatory.'''
     return policy.get('features', {}).get('can_be_mandatory', True)
 
-  def IsPolicySupportedOnPlatform(self, policy, platform, product=None):
-    '''Checks if |policy| is supported on |product| for |platform|. If not
-    specified, only the platform support is checked.
+  def IsPolicySupportedOnPlatform(
+      self, policy, platform, product=None, management=None):
+    '''Checks if |policy| is supported on |product| for |platform|. If
+    |platform| is not specified, only the platform support is checked.
+    If |management| is specified, also checks for support for Chrome OS
+    management type.
 
     Args:
       policy: The dictionary of the policy.
-      platform: The platform to check; one of 'win', 'mac', 'linux' or
-        'chrome_os'.
-      product: Optional product to check; one of 'chrome', 'chrome_frame',
-        'chrome_os', 'webview'
+      platform: The platform to check; one of
+        'win', 'mac', 'linux', 'chrome_os', 'android'.
+      product: Optional product to check; one of
+        'chrome', 'chrome_frame', 'chrome_os', 'webview'.
+      management: Optional Chrome OS management type to check; one of
+        'active_directory', 'google_cloud'.
     '''
-    is_supported = lambda x: (platform in x['platforms'] and
-                             (not product or product in x['product']))
+    if management and not self.IsCrOSManagementSupported(policy, management):
+      return False
 
-    return any(filter(is_supported, policy['supported_on']))
+    for supported_on in policy['supported_on']:
+      if platform in supported_on['platforms'] and \
+          (not product or product in supported_on['product']):
+        return True
+
+    return False
+
+  def IsCrOSManagementSupported(self, policy, management):
+    '''Checks whether |policy| supports the Chrome OS |management| type.
+
+    Args:
+      policy: The dictionary of the policy.
+      management: Chrome OS management type to check; one of
+        'active_directory', 'google_cloud'.
+    '''
+    # By default, i.e. if supported_chrome_os_management is not set, all
+    # management types are supported.
+    return management in policy.get('supported_chrome_os_management',
+                                    ['active_directory', 'google_cloud'])
 
   def _GetChromiumVersionString(self):
     '''Returns the Chromium version string stored in the environment variable
diff --git a/components/sync/base/sync_prefs.cc b/components/sync/base/sync_prefs.cc
index 04b3bd3..b252a0c 100644
--- a/components/sync/base/sync_prefs.cc
+++ b/components/sync/base/sync_prefs.cc
@@ -173,7 +173,7 @@
 base::Time SyncPrefs::GetLastPollTime() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return base::Time::FromInternalValue(
-      pref_service_->GetInt64(prefs::kSyncLastSyncedTime));
+      pref_service_->GetInt64(prefs::kSyncLastPollTime));
 }
 
 void SyncPrefs::SetLastPollTime(base::Time time) {
diff --git a/components/ui_devtools/views/ui_devtools_dom_agent.cc b/components/ui_devtools/views/ui_devtools_dom_agent.cc
index 805b79a..7d92230 100644
--- a/components/ui_devtools/views/ui_devtools_dom_agent.cc
+++ b/components/ui_devtools/views/ui_devtools_dom_agent.cc
@@ -302,6 +302,22 @@
                         render_text);
 }
 
+void DrawR1TopPartialLeftR2(const gfx::RectF& pinned_rect_f,
+                            const gfx::RectF& hovered_rect_f,
+                            const cc::PaintFlags& flags,
+                            gfx::Canvas* canvas,
+                            gfx::RenderText* render_text) {
+  float x1 = hovered_rect_f.x() + hovered_rect_f.width() / 2;
+  float y1 = hovered_rect_f.bottom();
+  float x2 = x1;
+  float y2 = pinned_rect_f.y();
+
+  // Vertical left dotted line.
+  canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
+  DrawTextWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
+                        render_text);
+}
+
 }  // namespace
 
 UIDevToolsDOMAgent::UIDevToolsDOMAgent()
@@ -618,7 +634,16 @@
                        flags);
       return;
     case HighlightRectsConfiguration::R1_TOP_PARTIAL_LEFT_R2:
-      NOTIMPLEMENTED();
+      DrawR1TopPartialLeftR2(pinned_rect_f, hovered_rect_f, flags, canvas,
+                             render_text_.get());
+
+      // Draw 1 guide line along distance lines.
+      flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
+
+      // Top horizontal dotted line from left to right.
+      canvas->DrawLine(gfx::PointF(0.0f, pinned_rect_f.y()),
+                       gfx::PointF(screen_bounds.right(), pinned_rect_f.y()),
+                       flags);
       return;
     case HighlightRectsConfiguration::R1_BOTTOM_PARTIAL_LEFT_R2:
       NOTIMPLEMENTED();
diff --git a/components/ui_devtools/views/ui_devtools_unittest.cc b/components/ui_devtools/views/ui_devtools_unittest.cc
index ea8e32e..40c1317 100644
--- a/components/ui_devtools/views/ui_devtools_unittest.cc
+++ b/components/ui_devtools/views/ui_devtools_unittest.cc
@@ -544,6 +544,7 @@
   // Swapping R1 and R2 shouldn't change |highlight_rect_config|.
   dom_agent()->ShowDistancesInHighlightOverlay(top_right_rect_id,
                                                bottom_left_rect_id);
+
   DCHECK_EQ(highlight_rect_config, dom_agent()->highlight_rect_config());
 
   const std::pair<aura::Window*, gfx::Rect> element_top_left(
@@ -562,6 +563,50 @@
             HighlightRectsConfiguration::R1_BOTTOM_FULL_LEFT_R2);
 }
 
+// Test case R1_TOP_PARTIAL_LEFT_R2.
+TEST_F(UIDevToolsTest, OneUIElementStaysPartiallyTopLeftOfAnother) {
+  const gfx::Rect top_left_rect(100, 100, 50, 50);
+  std::unique_ptr<views::Widget> widget_top_left(
+      CreateTestWidget(top_left_rect));
+
+  const gfx::Rect bottom_right_rect(120, 200, 50, 50);
+  std::unique_ptr<views::Widget> widget_bottom_right(
+      CreateTestWidget(bottom_right_rect));
+
+  std::unique_ptr<ui_devtools::protocol::DOM::Node> root;
+  dom_agent()->getDocument(&root);
+
+  int top_left_rect_id = dom_agent()->FindElementIdTargetedByPoint(
+      top_left_rect.origin(), GetPrimaryRootWindow());
+  int bottom_right_rect_id = dom_agent()->FindElementIdTargetedByPoint(
+      bottom_right_rect.origin(), GetPrimaryRootWindow());
+  dom_agent()->ShowDistancesInHighlightOverlay(top_left_rect_id,
+                                               bottom_right_rect_id);
+
+  HighlightRectsConfiguration highlight_rect_config =
+      dom_agent()->highlight_rect_config();
+
+  // Swapping R1 and R2 shouldn't change |highlight_rect_config|.
+  dom_agent()->ShowDistancesInHighlightOverlay(bottom_right_rect_id,
+                                               top_left_rect_id);
+  DCHECK_EQ(highlight_rect_config, dom_agent()->highlight_rect_config());
+
+  const std::pair<aura::Window*, gfx::Rect> element_top_left(
+      dom_agent()
+          ->GetElementFromNodeId(top_left_rect_id)
+          ->GetNodeWindowAndBounds());
+
+  const std::pair<aura::Window*, gfx::Rect> element_bottom_right(
+      dom_agent()
+          ->GetElementFromNodeId(bottom_right_rect_id)
+          ->GetNodeWindowAndBounds());
+
+  EXPECT_EQ(element_top_left.second, top_left_rect);
+  EXPECT_EQ(element_bottom_right.second, bottom_right_rect);
+  DCHECK_EQ(dom_agent()->highlight_rect_config(),
+            HighlightRectsConfiguration::R1_TOP_PARTIAL_LEFT_R2);
+}
+
 // Tests that the correct Overlay events are dispatched to the frontend when
 // hovering and clicking over a UI element in inspect mode.
 TEST_F(UIDevToolsTest, MouseEventsGenerateFEEventsInInspectMode) {
diff --git a/content/browser/android/ime_adapter_android.cc b/content/browser/android/ime_adapter_android.cc
index 070b715..4d56af3 100644
--- a/content/browser/android/ime_adapter_android.cc
+++ b/content/browser/android/ime_adapter_android.cc
@@ -99,21 +99,25 @@
                           jlong ime_text_spans_ptr,
                           jint start,
                           jint end,
+                          jboolean is_misspelling,
                           jint underline_color,
                           jint suggestion_highlight_color,
                           const JavaParamRef<jobjectArray>& suggestions) {
   DCHECK_GE(start, 0);
   DCHECK_GE(end, 0);
 
+  blink::WebImeTextSpan::Type type =
+      is_misspelling ? blink::WebImeTextSpan::Type::kMisspellingSuggestion
+                     : blink::WebImeTextSpan::Type::kSuggestion;
+
   std::vector<blink::WebImeTextSpan>* ime_text_spans =
       reinterpret_cast<std::vector<blink::WebImeTextSpan>*>(ime_text_spans_ptr);
   std::vector<std::string> suggestions_vec;
   AppendJavaStringArrayToStringVector(env, suggestions, &suggestions_vec);
   ime_text_spans->push_back(blink::WebImeTextSpan(
-      blink::WebImeTextSpan::Type::kSuggestion, static_cast<unsigned>(start),
-      static_cast<unsigned>(end), static_cast<unsigned>(underline_color), true,
-      SK_ColorTRANSPARENT, static_cast<unsigned>(suggestion_highlight_color),
-      suggestions_vec));
+      type, static_cast<unsigned>(start), static_cast<unsigned>(end),
+      static_cast<unsigned>(underline_color), true, SK_ColorTRANSPARENT,
+      static_cast<unsigned>(suggestion_highlight_color), suggestions_vec));
 }
 
 // Callback from Java to convert UnderlineSpan data to a
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index bf49a80..fff0f247 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -2108,6 +2108,11 @@
     const base::string16& content,
     uint32_t start_offset,
     uint32_t end_offset) {
+  // text_surrounding_selection_callback_ should not be null, but don't trust
+  // the renderer.
+  if (text_surrounding_selection_callback_.is_null())
+    return;
+
   // Just Run the callback instead of propagating further.
   text_surrounding_selection_callback_.Run(content, start_offset, end_offset);
   // Reset the callback for enabling early exit from future request.
diff --git a/content/browser/renderer_host/p2p/socket_host.cc b/content/browser/renderer_host/p2p/socket_host.cc
index 70b4482..499c0a03 100644
--- a/content/browser/renderer_host/p2p/socket_host.cc
+++ b/content/browser/renderer_host/p2p/socket_host.cc
@@ -12,6 +12,8 @@
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/base/net_errors.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
 #include "third_party/webrtc/media/base/rtputils.h"
 #include "third_party/webrtc/media/base/turnutils.h"
 
@@ -182,7 +184,9 @@
                                      P2PMessageThrottler* throttler) {
   switch (type) {
     case P2P_SOCKET_UDP:
-      return new P2PSocketHostUdp(message_sender, socket_id, throttler);
+      return new P2PSocketHostUdp(
+          message_sender, socket_id, throttler,
+          url_context->GetURLRequestContext()->net_log());
     case P2P_SOCKET_TCP_SERVER:
       return new P2PSocketHostTcpServer(
           message_sender, socket_id, P2P_SOCKET_TCP_CLIENT);
diff --git a/content/browser/renderer_host/p2p/socket_host_udp.cc b/content/browser/renderer_host/p2p/socket_host_udp.cc
index 375e5571..3094699 100644
--- a/content/browser/renderer_host/p2p/socket_host_udp.cc
+++ b/content/browser/renderer_host/p2p/socket_host_udp.cc
@@ -97,21 +97,25 @@
     IPC::Sender* message_sender,
     int socket_id,
     P2PMessageThrottler* throttler,
+    net::NetLog* net_log,
     const DatagramServerSocketFactory& socket_factory)
     : P2PSocketHost(message_sender, socket_id, P2PSocketHost::UDP),
-      socket_(socket_factory.Run()),
+      socket_(socket_factory.Run(net_log)),
       send_pending_(false),
       last_dscp_(net::DSCP_CS0),
       throttler_(throttler),
       send_buffer_size_(0),
+      net_log_(net_log),
       socket_factory_(socket_factory) {}
 
 P2PSocketHostUdp::P2PSocketHostUdp(IPC::Sender* message_sender,
                                    int socket_id,
-                                   P2PMessageThrottler* throttler)
+                                   P2PMessageThrottler* throttler,
+                                   net::NetLog* net_log)
     : P2PSocketHostUdp(message_sender,
                        socket_id,
                        throttler,
+                       net_log,
                        base::Bind(&P2PSocketHostUdp::DefaultSocketFactory)) {}
 
 P2PSocketHostUdp::~P2PSocketHostUdp() {
@@ -153,7 +157,7 @@
     for (unsigned port = min_port; port <= max_port && result < 0; ++port) {
       result = socket_->Listen(net::IPEndPoint(local_address.address(), port));
       if (result < 0 && port != max_port)
-        socket_ = socket_factory_.Run();
+        socket_ = socket_factory_.Run(net_log_);
     }
   } else if (local_address.port() >= min_port &&
              local_address.port() <= max_port) {
@@ -448,9 +452,9 @@
 
 // static
 std::unique_ptr<net::DatagramServerSocket>
-P2PSocketHostUdp::DefaultSocketFactory() {
-  net::UDPServerSocket* socket = new net::UDPServerSocket(
-      GetContentClient()->browser()->GetNetLog(), net::NetLogSource());
+P2PSocketHostUdp::DefaultSocketFactory(net::NetLog* net_log) {
+  net::UDPServerSocket* socket =
+      new net::UDPServerSocket(net_log, net::NetLogSource());
 #if defined(OS_WIN)
   socket->UseNonBlockingIO();
 #endif
diff --git a/content/browser/renderer_host/p2p/socket_host_udp.h b/content/browser/renderer_host/p2p/socket_host_udp.h
index a54f001..5e4bd80 100644
--- a/content/browser/renderer_host/p2p/socket_host_udp.h
+++ b/content/browser/renderer_host/p2p/socket_host_udp.h
@@ -26,21 +26,28 @@
 #include "net/socket/udp_server_socket.h"
 #include "third_party/webrtc/rtc_base/asyncpacketsocket.h"
 
+namespace net {
+class NetLog;
+}  // namespace net
+
 namespace content {
 
 class P2PMessageThrottler;
 
 class CONTENT_EXPORT P2PSocketHostUdp : public P2PSocketHost {
  public:
-  typedef base::Callback<std::unique_ptr<net::DatagramServerSocket>()>
+  typedef base::Callback<std::unique_ptr<net::DatagramServerSocket>(
+      net::NetLog* net_log)>
       DatagramServerSocketFactory;
   P2PSocketHostUdp(IPC::Sender* message_sender,
                    int socket_id,
                    P2PMessageThrottler* throttler,
+                   net::NetLog* net_log,
                    const DatagramServerSocketFactory& socket_factory);
   P2PSocketHostUdp(IPC::Sender* message_sender,
                    int socket_id,
-                   P2PMessageThrottler* throttler);
+                   P2PMessageThrottler* throttler,
+                   net::NetLog* net_log);
   ~P2PSocketHostUdp() override;
 
   // P2PSocketHost overrides.
@@ -93,7 +100,8 @@
                         int32_t transport_sequence_number,
                         base::TimeTicks send_time,
                         int result);
-  static std::unique_ptr<net::DatagramServerSocket> DefaultSocketFactory();
+  static std::unique_ptr<net::DatagramServerSocket> DefaultSocketFactory(
+      net::NetLog* net_log);
 
   std::unique_ptr<net::DatagramServerSocket> socket_;
   scoped_refptr<net::IOBuffer> recv_buffer_;
@@ -111,6 +119,8 @@
   // Keep track of the send socket buffer size under experiment.
   size_t send_buffer_size_;
 
+  net::NetLog* net_log_;
+
   // Callback object that returns a new socket when invoked.
   DatagramServerSocketFactory socket_factory_;
 
diff --git a/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc b/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc
index 2e52da9..4df3558 100644
--- a/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc
+++ b/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc
@@ -192,7 +192,8 @@
 
 std::unique_ptr<net::DatagramServerSocket> CreateFakeDatagramServerSocket(
     base::circular_deque<FakeDatagramServerSocket::UDPPacket>* sent_packets,
-    std::vector<uint16_t>* used_ports) {
+    std::vector<uint16_t>* used_ports,
+    net::NetLog* net_log) {
   return base::MakeUnique<FakeDatagramServerSocket>(sent_packets, used_ports);
 }
 
@@ -209,7 +210,7 @@
         .WillOnce(DoAll(DeleteArg<0>(), Return(true)));
 
     socket_host_.reset(new P2PSocketHostUdp(
-        &sender_, 0, &throttler_,
+        &sender_, 0, &throttler_, /*net_log=*/nullptr,
         base::Bind(&CreateFakeDatagramServerSocket, &sent_packets_, nullptr)));
 
     local_address_ = ParseAddress(kTestLocalIpAddress, kTestPort1);
@@ -507,8 +508,8 @@
       .WillRepeatedly(DoAll(DeleteArg<0>(), Return(true)));
 
   for (unsigned port = min_port; port <= max_port; ++port) {
-    std::unique_ptr<P2PSocketHostUdp> socket_host(
-        new P2PSocketHostUdp(&sender, 0, &throttler, fake_socket_factory));
+    std::unique_ptr<P2PSocketHostUdp> socket_host(new P2PSocketHostUdp(
+        &sender, 0, &throttler, /*net_log=*/nullptr, fake_socket_factory));
     net::IPEndPoint local_address = ParseAddress(kTestLocalIpAddress, 0);
     bool rv = socket_host->Init(local_address, min_port, max_port,
                                 P2PHostAndIPEndPoint());
@@ -523,8 +524,8 @@
   EXPECT_CALL(sender,
               Send(MatchMessage(static_cast<uint32_t>(P2PMsg_OnError::ID))))
       .WillOnce(DoAll(DeleteArg<0>(), Return(true)));
-  std::unique_ptr<P2PSocketHostUdp> socket_host(
-      new P2PSocketHostUdp(&sender, 0, &throttler, fake_socket_factory));
+  std::unique_ptr<P2PSocketHostUdp> socket_host(new P2PSocketHostUdp(
+      &sender, 0, &throttler, /*net_log=*/nullptr, fake_socket_factory));
   net::IPEndPoint local_address = ParseAddress(kTestLocalIpAddress, 0);
   bool rv = socket_host->Init(local_address, min_port, max_port,
                               P2PHostAndIPEndPoint());
@@ -548,8 +549,8 @@
       Send(MatchMessage(static_cast<uint32_t>(P2PMsg_OnSocketCreated::ID))))
       .WillOnce(DoAll(DeleteArg<0>(), Return(true)));
 
-  std::unique_ptr<P2PSocketHostUdp> socket_host(
-      new P2PSocketHostUdp(&sender, 0, &throttler, fake_socket_factory));
+  std::unique_ptr<P2PSocketHostUdp> socket_host(new P2PSocketHostUdp(
+      &sender, 0, &throttler, /*net_log=*/nullptr, fake_socket_factory));
   net::IPEndPoint local_address = ParseAddress(kTestLocalIpAddress, valid_port);
   bool rv = socket_host->Init(local_address, min_port, max_port,
                               P2PHostAndIPEndPoint());
@@ -577,8 +578,8 @@
               Send(MatchMessage(static_cast<uint32_t>(P2PMsg_OnError::ID))))
       .WillOnce(DoAll(DeleteArg<0>(), Return(true)));
 
-  std::unique_ptr<P2PSocketHostUdp> socket_host(
-      new P2PSocketHostUdp(&sender, 0, &throttler, fake_socket_factory));
+  std::unique_ptr<P2PSocketHostUdp> socket_host(new P2PSocketHostUdp(
+      &sender, 0, &throttler, /*net_log=*/nullptr, fake_socket_factory));
   net::IPEndPoint local_address =
       ParseAddress(kTestLocalIpAddress, invalid_port);
   bool rv = socket_host->Init(local_address, min_port, max_port,
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index c9d1ba79..f6d30cd 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -36,6 +36,7 @@
   sources = [
     "accessibility_messages.h",
     "all_messages.h",
+    "android/browser_side_navigation_policy_android.cc",
     "android/gin_java_bridge_errors.cc",
     "android/gin_java_bridge_errors.h",
     "android/gin_java_bridge_value.cc",
diff --git a/content/common/android/browser_side_navigation_policy_android.cc b/content/common/android/browser_side_navigation_policy_android.cc
new file mode 100644
index 0000000..d2fe4a8
--- /dev/null
+++ b/content/common/android/browser_side_navigation_policy_android.cc
@@ -0,0 +1,20 @@
+// Copyright 2017 The Chromium 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 <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "content/public/common/browser_side_navigation_policy.h"
+#include "jni/BrowserSideNavigationPolicy_jni.h"
+
+using base::android::JavaParamRef;
+
+namespace content {
+
+jboolean IsBrowserSideNavigationEnabled(JNIEnv* env,
+                                        const JavaParamRef<jclass>& clazz) {
+  return IsBrowserSideNavigationEnabled();
+}
+
+}  // namespace content
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index b6261a8..057aca7 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -245,6 +245,7 @@
     "java/src/org/chromium/content_public/browser/WebContents.java",
     "java/src/org/chromium/content_public/browser/WebContentsObserver.java",
     "java/src/org/chromium/content_public/browser/WebContentsStatics.java",
+    "java/src/org/chromium/content_public/common/BrowserSideNavigationPolicy.java",
     "java/src/org/chromium/content_public/common/ContentProcessInfo.java",
     "java/src/org/chromium/content_public/common/ContentUrlConstants.java",
     "java/src/org/chromium/content_public/common/MediaMetadata.java",
@@ -374,6 +375,7 @@
     "java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java",
     "java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java",
     "java/src/org/chromium/content_public/browser/LoadUrlParams.java",
+    "java/src/org/chromium/content_public/common/BrowserSideNavigationPolicy.java",
     "java/src/org/chromium/content_public/common/MediaMetadata.java",
     "java/src/org/chromium/content_public/common/ResourceRequestBody.java",
   ]
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java
index 7c61c5f..da92604 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java
@@ -911,8 +911,7 @@
             } else if (span instanceof SuggestionSpan) {
                 final SuggestionSpan suggestionSpan = (SuggestionSpan) span;
 
-                // We currently only support FLAG_EASY_CORRECT SuggestionSpans.
-                // TODO(rlanday): support FLAG_MISSPELLED SuggestionSpans.
+                // We currently only support FLAG_EASY_CORRECT and FLAG_MISSPELLED SuggestionSpans.
 
                 // Other types:
                 // - FLAG_AUTO_CORRECTION is used e.g. by Samsung's IME to flash a blue background
@@ -923,7 +922,10 @@
                 //   flags set and no underline color to add suggestions to words marked as
                 //   misspelled (instead of having the spell checker return the suggestions when
                 //   called). We don't support these either.
-                if (suggestionSpan.getFlags() != SuggestionSpan.FLAG_EASY_CORRECT) {
+                final boolean isMisspellingSpan =
+                        (suggestionSpan.getFlags() & SuggestionSpan.FLAG_MISSPELLED) != 0;
+                if (suggestionSpan.getFlags() != SuggestionSpan.FLAG_EASY_CORRECT
+                        && !isMisspellingSpan) {
                     continue;
                 }
 
@@ -937,8 +939,8 @@
 
                 nativeAppendSuggestionSpan(imeTextSpans,
                         spannableString.getSpanStart(suggestionSpan),
-                        spannableString.getSpanEnd(suggestionSpan), underlineColor,
-                        suggestionHighlightColor, suggestionSpan.getSuggestions());
+                        spannableString.getSpanEnd(suggestionSpan), isMisspellingSpan,
+                        underlineColor, suggestionHighlightColor, suggestionSpan.getSuggestions());
             }
         }
     }
@@ -971,7 +973,8 @@
     private static native void nativeAppendBackgroundColorSpan(
             long spanPtr, int start, int end, int backgroundColor);
     private static native void nativeAppendSuggestionSpan(long spanPtr, int start, int end,
-            int underlineColor, int suggestionHighlightColor, String[] suggestions);
+            boolean isMisspelling, int underlineColor, int suggestionHighlightColor,
+            String[] suggestions);
     private native void nativeSetComposingText(long nativeImeAdapterAndroid, CharSequence text,
             String textStr, int newCursorPosition);
     private native void nativeCommitText(
diff --git a/content/public/android/java/src/org/chromium/content_public/common/BrowserSideNavigationPolicy.java b/content/public/android/java/src/org/chromium/content_public/common/BrowserSideNavigationPolicy.java
new file mode 100644
index 0000000..f659d79
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content_public/common/BrowserSideNavigationPolicy.java
@@ -0,0 +1,22 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.content_public.common;
+
+import org.chromium.base.annotations.JNINamespace;
+
+/**
+ * This is a utility class to wrap browser_side_navigation_policy.cc.
+ */
+@JNINamespace("content")
+public final class BrowserSideNavigationPolicy {
+    public static boolean isBrowserSideNavigationEnabled() {
+        return nativeIsBrowserSideNavigationEnabled();
+    }
+
+    private static native boolean nativeIsBrowserSideNavigationEnabled();
+
+    // Do not instantiate this class.
+    private BrowserSideNavigationPolicy() {}
+}
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/TextSuggestionMenuTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/TextSuggestionMenuTest.java
index f060462..0d24b82 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/input/TextSuggestionMenuTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/input/TextSuggestionMenuTest.java
@@ -149,6 +149,53 @@
 
     @Test
     @LargeTest
+    public void testApplyMisspellingSuggestion()
+            throws InterruptedException, Throwable, TimeoutException {
+        final ContentViewCore cvc = mRule.getContentViewCore();
+        WebContents webContents = cvc.getWebContents();
+
+        DOMUtils.focusNode(webContents, "div");
+
+        SpannableString textToCommit = new SpannableString("word");
+
+        SuggestionSpan suggestionSpan = new SuggestionSpan(mRule.getContentViewCore().getContext(),
+                new String[] {"replacement"},
+                SuggestionSpan.FLAG_EASY_CORRECT | SuggestionSpan.FLAG_MISSPELLED);
+        textToCommit.setSpan(suggestionSpan, 0, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+        mRule.commitText(textToCommit, 1);
+
+        DOMUtils.clickNode(cvc, "span");
+        waitForMenuToShow(cvc);
+
+        // There should be 2 child views: 1 suggestion plus the list footer.
+        Assert.assertEquals(2, getSuggestionList(cvc).getChildCount());
+
+        Assert.assertEquals(
+                "replacement", ((TextView) getSuggestionButton(cvc, 0)).getText().toString());
+
+        TouchCommon.singleClickView(getSuggestionButton(cvc, 0));
+
+        CriteriaHelper.pollInstrumentationThread(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                try {
+                    return DOMUtils.getNodeContents(cvc.getWebContents(), "div")
+                            .equals("replacement");
+                } catch (InterruptedException | TimeoutException e) {
+                    return false;
+                }
+            }
+        });
+
+        waitForMenuToHide(cvc);
+
+        // TODO(rlanday): Verify that the suggestion marker was removed once we have a way to do
+        // this in a content test (crbug.com/767507).
+    }
+
+    @Test
+    @LargeTest
     public void menuDismissal() throws InterruptedException, Throwable, TimeoutException {
         final ContentViewCore cvc = mRule.getContentViewCore();
         WebContents webContents = cvc.getWebContents();
diff --git a/content/test/gpu/generate_buildbot_json.py b/content/test/gpu/generate_buildbot_json.py
index dbdd8a74..6b5e579 100755
--- a/content/test/gpu/generate_buildbot_json.py
+++ b/content/test/gpu/generate_buildbot_json.py
@@ -1243,7 +1243,18 @@
           {
             'gpu': '10de:104a',
             'os': 'Ubuntu'
-          }
+          },
+          # Mac Intel
+          {
+            'gpu': '8086:0a2e',
+            'os': 'Mac-10.12.6'
+          },
+          # Mac AMD
+          {
+            'gpu': '1002:6821',
+            'hidpi': '1',
+            'os': 'Mac-10.12.6'
+          },
         ],
       }
     ],
diff --git a/docs/speed/binary_size/metrics.md b/docs/speed/binary_size/metrics.md
new file mode 100644
index 0000000..40bd77b
--- /dev/null
+++ b/docs/speed/binary_size/metrics.md
@@ -0,0 +1,89 @@
+# Binary Size Metrics
+
+This document lists metrics used to track binary size.
+
+[TOC]
+
+## Metrics for Desktop
+
+ * Sizes are collected by
+   [//build/scripts/slave/chromium/sizes.py](https://cs.chromium.org/chromium/build/scripts/slave/chromium/sizes.py)
+   * [Win32 Telemetry Graphs](https://chromeperf.appspot.com/report?sid=b3dcc318b51f3780924dfd3d82265ca901ac690cb61af91919997dda9821547c)
+   * [Linux Telemetry Graphs](https://chromeperf.appspot.com/report?sid=bd18d34b6d29f26877e7075cb5c34c56c011d99803e9120d61610d7eaef38e9c)
+   * [Mac Telemetry Graphs](https://chromeperf.appspot.com/report?sid=2cb6e0a9941e63418e7b83f91583282fa9fbaaafc2d19b3fa1179b28e7d3f7eb)
+
+### Alerting
+
+ * Alerts are sheriffed as part of the main perf sherif rotation.
+ * Alerts generally fire for ~100kb jumps.
+
+## Metrics for Android
+
+For Googlers, more information available at [go/chrome-apk-size](https://goto.google.com/chrome-apk-size).
+
+ * Sizes are collected by
+   [//build/android/resource_sizes.py](https://cs.chromium.org/chromium/src/build/android/resource_sizes.py).
+ * How to analyze Android binary size discussed in [apk_size_regressions.md#debugging-apk-size-increase](../apk_size_regressions.md#debugging-apk-size-increase).
+ * Sizes for `ChromePublic.apk`, `ChromeModernPublic.apk`, `MonochromePublic.apk`, `SystemWebview.apk` are tracked.
+   * But only `MonochromePublic.apk` is actively monitored.
+ * We care most about on-disk size (for users with small device storage)
+   * But also care about patch size (so that browser updates get applied)
+
+### Normalized APK Size
+
+ * [Telemetry Graph](https://chromeperf.appspot.com/report?sid=d8f7a27ce61034755ba926dfff9beff7dfbee6f8a596da7f4bb80e3bdd721ad4)
+ * Monitored by [Binary Size Sheriffs](../apk_size_regressions.md).
+   * Alerts fire for changes of 16kb or more.
+ * Computed as:
+   * The size of an APK
+   * With all native code as if it were stored uncompressed.
+   * With all dex code as if it were stored compressed and also extracted.
+   * With all translations as if they were not missing (estimates size of missing translations based on size of english strings).
+     * Without translation-normalization, translation dumps cause jumps.
+
+### Native Code Size Metrics
+
+ * [Telemetry Graph](https://chromeperf.appspot.com/report?sid=3b4a5766fd4cee11679defe471618fff2fb23bd2f46b901b573c10092f8e03cf)
+ * File size of `libchrome.so`
+ * Code vs data sections (.text vs .data + .rodata)
+
+### Java Code Size Metrics
+
+ * [Telemetry Graph](https://chromeperf.appspot.com/report?sid=a9fecfc34518cf942ec7841324d05947670c65a0e4e4016f2fe31a66cb76b844)
+ * File size of `classes.dex`
+ * "Dex": Counts of the number of fields, methods, strings, types.
+ * "DexCache": The number of bytes of RAM required to load our dex file (computed from counts)
+
+### Breakdown Metrics
+
+ * [Telemetry Graph](https://chromeperf.appspot.com/report?sid=eeff03cb0872424f121c3adb897e2815eaa891a1261e270e87c052aee770e1f5)
+ * APK Size: The file size of `Chrome.apk`
+ * Breakdown: The sum of these equals APK Size.
+
+### Install Metrics
+
+ * [Telemetry Graph](https://chromeperf.appspot.com/report?sid=baaba126e7098409c7848647a8cb7c8b101033e3ffdf3420f9d7261cda56a048)
+ * Estimated installed size: How much disk is required to install Chrome on a device.
+   * This is just an estimate, since actual size depends on Android version (e.g. Dex overhead is very different for pre-L vs L+).
+   * Does not include disk used up by profile, etc.
+   * We believe it to be fairly accurate for all L+ devices (less space is required for Dalvik runtime pre-L).
+ * InstallBreakdown: The sum of these equals Estimated installed size.
+
+### Transfer Size Metrics
+
+ * Deflated apk size:
+   * [Telemetry Graph](https://chromeperf.appspot.com/report?sid=f77578280ea9636212c6823b4aa59eb94f785e8de882a25621b59014e36fcf8a)
+   * Only relevant for non-patch updates of Chrome (new installs, or manual app updates)
+ * Patch Size:
+   * [Telemetry Graph](https://chromeperf.appspot.com/report?sid=414b082ff6c4d1841a33dc6b2315e1bfa159f75f85ef93fadf5e4cc46d7ad7da)
+   * Uses [https://github.com/googlesamples/apk-patch-size-estimator](https://github.com/googlesamples/apk-patch-size-estimator)
+   * No longer runs:
+     * Is too slow to be running on the Perf Builder
+     * Was found to be fairly unactionable
+     * Can be run manually: `build/android/resource_sizes.py --estimate-patch-size out/Release/apks/ChromePublic.apk`
+
+### Uncompressed Metrics
+
+ * [Telemetry Graph](https://chromeperf.appspot.com/report?sid=b91f74582d1069c5491956b6c55fc3071e6645ecdec04de8443633a42acaf0ed)
+ * Uncompressed size of classes.dex, locale .pak files, etc
+ * Reported only for things that are compressed within the .apk
diff --git a/docs/speed/speed_tracks.md b/docs/speed/speed_tracks.md
index daf5e27a..3efea41 100644
--- a/docs/speed/speed_tracks.md
+++ b/docs/speed/speed_tracks.md
@@ -71,6 +71,7 @@
  * [Mailing List](://groups.google.com/a/chromium.org/forum/#!forum/binary-size)
  * Performance-Size [Bug
    Queue](https://bugs.chromium.org/p/chromium/issues/list?can=2&q=Performance%3DSize)
+ * Description of [metrics](binary_size/metrics.md)
 
 ### Data Usage
 
diff --git a/extensions/common/features/simple_feature.h b/extensions/common/features/simple_feature.h
index d6df5746..b56a5fa 100644
--- a/extensions/common/features/simple_feature.h
+++ b/extensions/common/features/simple_feature.h
@@ -29,8 +29,6 @@
 
 class FeatureProviderTest;
 class ExtensionAPITest;
-class ManifestUnitTest;
-class SimpleFeatureTest;
 
 class SimpleFeature : public Feature {
  public:
@@ -173,34 +171,10 @@
 
  private:
   friend struct FeatureComparator;
-  friend class SimpleFeatureTest;
   FRIEND_TEST_ALL_PREFIXES(FeatureProviderTest, ManifestFeatureTypes);
   FRIEND_TEST_ALL_PREFIXES(FeatureProviderTest, PermissionFeatureTypes);
   FRIEND_TEST_ALL_PREFIXES(ExtensionAPITest, DefaultConfigurationFeatures);
   FRIEND_TEST_ALL_PREFIXES(FeaturesGenerationTest, FeaturesTest);
-  FRIEND_TEST_ALL_PREFIXES(ManifestUnitTest, Extension);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, Blacklist);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, CommandLineSwitch);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, ComplexFeatureAvailability);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, Context);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, SessionType);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, FeatureValidation);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, HashedIdBlacklist);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, HashedIdWhitelist);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, Inheritance);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, Location);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, ManifestVersion);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, PackageType);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, ParseContexts);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, ParseLocation);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, ParseManifestVersion);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, ParseNull);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, ParsePackageTypes);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, ParsePlatforms);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, ParseWhitelist);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, Platform);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, SimpleFeatureAvailability);
-  FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, Whitelist);
 
   // Holds String to Enum value mappings.
   struct Mappings;
diff --git a/extensions/common/features/simple_feature_unittest.cc b/extensions/common/features/simple_feature_unittest.cc
index 4cbdd500..113233a 100644
--- a/extensions/common/features/simple_feature_unittest.cc
+++ b/extensions/common/features/simple_feature_unittest.cc
@@ -115,8 +115,7 @@
   const HashedExtensionId kIdBar("barabbbbccccddddeeeeffffgggghhhh");
   const HashedExtensionId kIdBaz("bazabbbbccccddddeeeeffffgggghhhh");
   SimpleFeature feature;
-  feature.whitelist_.push_back(kIdFoo.value());
-  feature.whitelist_.push_back(kIdBar.value());
+  feature.set_whitelist({kIdFoo.value().c_str(), kIdBar.value().c_str()});
 
   EXPECT_EQ(
       Feature::IS_AVAILABLE,
@@ -148,7 +147,7 @@
                                  Feature::UNSPECIFIED_PLATFORM)
           .result());
 
-  feature.extension_types_.push_back(Manifest::TYPE_LEGACY_PACKAGED_APP);
+  feature.set_extension_types({Manifest::TYPE_LEGACY_PACKAGED_APP});
   EXPECT_EQ(
       Feature::NOT_FOUND_IN_WHITELIST,
       feature.IsAvailableToManifest(kIdBaz,
@@ -165,7 +164,7 @@
   const std::string kIdFooHashed("55BC7228A0D502A2A48C9BB16B07062A01E62897");
   SimpleFeature feature;
 
-  feature.whitelist_.push_back(kIdFooHashed);
+  feature.set_whitelist({kIdFooHashed.c_str()});
 
   EXPECT_EQ(Feature::IS_AVAILABLE,
             feature
@@ -202,8 +201,7 @@
   const HashedExtensionId kIdBar("barabbbbccccddddeeeeffffgggghhhh");
   const HashedExtensionId kIdBaz("bazabbbbccccddddeeeeffffgggghhhh");
   SimpleFeature feature;
-  feature.blacklist_.push_back(kIdFoo.value());
-  feature.blacklist_.push_back(kIdBar.value());
+  feature.set_blacklist({kIdFoo.value().c_str(), kIdBar.value().c_str()});
 
   EXPECT_EQ(
       Feature::FOUND_IN_BLACKLIST,
@@ -243,7 +241,7 @@
   const std::string kIdFooHashed("55BC7228A0D502A2A48C9BB16B07062A01E62897");
   SimpleFeature feature;
 
-  feature.blacklist_.push_back(kIdFooHashed);
+  feature.set_blacklist({kIdFooHashed.c_str()});
 
   EXPECT_EQ(Feature::FOUND_IN_BLACKLIST,
             feature
@@ -277,8 +275,8 @@
 
 TEST_F(SimpleFeatureTest, PackageType) {
   SimpleFeature feature;
-  feature.extension_types_.push_back(Manifest::TYPE_EXTENSION);
-  feature.extension_types_.push_back(Manifest::TYPE_LEGACY_PACKAGED_APP);
+  feature.set_extension_types(
+      {Manifest::TYPE_EXTENSION, Manifest::TYPE_LEGACY_PACKAGED_APP});
 
   EXPECT_EQ(
       Feature::IS_AVAILABLE,
@@ -314,9 +312,9 @@
 TEST_F(SimpleFeatureTest, Context) {
   SimpleFeature feature;
   feature.set_name("somefeature");
-  feature.contexts_.push_back(Feature::BLESSED_EXTENSION_CONTEXT);
-  feature.extension_types_.push_back(Manifest::TYPE_LEGACY_PACKAGED_APP);
-  feature.platforms_.push_back(Feature::CHROMEOS_PLATFORM);
+  feature.set_contexts({Feature::BLESSED_EXTENSION_CONTEXT});
+  feature.set_extension_types({Manifest::TYPE_LEGACY_PACKAGED_APP});
+  feature.set_platforms({Feature::CHROMEOS_PLATFORM});
   feature.set_min_manifest_version(21);
   feature.set_max_manifest_version(25);
 
@@ -333,14 +331,13 @@
   EXPECT_EQ("", error);
   ASSERT_TRUE(extension.get());
 
-  feature.whitelist_.push_back("monkey");
+  feature.set_whitelist({"monkey"});
   EXPECT_EQ(Feature::NOT_FOUND_IN_WHITELIST, feature.IsAvailableToContext(
       extension.get(), Feature::BLESSED_EXTENSION_CONTEXT,
       Feature::CHROMEOS_PLATFORM).result());
-  feature.whitelist_.clear();
+  feature.set_whitelist({});
 
-  feature.extension_types_.clear();
-  feature.extension_types_.push_back(Manifest::TYPE_THEME);
+  feature.set_extension_types({Manifest::TYPE_THEME});
   {
     Feature::Availability availability = feature.IsAvailableToContext(
         extension.get(), Feature::BLESSED_EXTENSION_CONTEXT,
@@ -351,11 +348,9 @@
               availability.message());
   }
 
-  feature.extension_types_.clear();
-  feature.extension_types_.push_back(Manifest::TYPE_LEGACY_PACKAGED_APP);
-  feature.contexts_.clear();
-  feature.contexts_.push_back(Feature::UNBLESSED_EXTENSION_CONTEXT);
-  feature.contexts_.push_back(Feature::CONTENT_SCRIPT_CONTEXT);
+  feature.set_extension_types({Manifest::TYPE_LEGACY_PACKAGED_APP});
+  feature.set_contexts(
+      {Feature::UNBLESSED_EXTENSION_CONTEXT, Feature::CONTENT_SCRIPT_CONTEXT});
   {
     Feature::Availability availability = feature.IsAvailableToContext(
         extension.get(), Feature::BLESSED_EXTENSION_CONTEXT,
@@ -366,7 +361,9 @@
               availability.message());
   }
 
-  feature.contexts_.push_back(Feature::WEB_PAGE_CONTEXT);
+  feature.set_contexts({Feature::UNBLESSED_EXTENSION_CONTEXT,
+                        Feature::CONTENT_SCRIPT_CONTEXT,
+                        Feature::WEB_PAGE_CONTEXT});
   {
     Feature::Availability availability = feature.IsAvailableToContext(
         extension.get(), Feature::BLESSED_EXTENSION_CONTEXT,
@@ -377,8 +374,7 @@
               availability.message());
   }
 
-  feature.contexts_.clear();
-  feature.contexts_.push_back(Feature::BLESSED_EXTENSION_CONTEXT);
+  feature.set_contexts({Feature::BLESSED_EXTENSION_CONTEXT});
   feature.set_location(SimpleFeature::COMPONENT_LOCATION);
   EXPECT_EQ(Feature::INVALID_LOCATION, feature.IsAvailableToContext(
       extension.get(), Feature::BLESSED_EXTENSION_CONTEXT,
@@ -389,9 +385,6 @@
       extension.get(), Feature::BLESSED_EXTENSION_CONTEXT,
       Feature::UNSPECIFIED_PLATFORM).result());
 
-  feature.contexts_.clear();
-  feature.contexts_.push_back(Feature::BLESSED_EXTENSION_CONTEXT);
-
   {
     Feature::Availability availability = feature.IsAvailableToContext(
         extension.get(), Feature::LOCK_SCREEN_EXTENSION_CONTEXT,
@@ -403,8 +396,7 @@
         availability.message());
   }
 
-  feature.contexts_.clear();
-  feature.contexts_.push_back(Feature::LOCK_SCREEN_EXTENSION_CONTEXT);
+  feature.set_contexts({Feature::LOCK_SCREEN_EXTENSION_CONTEXT});
 
   EXPECT_EQ(Feature::IS_AVAILABLE,
             feature
@@ -589,7 +581,7 @@
 
 TEST_F(SimpleFeatureTest, Platform) {
   SimpleFeature feature;
-  feature.platforms_.push_back(Feature::CHROMEOS_PLATFORM);
+  feature.set_platforms({Feature::CHROMEOS_PLATFORM});
   EXPECT_EQ(Feature::IS_AVAILABLE,
             feature
                 .IsAvailableToManifest(
@@ -793,11 +785,11 @@
   std::unique_ptr<ComplexFeature> complex_feature;
   {
     std::unique_ptr<SimpleFeature> feature1(new SimpleFeature());
-    feature1->channel_.reset(new Channel(Channel::BETA));
-    feature1->extension_types_.push_back(Manifest::TYPE_EXTENSION);
+    feature1->set_channel(Channel::BETA);
+    feature1->set_extension_types({Manifest::TYPE_EXTENSION});
     std::unique_ptr<SimpleFeature> feature2(new SimpleFeature());
-    feature2->channel_.reset(new Channel(Channel::BETA));
-    feature2->extension_types_.push_back(Manifest::TYPE_LEGACY_PACKAGED_APP);
+    feature2->set_channel(Channel::BETA);
+    feature2->set_extension_types({Manifest::TYPE_LEGACY_PACKAGED_APP});
     std::vector<Feature*> list;
     list.push_back(feature1.release());
     list.push_back(feature2.release());
@@ -847,12 +839,12 @@
   {
     // Rule: "extension", channel trunk.
     std::unique_ptr<SimpleFeature> feature1(new SimpleFeature());
-    feature1->channel_.reset(new Channel(Channel::UNKNOWN));
-    feature1->extension_types_.push_back(Manifest::TYPE_EXTENSION);
+    feature1->set_channel(Channel::UNKNOWN);
+    feature1->set_extension_types({Manifest::TYPE_EXTENSION});
     std::unique_ptr<SimpleFeature> feature2(new SimpleFeature());
     // Rule: "legacy_packaged_app", channel stable.
-    feature2->channel_.reset(new Channel(Channel::STABLE));
-    feature2->extension_types_.push_back(Manifest::TYPE_LEGACY_PACKAGED_APP);
+    feature2->set_channel(Channel::STABLE);
+    feature2->set_extension_types({Manifest::TYPE_LEGACY_PACKAGED_APP});
     std::vector<Feature*> list;
     list.push_back(feature1.release());
     list.push_back(feature2.release());
diff --git a/extensions/shell/installer/linux/BUILD.gn b/extensions/shell/installer/linux/BUILD.gn
index 8bda433..f73353f 100644
--- a/extensions/shell/installer/linux/BUILD.gn
+++ b/extensions/shell/installer/linux/BUILD.gn
@@ -38,7 +38,7 @@
   deps = [
     "//extensions/shell:app_shell",
   ]
-  script = "//chrome/installer/linux/strip_chrome_binary.py"
+  script = "//build/gn_run_binary.py"
   sources = [
     prog_name,
   ]
@@ -48,9 +48,11 @@
   ]
   args = [
     rebase_path("//third_party/eu-strip/bin/eu-strip", root_build_dir),
-    rebase_path(prog_name, root_build_dir),
-    rebase_path(debug_file, root_build_dir),
+    "-o",
     rebase_path(stripped_file, root_build_dir),
+    "-f",
+    rebase_path(debug_file, root_build_dir),
+    rebase_path(prog_name, root_build_dir),
   ]
 }
 
@@ -176,7 +178,7 @@
   deb_target_name = "${target_name}_deb"
   action(deb_target_name) {
     visibility = [ ":*" ]
-    script = "//chrome/installer/linux/make_package.py"
+    script = "//build/gn_run_binary.py"
     deb_arch = "amd64"
 
     inputs = packaging_files_binaries
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 3863f75..f67772bb 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -1742,7 +1742,8 @@
 }
 
 - (void)displayCurrentBVC {
-  self.mainViewController.activeViewController = self.currentBVC;
+  [self.mainViewController setActiveViewController:self.currentBVC
+                                        completion:nil];
 }
 
 - (TabModel*)currentTabModel {
@@ -1806,12 +1807,14 @@
                                             mainBVC:self.mainBVC
                                              otrBVC:self.otrBVC];
     [_tabSwitcherController setTransitionContext:transitionContext];
-    self.mainViewController.activeViewController = _tabSwitcherController;
+    [self.mainViewController setActiveViewController:_tabSwitcherController
+                                          completion:nil];
     [_tabSwitcherController showWithSelectedTabAnimation];
   } else {
     // User interaction is disabled when the stack controller is dismissed.
     [[_tabSwitcherController view] setUserInteractionEnabled:YES];
-    self.mainViewController.activeViewController = _tabSwitcherController;
+    [self.mainViewController setActiveViewController:_tabSwitcherController
+                                          completion:nil];
     [_tabSwitcherController showWithSelectedTabAnimation];
   }
 }
diff --git a/ios/chrome/browser/ui/main/BUILD.gn b/ios/chrome/browser/ui/main/BUILD.gn
index 6034402..0cbf92d 100644
--- a/ios/chrome/browser/ui/main/BUILD.gn
+++ b/ios/chrome/browser/ui/main/BUILD.gn
@@ -14,6 +14,7 @@
     "main_view_controller.mm",
   ]
   deps = [
+    ":feature_flags",
     "//base",
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
@@ -54,13 +55,16 @@
     "main_view_controller_unittest.mm",
   ]
   deps = [
+    ":feature_flags",
     ":main",
     "//base",
+    "//base/test:test_support",
     "//components/bookmarks/test",
     "//ios/chrome/browser/bookmarks",
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/ui:ui_internal",
+    "//ios/chrome/test:block_cleanup_test",
     "//ios/web/public/test",
     "//testing/gtest",
   ]
diff --git a/ios/chrome/browser/ui/main/main_view_controller.h b/ios/chrome/browser/ui/main/main_view_controller.h
index 0bb7d13f..933446e0 100644
--- a/ios/chrome/browser/ui/main/main_view_controller.h
+++ b/ios/chrome/browser/ui/main/main_view_controller.h
@@ -7,12 +7,21 @@
 
 #import <UIKit/UIKit.h>
 
+#import "base/ios/block_types.h"
+
 // A UIViewController that provides a property to maintain a single child view
 // controller.
 @interface MainViewController : UIViewController
-// The child view controller, if any, that is active. Assigning to
-// |activeViewController| will remove any previous active view controller.
-@property(nonatomic, strong) UIViewController* activeViewController;
+
+// The view controller, if any, that is active.
+@property(nonatomic, readonly, strong) UIViewController* activeViewController;
+
+// Sets the active view controller, replacing any previously-active view
+// controller.  The |completion| block is invoked once the new view controller
+// is presented or added as a child.
+- (void)setActiveViewController:(UIViewController*)activeViewController
+                     completion:(ProceduralBlock)completion;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_MAIN_MAIN_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/main/main_view_controller.mm b/ios/chrome/browser/ui/main/main_view_controller.mm
index 10f8247..0b78ccb 100644
--- a/ios/chrome/browser/ui/main/main_view_controller.mm
+++ b/ios/chrome/browser/ui/main/main_view_controller.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/ui/main/main_view_controller.h"
 
 #import "base/logging.h"
+#include "ios/chrome/browser/ui/main/main_feature_flags.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -13,10 +14,15 @@
 @implementation MainViewController
 
 - (UIViewController*)activeViewController {
-  return [self.childViewControllers firstObject];
+  if (TabSwitcherPresentsBVCEnabled()) {
+    return self.presentedViewController;
+  } else {
+    return [self.childViewControllers firstObject];
+  }
 }
 
-- (void)setActiveViewController:(UIViewController*)activeViewController {
+- (void)setActiveViewController:(UIViewController*)activeViewController
+                     completion:(void (^)())completion {
   DCHECK(activeViewController);
   if (self.activeViewController == activeViewController)
     return;
@@ -24,27 +30,54 @@
   // TODO(crbug.com/546189): DCHECK here that there isn't a modal view
   // controller showing once the known violations of that are fixed.
 
-  // Remove the current active view controller, if any.
-  if (self.activeViewController) {
-    [self.activeViewController willMoveToParentViewController:nil];
-    [self.activeViewController.view removeFromSuperview];
-    [self.activeViewController removeFromParentViewController];
+  if (TabSwitcherPresentsBVCEnabled()) {
+    if (self.activeViewController) {
+      // This call must be to super, as the override of
+      // dismissViewControllerAnimated:completion: below tries to dismiss using
+      // self.activeViewController, but this call explicitly needs to present
+      // using the MainViewController itself.
+      [super dismissViewControllerAnimated:NO completion:nil];
+    }
+
+    // This call must be to super, as the override of
+    // presentViewController:animated:completion: below tries to present using
+    // self.activeViewController, but this call explicitly needs to present
+    // using the MainViewController itself.
+    [super
+        presentViewController:activeViewController
+                     animated:NO
+                   completion:^{
+                     DCHECK(self.activeViewController == activeViewController);
+                     if (completion) {
+                       completion();
+                     }
+                   }];
+  } else {
+    // Remove the current active view controller, if any.
+    if (self.activeViewController) {
+      [self.activeViewController willMoveToParentViewController:nil];
+      [self.activeViewController.view removeFromSuperview];
+      [self.activeViewController removeFromParentViewController];
+    }
+
+    DCHECK(self.activeViewController == nil);
+    DCHECK(self.view.subviews.count == 0);
+
+    // Add the new active view controller.
+    [self addChildViewController:activeViewController];
+    self.activeViewController.view.frame = self.view.bounds;
+    [self.view addSubview:self.activeViewController.view];
+    [activeViewController didMoveToParentViewController:self];
+
+    // Let the system know that the child has changed so appearance updates can
+    // be made.
+    [self setNeedsStatusBarAppearanceUpdate];
+
+    DCHECK(self.activeViewController == activeViewController);
+    if (completion) {
+      completion();
+    }
   }
-
-  DCHECK(self.activeViewController == nil);
-  DCHECK(self.view.subviews.count == 0);
-
-  // Add the new active view controller.
-  [self addChildViewController:activeViewController];
-  self.activeViewController.view.frame = self.view.bounds;
-  [self.view addSubview:self.activeViewController.view];
-  [activeViewController didMoveToParentViewController:self];
-
-  // Let the system know that the child has changed so appearance updates can
-  // be made.
-  [self setNeedsStatusBarAppearanceUpdate];
-
-  DCHECK(self.activeViewController == activeViewController);
 }
 
 #pragma mark - UIViewController methods
diff --git a/ios/chrome/browser/ui/main/main_view_controller_unittest.mm b/ios/chrome/browser/ui/main/main_view_controller_unittest.mm
index 1efca18..72a4d41 100644
--- a/ios/chrome/browser/ui/main/main_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/main/main_view_controller_unittest.mm
@@ -6,6 +6,10 @@
 
 #import <UIKit/UIKit.h>
 
+#import "base/test/ios/wait_util.h"
+#include "base/test/scoped_feature_list.h"
+#include "ios/chrome/browser/ui/main/main_feature_flags.h"
+#import "ios/chrome/test/block_cleanup_test.h"
 #include "testing/gtest_mac.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -52,7 +56,40 @@
 
 namespace {
 
-TEST(MainViewControllerTest, ActiveVC) {
+class MainViewControllerTest : public BlockCleanupTest {
+ public:
+  MainViewControllerTest() = default;
+  ~MainViewControllerTest() override = default;
+
+  void TearDown() override {
+    if (original_root_view_controller_) {
+      [[UIApplication sharedApplication] keyWindow].rootViewController =
+          original_root_view_controller_;
+      original_root_view_controller_ = nil;
+    }
+  }
+
+  // Sets the current key window's rootViewController and saves a pointer to
+  // the original VC to allow restoring it at the end of the test.
+  void SetRootViewController(UIViewController* new_root_view_controller) {
+    original_root_view_controller_ =
+        [[[UIApplication sharedApplication] keyWindow] rootViewController];
+    [[UIApplication sharedApplication] keyWindow].rootViewController =
+        new_root_view_controller;
+  }
+
+ private:
+  // The key window's original root view controller, which must be restored at
+  // the end of the test.
+  UIViewController* original_root_view_controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(MainViewControllerTest);
+};
+
+TEST_F(MainViewControllerTest, ActiveVC) {
+  base::test::ScopedFeatureList disable_feature;
+  disable_feature.InitAndDisableFeature(kTabSwitcherPresentsBVC);
+
   MainViewController* main_view_controller = [[MainViewController alloc] init];
   UIViewController* child_view_controller_1 = [[UIViewController alloc] init];
   UIViewController* child_view_controller_2 = [[UIViewController alloc] init];
@@ -71,7 +108,10 @@
             [main_view_controller activeViewController]);
 }
 
-TEST(MainViewControllerTest, SetActiveVC) {
+TEST_F(MainViewControllerTest, SetActiveVCWithContainment) {
+  base::test::ScopedFeatureList disable_feature;
+  disable_feature.InitAndDisableFeature(kTabSwitcherPresentsBVC);
+
   MainViewController* main_view_controller = [[MainViewController alloc] init];
   CGRect windowRect = CGRectMake(0, 0, 200, 200);
   [main_view_controller view].frame = windowRect;
@@ -80,7 +120,8 @@
       [[MockViewController alloc] init];
   [child_view_controller_1 view].frame = CGRectMake(0, 0, 10, 10);
 
-  [main_view_controller setActiveViewController:child_view_controller_1];
+  [main_view_controller setActiveViewController:child_view_controller_1
+                                     completion:nil];
   EXPECT_EQ(child_view_controller_1,
             [[main_view_controller childViewControllers] firstObject]);
   EXPECT_EQ([child_view_controller_1 view],
@@ -97,7 +138,8 @@
             [child_view_controller_1 willMoveToParentViewControllerArgument]);
 
   UIViewController* child_view_controller_2 = [[UIViewController alloc] init];
-  [main_view_controller setActiveViewController:child_view_controller_2];
+  [main_view_controller setActiveViewController:child_view_controller_2
+                                     completion:nil];
   EXPECT_EQ(child_view_controller_2,
             [[main_view_controller childViewControllers] firstObject]);
   EXPECT_EQ(1U, [[main_view_controller childViewControllers] count]);
@@ -112,18 +154,18 @@
             [child_view_controller_1 didMoveToParentViewControllerArgument]);
 }
 
-TEST(MainViewControllerTest, StatusBar) {
+TEST_F(MainViewControllerTest, StatusBar) {
+  base::test::ScopedFeatureList disable_feature;
+  disable_feature.InitAndDisableFeature(kTabSwitcherPresentsBVC);
+
   MainViewController* main_view_controller = [[MainViewController alloc] init];
-  // MVC needs to be the root view controller for this to work, so save the
-  // current one and restore it at the end of the test.
-  UIViewController* root_view_controller =
-      [[[UIApplication sharedApplication] keyWindow] rootViewController];
-  [[UIApplication sharedApplication] keyWindow].rootViewController =
-      main_view_controller;
+  SetRootViewController(main_view_controller);
+
   UIViewController* status_bar_visible_view_controller =
       [[UIViewController alloc] init];
   [main_view_controller
-      setActiveViewController:status_bar_visible_view_controller];
+      setActiveViewController:status_bar_visible_view_controller
+                   completion:nil];
   // Check if the status bar is hidden by testing if the status bar rect is
   // CGRectZero.
   EXPECT_FALSE(CGRectEqualToRect(
@@ -131,13 +173,101 @@
   HiddenStatusBarViewController* status_bar_hidden_view_controller =
       [[HiddenStatusBarViewController alloc] init];
   [main_view_controller
-      setActiveViewController:status_bar_hidden_view_controller];
+      setActiveViewController:status_bar_hidden_view_controller
+                   completion:nil];
   EXPECT_EQ([main_view_controller childViewControllerForStatusBarHidden],
             status_bar_hidden_view_controller);
   EXPECT_TRUE(CGRectEqualToRect(
       [UIApplication sharedApplication].statusBarFrame, CGRectZero));
-  [[UIApplication sharedApplication] keyWindow].rootViewController =
-      root_view_controller;
+}
+
+// Tests that completion handlers are called properly after the new view
+// controller is made active, when using containment
+TEST_F(MainViewControllerTest, CompletionHandlersWithContainment) {
+  base::test::ScopedFeatureList disable_feature;
+  disable_feature.InitAndDisableFeature(kTabSwitcherPresentsBVC);
+
+  MainViewController* main_view_controller = [[MainViewController alloc] init];
+  CGRect windowRect = CGRectMake(0, 0, 200, 200);
+  [main_view_controller view].frame = windowRect;
+
+  UIViewController* child_view_controller_1 = [[UIViewController alloc] init];
+  [child_view_controller_1 view].frame = CGRectMake(0, 0, 10, 10);
+
+  UIViewController* child_view_controller_2 = [[UIViewController alloc] init];
+  [child_view_controller_2 view].frame = CGRectMake(20, 20, 10, 10);
+
+  // Tests that the completion handler is called when there is no preexisting
+  // active view controller.
+  __block BOOL completion_handler_was_called = false;
+  [main_view_controller setActiveViewController:child_view_controller_1
+                                     completion:^{
+                                       completion_handler_was_called = YES;
+                                     }];
+  base::test::ios::WaitUntilCondition(^bool() {
+    return completion_handler_was_called;
+  });
+  ASSERT_TRUE(completion_handler_was_called);
+
+  // Tests that the completion handler is called when replacing an existing
+  // active view controller.
+  completion_handler_was_called = NO;
+  [main_view_controller setActiveViewController:child_view_controller_2
+                                     completion:^{
+                                       completion_handler_was_called = YES;
+                                     }];
+  base::test::ios::WaitUntilCondition(^bool() {
+    return completion_handler_was_called;
+  });
+  ASSERT_TRUE(completion_handler_was_called);
+}
+
+// Tests that setting the active view controller work and that completion
+// handlers are called properly after the new view controller is made active,
+// when using presentation.
+TEST_F(MainViewControllerTest,
+       SetActiveVCAndCompletionHandlersWithPresentation) {
+  base::test::ScopedFeatureList enable_feature;
+  enable_feature.InitAndEnableFeature(kTabSwitcherPresentsBVC);
+
+  MainViewController* main_view_controller = [[MainViewController alloc] init];
+  SetRootViewController(main_view_controller);
+  CGRect windowRect = CGRectMake(0, 0, 200, 200);
+  [main_view_controller view].frame = windowRect;
+
+  UIViewController* child_view_controller_1 = [[UIViewController alloc] init];
+  [child_view_controller_1 view].frame = CGRectMake(0, 0, 10, 10);
+
+  UIViewController* child_view_controller_2 = [[UIViewController alloc] init];
+  [child_view_controller_2 view].frame = CGRectMake(20, 20, 10, 10);
+
+  // Tests that the completion handler is called when there is no preexisting
+  // active view controller.
+  __block BOOL completion_handler_was_called = false;
+  [main_view_controller setActiveViewController:child_view_controller_1
+                                     completion:^{
+                                       completion_handler_was_called = YES;
+                                     }];
+  base::test::ios::WaitUntilCondition(^bool() {
+    return completion_handler_was_called;
+  });
+  ASSERT_TRUE(completion_handler_was_called);
+  EXPECT_EQ(main_view_controller.presentedViewController,
+            child_view_controller_1);
+
+  // Tests that the completion handler is called when replacing an existing
+  // active view controller.
+  completion_handler_was_called = NO;
+  [main_view_controller setActiveViewController:child_view_controller_2
+                                     completion:^{
+                                       completion_handler_was_called = YES;
+                                     }];
+  base::test::ios::WaitUntilCondition(^bool() {
+    return completion_handler_was_called;
+  });
+  ASSERT_TRUE(completion_handler_was_called);
+  EXPECT_EQ(main_view_controller.presentedViewController,
+            child_view_controller_2);
 }
 
 }  // namespace
diff --git a/ios/web/public/test/fakes/test_web_state.h b/ios/web/public/test/fakes/test_web_state.h
index c2f9316a..b134f58 100644
--- a/ios/web/public/test/fakes/test_web_state.h
+++ b/ios/web/public/test/fakes/test_web_state.h
@@ -54,6 +54,7 @@
   const base::string16& GetTitle() const override;
   bool IsLoading() const override;
   double GetLoadingProgress() const override;
+  bool IsVisible() const override;
   bool IsCrashed() const override;
   bool IsEvicted() const override;
   bool IsBeingDestroyed() const override;
@@ -109,6 +110,7 @@
   BrowserState* browser_state_;
   bool web_usage_enabled_;
   bool is_loading_;
+  bool is_visible_;
   bool is_crashed_;
   bool is_evicted_;
   CRWContentView* transient_content_view_;
diff --git a/ios/web/public/test/fakes/test_web_state.mm b/ios/web/public/test/fakes/test_web_state.mm
index bec1c09b..cbe3ac7a 100644
--- a/ios/web/public/test/fakes/test_web_state.mm
+++ b/ios/web/public/test/fakes/test_web_state.mm
@@ -31,6 +31,7 @@
     : browser_state_(nullptr),
       web_usage_enabled_(false),
       is_loading_(false),
+      is_visible_(false),
       is_crashed_(false),
       is_evicted_(false),
       trust_level_(kAbsolute),
@@ -74,11 +75,13 @@
 }
 
 void TestWebState::WasShown() {
+  is_visible_ = true;
   for (auto& observer : observers_)
     observer.WasShown();
 }
 
 void TestWebState::WasHidden() {
+  is_visible_ = false;
   for (auto& observer : observers_)
     observer.WasHidden();
 }
@@ -186,6 +189,10 @@
   return 0.0;
 }
 
+bool TestWebState::IsVisible() const {
+  return is_visible_;
+}
+
 bool TestWebState::IsCrashed() const {
   return is_crashed_;
 }
diff --git a/ios/web/public/web_state/web_state.h b/ios/web/public/web_state/web_state.h
index 9a7b619..a2649f3 100644
--- a/ios/web/public/web_state/web_state.h
+++ b/ios/web/public/web_state/web_state.h
@@ -187,6 +187,10 @@
   // (nothing loaded) and 1.0 (fully loaded).
   virtual double GetLoadingProgress() const = 0;
 
+  // Whether the WebState is visible. Returns true after WasShown() call and
+  // false after WasHidden() call.
+  virtual bool IsVisible() const = 0;
+
   // Returns true if the web process backing this WebState is believed to
   // currently be crashed.
   virtual bool IsCrashed() const = 0;
diff --git a/ios/web/web_state/ui/crw_web_controller.h b/ios/web/web_state/ui/crw_web_controller.h
index ae95ad3..6050fd63 100644
--- a/ios/web/web_state/ui/crw_web_controller.h
+++ b/ios/web/web_state/ui/crw_web_controller.h
@@ -103,6 +103,10 @@
 // YES if the web process backing WebView is believed to currently be crashed.
 @property(nonatomic, assign, getter=isWebProcessCrashed) BOOL webProcessCrashed;
 
+// Whether the WebController is visible. Returns YES after wasShown call and
+// NO after wasHidden() call.
+@property(nonatomic, assign, getter=isVisible) BOOL visible;
+
 // Designated initializer. Initializes web controller with |webState|. The
 // calling code must retain the ownership of |webState|.
 - (instancetype)initWithWebState:(web::WebStateImpl*)webState;
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 0bbfe75..e68d1fd9 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -923,6 +923,7 @@
 @synthesize loadPhase = _loadPhase;
 @synthesize shouldSuppressDialogs = _shouldSuppressDialogs;
 @synthesize webProcessCrashed = _webProcessCrashed;
+@synthesize visible = _visible;
 @synthesize nativeProvider = _nativeProvider;
 @synthesize swipeRecognizerProvider = _swipeRecognizerProvider;
 @synthesize webViewProxy = _webViewProxy;
@@ -2769,12 +2770,14 @@
 }
 
 - (void)wasShown {
+  self.visible = YES;
   if ([self.nativeController respondsToSelector:@selector(wasShown)]) {
     [self.nativeController wasShown];
   }
 }
 
 - (void)wasHidden {
+  self.visible = NO;
   if (_isHalted)
     return;
   [self recordStateInHistory];
diff --git a/ios/web/web_state/web_state_impl.h b/ios/web/web_state/web_state_impl.h
index 2875261b..c50022f40 100644
--- a/ios/web/web_state/web_state_impl.h
+++ b/ios/web/web_state/web_state_impl.h
@@ -211,6 +211,7 @@
   bool IsLoading() const override;
   double GetLoadingProgress() const override;
   bool IsCrashed() const override;
+  bool IsVisible() const override;
   bool IsEvicted() const override;
   bool IsBeingDestroyed() const override;
   const GURL& GetVisibleURL() const override;
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm
index a43cc33..c59f4987 100644
--- a/ios/web/web_state/web_state_impl.mm
+++ b/ios/web/web_state/web_state_impl.mm
@@ -244,6 +244,10 @@
   return [web_controller_ isWebProcessCrashed];
 }
 
+bool WebStateImpl::IsVisible() const {
+  return [web_controller_ isVisible];
+}
+
 bool WebStateImpl::IsEvicted() const {
   return ![web_controller_ isViewAlive];
 }
diff --git a/ios/web/web_state/web_state_impl_unittest.mm b/ios/web/web_state/web_state_impl_unittest.mm
index 7b68b31..f76d71f 100644
--- a/ios/web/web_state/web_state_impl_unittest.mm
+++ b/ios/web/web_state/web_state_impl_unittest.mm
@@ -266,16 +266,20 @@
   EXPECT_EQ(web_state_.get(), observer->web_state());
 
   // Test that WasShown() is called.
+  ASSERT_FALSE(web_state_->IsVisible());
   ASSERT_FALSE(observer->was_shown_info());
   web_state_->WasShown();
   ASSERT_TRUE(observer->was_shown_info());
   EXPECT_EQ(web_state_.get(), observer->was_shown_info()->web_state);
+  EXPECT_TRUE(web_state_->IsVisible());
 
   // Test that WasHidden() is called.
+  ASSERT_TRUE(web_state_->IsVisible());
   ASSERT_FALSE(observer->was_hidden_info());
   web_state_->WasHidden();
   ASSERT_TRUE(observer->was_hidden_info());
   EXPECT_EQ(web_state_.get(), observer->was_hidden_info()->web_state);
+  EXPECT_FALSE(web_state_->IsVisible());
 
   // Test that LoadProgressChanged() is called.
   ASSERT_FALSE(observer->change_loading_progress_info());
diff --git a/media/midi/task_service.cc b/media/midi/task_service.cc
index 664f309..a3f97791 100644
--- a/media/midi/task_service.cc
+++ b/media/midi/task_service.cc
@@ -6,6 +6,7 @@
 
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
 
 namespace midi {
 
@@ -139,10 +140,13 @@
   if (!threads_[thread]) {
     threads_[thread] = base::MakeUnique<base::Thread>(
         base::StringPrintf("MidiService_TaskService_Thread(%zu)", runner_id));
+    base::Thread::Options options;
 #if defined(OS_WIN)
     threads_[thread]->init_com_with_mta(true);
+#elif defined(OS_MACOSX)
+    options.message_loop_type = base::MessageLoop::TYPE_UI;
 #endif
-    threads_[thread]->Start();
+    threads_[thread]->StartWithOptions(options);
   }
   return threads_[thread]->task_runner();
 }
diff --git a/net/cert/internal/ocsp.cc b/net/cert/internal/ocsp.cc
index d24743f..816b44fd9 100644
--- a/net/cert/internal/ocsp.cc
+++ b/net/cert/internal/ocsp.cc
@@ -154,14 +154,6 @@
   return !parser.HasMore();
 }
 
-// DER bytes for a SHA1 AlgorithmIdentifier.
-//
-//     SEQUENCE (2 elem)
-//         OBJECT IDENTIFIER  1.3.14.3.2.26
-//         NULL
-const uint8_t kSha1HashAlgorithm[] = {0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E,
-                                      0x03, 0x02, 0x1A, 0x05, 0x00};
-
 // Writes the hash of |value| as an OCTET STRING to |cbb|, using |hash_type| as
 // the algorithm. Returns true on success.
 bool AppendHashAsOctetString(const EVP_MD* hash_type,
@@ -902,17 +894,16 @@
   //       serialNumber        CertificateSerialNumber }
 
   // TODO(eroman): Don't use SHA1.
-  if (!CBB_add_bytes(&req_cert, kSha1HashAlgorithm,
-                     arraysize(kSha1HashAlgorithm))) {
+  const EVP_MD* md = EVP_sha1();
+  if (!EVP_marshal_digest_algorithm(&req_cert, md))
     return false;
-  }
 
-  AppendHashAsOctetString(EVP_sha1(), &req_cert, issuer->tbs().issuer_tlv);
+  AppendHashAsOctetString(md, &req_cert, issuer->tbs().issuer_tlv);
 
   der::Input key_tlv;
   if (!GetSubjectPublicKeyBytes(issuer->tbs().spki_tlv, &key_tlv))
     return false;
-  AppendHashAsOctetString(EVP_sha1(), &req_cert, key_tlv);
+  AppendHashAsOctetString(md, &req_cert, key_tlv);
 
   CBB serial_number;
   if (!CBB_add_asn1(&req_cert, &serial_number, CBS_ASN1_INTEGER))
diff --git a/net/cert/internal/signature_algorithm.cc b/net/cert/internal/signature_algorithm.cc
index 17c5783..e2841a41 100644
--- a/net/cert/internal/signature_algorithm.cc
+++ b/net/cert/internal/signature_algorithm.cc
@@ -14,6 +14,8 @@
 #include "net/der/input.h"
 #include "net/der/parse_values.h"
 #include "net/der/parser.h"
+#include "third_party/boringssl/src/include/openssl/bytestring.h"
+#include "third_party/boringssl/src/include/openssl/digest.h"
 
 namespace net {
 
@@ -154,45 +156,6 @@
 
 // From RFC 5912:
 //
-//     id-sha1 OBJECT IDENTIFIER ::= {
-//      iso(1) identified-organization(3) oiw(14) secsig(3)
-//      algorithm(2) 26 }
-//
-// In dotted notation: 1.3.14.3.2.26
-const uint8_t kOidSha1[] = {0x2B, 0x0E, 0x03, 0x02, 0x1A};
-
-// From RFC 5912:
-//
-//     id-sha256  OBJECT IDENTIFIER  ::=
-//         { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101)
-//         csor(3) nistAlgorithms(4) hashalgs(2) 1 }
-//
-// In dotted notation: 2.16.840.1.101.3.4.2.1
-const uint8_t kOidSha256[] = {0x60, 0x86, 0x48, 0x01, 0x65,
-                              0x03, 0x04, 0x02, 0x01};
-
-// From RFC 5912:
-//
-//     id-sha384  OBJECT IDENTIFIER  ::=
-//         { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101)
-//         csor(3) nistAlgorithms(4) hashalgs(2) 2 }
-//
-// In dotted notation: 2.16.840.1.101.3.4.2.2
-const uint8_t kOidSha384[] = {0x60, 0x86, 0x48, 0x01, 0x65,
-                              0x03, 0x04, 0x02, 0x02};
-
-// From RFC 5912:
-//
-//     id-sha512  OBJECT IDENTIFIER  ::=
-//         { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101)
-//         csor(3) nistAlgorithms(4) hashalgs(2) 3 }
-//
-// In dotted notation: 2.16.840.1.101.3.4.2.3
-const uint8_t kOidSha512[] = {0x60, 0x86, 0x48, 0x01, 0x65,
-                              0x03, 0x04, 0x02, 0x03};
-
-// From RFC 5912:
-//
 //     id-mgf1  OBJECT IDENTIFIER  ::=  { pkcs-1 8 }
 //
 // In dotted notation: 1.2.840.113549.1.1.8
@@ -554,33 +517,24 @@
 
 WARN_UNUSED_RESULT bool ParseHashAlgorithm(const der::Input& input,
                                            DigestAlgorithm* out) {
-  der::Input oid;
-  der::Input params;
-  if (!ParseAlgorithmIdentifier(input, &oid, &params))
-    return false;
+  CBS cbs;
+  CBS_init(&cbs, input.UnsafeData(), input.Length());
+  const EVP_MD* md = EVP_parse_digest_algorithm(&cbs);
 
-  DigestAlgorithm hash;
-
-  if (oid == der::Input(kOidSha1)) {
-    hash = DigestAlgorithm::Sha1;
-  } else if (oid == der::Input(kOidSha256)) {
-    hash = DigestAlgorithm::Sha256;
-  } else if (oid == der::Input(kOidSha384)) {
-    hash = DigestAlgorithm::Sha384;
-  } else if (oid == der::Input(kOidSha512)) {
-    hash = DigestAlgorithm::Sha512;
+  if (md == EVP_sha1()) {
+    *out = DigestAlgorithm::Sha1;
+  } else if (md == EVP_sha256()) {
+    *out = DigestAlgorithm::Sha256;
+  } else if (md == EVP_sha384()) {
+    *out = DigestAlgorithm::Sha384;
+  } else if (md == EVP_sha512()) {
+    *out = DigestAlgorithm::Sha512;
   } else {
     // TODO(eroman): Support MD2, MD4, MD5 for completeness?
     // Unsupported digest algorithm.
     return false;
   }
 
-  // From RFC 5912: "PARAMS TYPE NULL ARE preferredPresent". Which is to say
-  // the can either be absent, or NULL.
-  if (!IsEmpty(params) && !IsNull(params))
-    return false;
-
-  *out = hash;
   return true;
 }
 
diff --git a/net/data/ocsp_unittest/make_ocsp.py b/net/data/ocsp_unittest/make_ocsp.py
index 996fbb3..426674d 100755
--- a/net/data/ocsp_unittest/make_ocsp.py
+++ b/net/data/ocsp_unittest/make_ocsp.py
@@ -339,6 +339,13 @@
     'Uses byName to identify the signer',
     CA,
     Create(responder=GetName(CA)))
+
+# TODO(eroman): pyasn1 module has a bug in rfc2560.ResponderID() that will use
+# IMPLICIT rather than EXPLICIT tagging for byKey
+# (https://github.com/etingof/pyasn1-modules/issues/8). If using an affected
+# version of the library you will need to patch pyasn1_modules/rfc2560.py and
+# replace "implicitTag" with "explicitTag" in ResponderID to generate this
+# test data correctly.
 Store(
     'responder_id',
     'Uses byKey to identify the signer',
diff --git a/net/test/android/javatests/src/org/chromium/net/test/EmbeddedTestServer.java b/net/test/android/javatests/src/org/chromium/net/test/EmbeddedTestServer.java
index 153760e..4318a70 100644
--- a/net/test/android/javatests/src/org/chromium/net/test/EmbeddedTestServer.java
+++ b/net/test/android/javatests/src/org/chromium/net/test/EmbeddedTestServer.java
@@ -475,4 +475,16 @@
         }
         destroy();
     }
+
+    /** Get the path of the PEM file of the root cert. */
+    public String getRootCertPemPath() {
+        try {
+            synchronized (mImplMonitor) {
+                checkServiceLocked();
+                return mImpl.getRootCertPemPath();
+            }
+        } catch (RemoteException e) {
+            throw new EmbeddedTestServerFailure("Failed to get root cert's path", e);
+        }
+    }
 }
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index fdde177..b3eec8f 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -1038,16 +1038,14 @@
 FPDF_BOOL PDFiumEngine::IsDataAvail(FX_FILEAVAIL* param,
                                     size_t offset,
                                     size_t size) {
-  PDFiumEngine::FileAvail* file_avail =
-      static_cast<PDFiumEngine::FileAvail*>(param);
+  auto* file_avail = static_cast<FileAvail*>(param);
   return file_avail->engine->doc_loader_->IsDataAvailable(offset, size);
 }
 
 void PDFiumEngine::AddSegment(FX_DOWNLOADHINTS* param,
                               size_t offset,
                               size_t size) {
-  PDFiumEngine::DownloadHints* download_hints =
-      static_cast<PDFiumEngine::DownloadHints*>(param);
+  auto* download_hints = static_cast<DownloadHints*>(param);
   return download_hints->engine->doc_loader_->RequestData(offset, size);
 }
 
@@ -1105,7 +1103,6 @@
   pp::Rect leftover = rect;
   for (size_t i = 0; i < visible_pages_.size(); ++i) {
     int index = visible_pages_[i];
-    pp::Rect page_rect = pages_[index]->rect();
     // Convert the current page's rectangle to screen rectangle.  We do this
     // instead of the reverse (converting the dirty rectangle from screen to
     // page coordinates) because then we'd have to convert back to screen
@@ -1444,9 +1441,9 @@
 }
 
 uint32_t PDFiumEngine::QuerySupportedPrintOutputFormats() {
-  if (HasPermission(PDFEngine::PERMISSION_PRINT_HIGH_QUALITY))
+  if (HasPermission(PERMISSION_PRINT_HIGH_QUALITY))
     return PP_PRINTOUTPUTFORMAT_PDF | PP_PRINTOUTPUTFORMAT_RASTER;
-  if (HasPermission(PDFEngine::PERMISSION_PRINT_LOW_QUALITY))
+  if (HasPermission(PERMISSION_PRINT_LOW_QUALITY))
     return PP_PRINTOUTPUTFORMAT_RASTER;
   return 0;
 }
@@ -1460,11 +1457,11 @@
     uint32_t page_range_count,
     const PP_PrintSettings_Dev& print_settings) {
   ScopedSubstFont scoped_subst_font(this);
-  if (HasPermission(PDFEngine::PERMISSION_PRINT_HIGH_QUALITY) &&
-      (print_settings.format & PP_PRINTOUTPUTFORMAT_PDF)) {
+  if ((print_settings.format & PP_PRINTOUTPUTFORMAT_PDF) &&
+      HasPermission(PERMISSION_PRINT_HIGH_QUALITY)) {
     return PrintPagesAsPDF(page_ranges, page_range_count, print_settings);
   }
-  if (HasPermission(PDFEngine::PERMISSION_PRINT_LOW_QUALITY))
+  if (HasPermission(PERMISSION_PRINT_LOW_QUALITY))
     return PrintPagesAsRasterPDF(page_ranges, page_range_count, print_settings);
   return pp::Resource();
 }
@@ -1643,12 +1640,10 @@
 
   PDFiumMemBufferFileWrite output_file_write;
   if (FPDF_SaveAsCopy(doc, &output_file_write, 0)) {
-    buffer =
-        pp::Buffer_Dev(client_->GetPluginInstance(), output_file_write.size());
-    if (!buffer.is_null()) {
-      memcpy(buffer.data(), output_file_write.buffer().c_str(),
-             output_file_write.size());
-    }
+    size_t size = output_file_write.size();
+    buffer = pp::Buffer_Dev(client_->GetPluginInstance(), size);
+    if (!buffer.is_null())
+      memcpy(buffer.data(), output_file_write.buffer().c_str(), size);
   }
   return buffer;
 }
@@ -1671,13 +1666,11 @@
   for (uint32_t index = 0; index < page_range_count; ++index) {
     if (!page_number_str.empty())
       page_number_str.append(",");
-    page_number_str.append(
-        base::UintToString(page_ranges[index].first_page_number + 1));
-    if (page_ranges[index].first_page_number !=
-        page_ranges[index].last_page_number) {
+    const PP_PrintPageNumberRange_Dev& range = page_ranges[index];
+    page_number_str.append(base::UintToString(range.first_page_number + 1));
+    if (range.first_page_number != range.last_page_number) {
       page_number_str.append("-");
-      page_number_str.append(
-          base::UintToString(page_ranges[index].last_page_number + 1));
+      page_number_str.append(base::UintToString(range.last_page_number + 1));
     }
   }
 
@@ -2665,9 +2658,8 @@
     FPDF_WIDESTRING destination_pdf_wide =
         reinterpret_cast<FPDF_WIDESTRING>(destination_wide.c_str());
     FPDF_BOOKMARK bookmark = FPDFBookmark_Find(doc_, destination_pdf_wide);
-    if (!bookmark)
-      return -1;
-    dest = FPDFBookmark_GetDest(doc_, bookmark);
+    if (bookmark)
+      dest = FPDFBookmark_GetDest(doc_, bookmark);
   }
   return dest ? FPDFDest_GetPageIndex(doc_, dest) : -1;
 }
diff --git a/remoting/client/chromoting_client.cc b/remoting/client/chromoting_client.cc
index 04c4c94c..d7f4896 100644
--- a/remoting/client/chromoting_client.cc
+++ b/remoting/client/chromoting_client.cc
@@ -243,6 +243,7 @@
     }
   } else if (state == SignalStrategy::DISCONNECTED) {
     VLOG(1) << "Signaling connection closed.";
+    mouse_input_scaler_.set_input_stub(nullptr);
     connection_.reset();
     user_interface_->OnConnectionState(protocol::ConnectionToHost::CLOSED,
                                        protocol::SIGNALING_ERROR);
diff --git a/services/preferences/tracked/pref_hash_calculator_unittest.cc b/services/preferences/tracked/pref_hash_calculator_unittest.cc
index 0d8838fc..8712d4f3 100644
--- a/services/preferences/tracked/pref_hash_calculator_unittest.cc
+++ b/services/preferences/tracked/pref_hash_calculator_unittest.cc
@@ -196,3 +196,17 @@
             PrefHashCalculator(kSeed, kNewDeviceId, kLegacyDeviceId)
                 .Validate("pref.path", &string_value, kExpectedValue));
 }
+
+TEST(PrefHashCalculatorTest, TestCompatibleWithEmptyLegacyDeviceId) {
+  static const char kSeed[] = "0123456789ABCDEF0123456789ABCDEF";
+  static const char kNewDeviceId[] = "unused";
+  static const char kLegacyDeviceId[] = "";
+
+  const base::Value string_value("testing with special chars:\n<>{}:^^@#$\\/");
+  static const char kExpectedValue[] =
+      "F14F989B7CAABF3B36ECAE34492C4D8094D2500E7A86D9A3203E54B274C27CB5";
+
+  EXPECT_EQ(PrefHashCalculator::VALID_SECURE_LEGACY,
+            PrefHashCalculator(kSeed, kNewDeviceId, kLegacyDeviceId)
+                .Validate("pref.path", &string_value, kExpectedValue));
+}
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index ae2596b8..7384413 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -10597,6 +10597,27 @@
         },
         "test": "angle_deqp_gles2_tests",
         "use_xvfb": false
+      },
+      {
+        "args": [
+          "--test-launcher-batch-limit=400",
+          "--deqp-egl-display-type=angle-gl"
+        ],
+        "name": "angle_deqp_gles3_gl_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.12.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "shards": 12
+        },
+        "test": "angle_deqp_gles3_tests",
+        "use_xvfb": false
       }
     ],
     "isolated_scripts": []
@@ -10621,6 +10642,25 @@
         },
         "test": "angle_deqp_gles2_tests",
         "use_xvfb": false
+      },
+      {
+        "args": [
+          "--test-launcher-batch-limit=400",
+          "--deqp-egl-display-type=angle-gl"
+        ],
+        "name": "angle_deqp_gles3_gl_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ],
+          "shards": 12
+        },
+        "test": "angle_deqp_gles3_tests",
+        "use_xvfb": false
       }
     ],
     "isolated_scripts": []
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index d7037993..99d8495b 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -29853,7 +29853,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -29885,7 +29885,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -29915,7 +29915,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -29947,7 +29947,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -29977,7 +29977,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30009,7 +30009,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30039,7 +30039,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build27-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30071,7 +30071,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build27-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30101,7 +30101,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30133,7 +30133,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30163,7 +30163,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30195,7 +30195,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30225,7 +30225,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30257,7 +30257,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30287,7 +30287,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30319,7 +30319,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30349,7 +30349,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build27-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30381,7 +30381,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build27-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30411,7 +30411,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30443,7 +30443,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30473,7 +30473,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30505,7 +30505,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30535,7 +30535,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30567,7 +30567,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30597,7 +30597,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30629,7 +30629,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30659,7 +30659,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build27-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30691,7 +30691,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build27-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30721,7 +30721,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30753,7 +30753,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30783,7 +30783,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30815,7 +30815,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30845,7 +30845,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30877,7 +30877,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30907,7 +30907,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30939,7 +30939,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -30969,7 +30969,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build27-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31001,7 +31001,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build27-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31031,7 +31031,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31063,7 +31063,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31093,7 +31093,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31125,7 +31125,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31146,7 +31146,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31176,7 +31176,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31206,7 +31206,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31238,7 +31238,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31268,7 +31268,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31300,7 +31300,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31330,7 +31330,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31362,7 +31362,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31392,7 +31392,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31424,7 +31424,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31445,7 +31445,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31475,7 +31475,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31507,7 +31507,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31537,7 +31537,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31569,7 +31569,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31599,7 +31599,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31631,7 +31631,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31652,7 +31652,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31682,7 +31682,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31714,7 +31714,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31744,7 +31744,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build27-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31776,7 +31776,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build27-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31806,7 +31806,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31838,7 +31838,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31868,7 +31868,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31898,7 +31898,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31930,7 +31930,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31960,7 +31960,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -31992,7 +31992,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32022,7 +32022,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32054,7 +32054,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32084,7 +32084,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32116,7 +32116,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32146,7 +32146,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32178,7 +32178,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32208,7 +32208,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32240,7 +32240,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32270,7 +32270,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32302,7 +32302,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32332,7 +32332,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32364,7 +32364,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32394,7 +32394,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32426,7 +32426,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32456,7 +32456,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32488,7 +32488,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32518,7 +32518,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32550,7 +32550,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32580,7 +32580,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build27-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32612,7 +32612,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build27-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32642,7 +32642,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32674,7 +32674,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32704,7 +32704,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32736,7 +32736,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32766,7 +32766,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build27-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32798,7 +32798,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build27-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32828,7 +32828,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32860,7 +32860,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32890,7 +32890,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32922,7 +32922,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32952,7 +32952,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -32984,7 +32984,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33014,7 +33014,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build27-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33046,7 +33046,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build27-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33076,7 +33076,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33108,7 +33108,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33138,7 +33138,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33170,7 +33170,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33200,7 +33200,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33232,7 +33232,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33262,7 +33262,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33294,7 +33294,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33324,7 +33324,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33356,7 +33356,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33386,7 +33386,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33418,7 +33418,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33448,7 +33448,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33480,7 +33480,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33510,7 +33510,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33542,7 +33542,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build31-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33572,7 +33572,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build27-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33604,7 +33604,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build27-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33634,7 +33634,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33666,7 +33666,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33696,7 +33696,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33728,7 +33728,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33758,7 +33758,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33790,7 +33790,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33820,7 +33820,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33852,7 +33852,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33882,7 +33882,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33914,7 +33914,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33935,7 +33935,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build29-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33965,7 +33965,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -33997,7 +33997,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -34027,7 +34027,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -34059,7 +34059,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -34089,7 +34089,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -34119,7 +34119,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -34151,7 +34151,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build30-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -34181,7 +34181,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
@@ -34213,7 +34213,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0534",
+              "gpu": "10de:1cb3",
               "id": "build28-a9",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
diff --git a/testing/scripts/run_telemetry_benchmark_as_googletest.py b/testing/scripts/run_telemetry_benchmark_as_googletest.py
index 469ef12..e57f7fd 100755
--- a/testing/scripts/run_telemetry_benchmark_as_googletest.py
+++ b/testing/scripts/run_telemetry_benchmark_as_googletest.py
@@ -54,9 +54,9 @@
   for output_format in args.output_format:
     rest_args.append('--output-format=' + output_format)
 
-  rc, chartresults, json_test_results = run_benchmark(args, rest_args)
+  rc, perf_results, json_test_results = run_benchmark(args, rest_args)
 
-  if chartresults:
+  if perf_results:
     if args.isolated_script_test_perf_output:
       filename = args.isolated_script_test_perf_output
     elif args.isolated_script_test_chartjson_output:
@@ -65,8 +65,8 @@
       filename = None
 
     if filename is not None:
-      with open(filename, 'w') as chartjson_output_file:
-        json.dump(chartresults, chartjson_output_file)
+      with open(filename, 'w') as perf_results_output_file:
+        json.dump(perf_results, perf_results_output_file)
 
   json.dump(json_test_results, args.isolated_script_test_output)
 
@@ -81,8 +81,9 @@
   tempfile_dir = tempfile.mkdtemp('telemetry')
   valid = True
   num_failures = 0
+  histogram_results_present = 'histograms' in args.output_format
   chartjson_results_present = 'chartjson' in args.output_format
-  chartresults = None
+  perf_results = None
   json_test_results = None
 
   results = None
@@ -99,10 +100,16 @@
     # If we have also output chartjson read it in and return it.
     # results-chart.json is the file name output by telemetry when the
     # chartjson output format is included
-    if chartjson_results_present:
-      chart_tempfile_name = os.path.join(tempfile_dir, 'results-chart.json')
-      with open(chart_tempfile_name) as f:
-        chartresults = json.load(f)
+    if histogram_results_present:
+      tempfile_name = os.path.join(tempfile_dir, 'histograms.json')
+    elif chartjson_results_present:
+      tempfile_name = os.path.join(tempfile_dir, 'results-chart.json')
+    else:
+      tempfile_name = None
+
+    if tempfile_name is not None:
+      with open(tempfile_name) as f:
+        perf_results = json.load(f)
 
     # test-results.json is the file name output by telemetry when the
     # json-test-results format is included
@@ -125,7 +132,7 @@
     if rc == 0:
       rc = 1  # Signal an abnormal exit.
 
-  return rc, chartresults, json_test_results
+  return rc, perf_results, json_test_results
 
 
 # This is not really a "script test" so does not need to manually add
diff --git a/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_remote_postMessage_test.https.html b/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_remote_postMessage_test.https.html
index d648ebe..57d2d44 100644
--- a/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_remote_postMessage_test.https.html
+++ b/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_remote_postMessage_test.https.html
@@ -10,25 +10,30 @@
   assert_true(mod instanceof WebAssembly.Module);
   var remote;
   await new Promise((resolve, reject) => {
+    var nr_of_tries = 0;
     var i = setInterval(()=>{
       try {
         remote.document;
+        ++nr_of_tries;
+        if(nr_of_tries > 30) {
+          reject();
+        }
       } catch(e) {
         clearInterval(i);
         resolve();
       }
-    }, 100);
+    }, 1000);
     remote = window.open("about:blank");
     remote.opener = null;
     remote.location = remote_loc;
   });
   var ans = await new Promise((resolve, reject) => {
-    remote.postMessage(mod, '*');
-    window.addEventListener("message",
-      function f(reply) {
-        resolve(reply.data);
-        window.removeEventListener("message", f);
-      }, false);
+  remote.postMessage(mod, '*');
+  window.addEventListener("message",
+     function f(reply) {
+       resolve(reply.data);
+       window.removeEventListener("message", f);
+     }, false);
   });
   assert_equals(ans, "didn't make it");
   ans = await new Promise((resolve, reject) => {
diff --git a/third_party/WebKit/LayoutTests/reporting-observer/resources/intervention.js b/third_party/WebKit/LayoutTests/reporting-observer/resources/intervention.js
index 06ed4195..09796d8 100644
--- a/third_party/WebKit/LayoutTests/reporting-observer/resources/intervention.js
+++ b/third_party/WebKit/LayoutTests/reporting-observer/resources/intervention.js
@@ -1,7 +1,5 @@
 async_test(function(test) {
   var observer = new ReportingObserver(function(reports, observer) {
-    test.done();
-  window.console.log("************************************** IN OBSERVER");
     test.step(function() {
       assert_equals(reports.length, 1);
 
@@ -26,7 +24,6 @@
   var targetX = rect.left + rect.width / 2;
   var targetY = rect.top + rect.height / 2;
   document.body.addEventListener('touchstart', function(e) {
-    window.console.log("************************************** TOUCH " + e.type);
     e.preventDefault();
     test.done();
   });
diff --git a/third_party/WebKit/PerformanceTests/Layout/flexbox-deeply-nested-column-flow.html b/third_party/WebKit/PerformanceTests/Layout/flexbox-deeply-nested-column-flow.html
new file mode 100644
index 0000000..47c9c71a
--- /dev/null
+++ b/third_party/WebKit/PerformanceTests/Layout/flexbox-deeply-nested-column-flow.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<script src="../resources/runner.js"></script>
+<style>
+  .col { display:flex; flex-direction:column; height:100%; }
+</style>
+<div style="display:flex; width:300px; height:200px;">
+  <div>
+    <div id="child"></div>
+  </div>
+  <div class="col">
+    <div class="col">
+      <div class="col">
+        <div class="col">
+          <div class="col">
+            <div class="col">
+              <div class="col">
+                <div class="col">
+                  <div class="col">
+                    <div class="col">
+                      <div class="col">
+                        <div class="col">
+                          <div class="col"></div>
+                        </div>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+
+<script>
+function runTest()
+{
+    document.getElementById("child").style.width = '100px';
+    PerfTestRunner.forceLayout();
+    document.getElementById("child").style.width = '200px';
+    PerfTestRunner.forceLayout();
+}
+
+PerfTestRunner.measureRunsPerSecond({
+    description: "Layout of sibling of deeply nested column flow flex containers",
+    run: runTest,
+});
+</script>
diff --git a/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.cpp b/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.cpp
index a35704b..77353abd 100644
--- a/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.cpp
+++ b/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.cpp
@@ -24,16 +24,15 @@
 ThreadedWorkletMessagingProxy::ThreadedWorkletMessagingProxy(
     ExecutionContext* execution_context,
     WorkerClients* worker_clients)
-    : ThreadedMessagingProxyBase(execution_context, worker_clients) {
-  worklet_object_proxy_ =
-      CreateObjectProxy(this, GetParentFrameTaskRunners());
-}
+    : ThreadedMessagingProxyBase(execution_context, worker_clients) {}
 
 void ThreadedWorkletMessagingProxy::Initialize() {
   DCHECK(IsMainThread());
   if (AskedToTerminate())
     return;
 
+  worklet_object_proxy_ = CreateObjectProxy(this, GetParentFrameTaskRunners());
+
   Document* document = ToDocument(GetExecutionContext());
   SecurityOrigin* starter_origin = document->GetSecurityOrigin();
   KURL script_url = document->Url();
@@ -102,4 +101,10 @@
                                             parent_frame_task_runners);
 }
 
+ThreadedWorkletObjectProxy&
+    ThreadedWorkletMessagingProxy::WorkletObjectProxy() {
+  DCHECK(worklet_object_proxy_);
+  return *worklet_object_proxy_;
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.h b/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.h
index aeb4a6b..706af2e 100644
--- a/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.h
+++ b/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.h
@@ -36,9 +36,7 @@
  protected:
   ThreadedWorkletMessagingProxy(ExecutionContext*, WorkerClients*);
 
-  ThreadedWorkletObjectProxy& WorkletObjectProxy() {
-    return *worklet_object_proxy_;
-  }
+  ThreadedWorkletObjectProxy& WorkletObjectProxy();
 
  private:
   friend class ThreadedWorkletMessagingProxyForTest;
diff --git a/third_party/WebKit/Source/modules/payments/CompleteTest.cpp b/third_party/WebKit/Source/modules/payments/CompleteTest.cpp
index 61c8d71..4d63234 100644
--- a/third_party/WebKit/Source/modules/payments/CompleteTest.cpp
+++ b/third_party/WebKit/Source/modules/payments/CompleteTest.cpp
@@ -29,7 +29,7 @@
       .Then(funcs.ExpectNoCall(), funcs.ExpectCall());
 }
 
-TEST(CompleteTest, RejectCompletePromiseOnError) {
+TEST(CompleteTest, ResolveCompletePromiseOnUnknownError) {
   V8TestingScope scope;
   PaymentRequestMockFunctionScope funcs(scope.GetScriptState());
   MakePaymentRequestOriginSecure(scope.GetDocument());
@@ -41,15 +41,30 @@
   static_cast<payments::mojom::blink::PaymentRequestClient*>(request)
       ->OnPaymentResponse(BuildPaymentResponseForTest());
 
-  String error_message;
   request->Complete(scope.GetScriptState(), PaymentCompleter::kSuccess)
-      .Then(funcs.ExpectNoCall(), funcs.ExpectCall(&error_message));
+      .Then(funcs.ExpectCall(), funcs.ExpectNoCall());
 
   static_cast<payments::mojom::blink::PaymentRequestClient*>(request)->OnError(
       payments::mojom::blink::PaymentErrorReason::UNKNOWN);
+}
 
-  v8::MicrotasksScope::PerformCheckpoint(scope.GetScriptState()->GetIsolate());
-  EXPECT_EQ("UnknownError: Request failed", error_message);
+TEST(CompleteTest, ResolveCompletePromiseOnUserClosingUI) {
+  V8TestingScope scope;
+  PaymentRequestMockFunctionScope funcs(scope.GetScriptState());
+  MakePaymentRequestOriginSecure(scope.GetDocument());
+  PaymentRequest* request = PaymentRequest::Create(
+      scope.GetExecutionContext(), BuildPaymentMethodDataForTest(),
+      BuildPaymentDetailsInitForTest(), scope.GetExceptionState());
+  EXPECT_FALSE(scope.GetExceptionState().HadException());
+  request->show(scope.GetScriptState());
+  static_cast<payments::mojom::blink::PaymentRequestClient*>(request)
+      ->OnPaymentResponse(BuildPaymentResponseForTest());
+
+  request->Complete(scope.GetScriptState(), PaymentCompleter::kSuccess)
+      .Then(funcs.ExpectCall(), funcs.ExpectNoCall());
+
+  static_cast<payments::mojom::blink::PaymentRequestClient*>(request)->OnError(
+      payments::mojom::blink::PaymentErrorReason::USER_CANCEL);
 }
 
 // If user cancels the transaction during processing, the complete() promise
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp b/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
index 7f9550c..d7118fcf 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
@@ -1226,8 +1226,14 @@
 
   DCHECK(!message.IsEmpty());
 
-  if (complete_resolver_)
-    complete_resolver_->Reject(DOMException::Create(ec, message));
+  // If the user closes PaymentRequest UI after PaymentResponse.complete() has
+  // been called, the PaymentResponse.complete() promise should be resolved with
+  // undefined instead of rejecting.
+  if (complete_resolver_) {
+    DCHECK(error == PaymentErrorReason::USER_CANCEL ||
+           error == PaymentErrorReason::UNKNOWN);
+    complete_resolver_->Resolve();
+  }
 
   if (show_resolver_)
     show_resolver_->Reject(DOMException::Create(ec, message));
diff --git a/third_party/closure_compiler/compiled_resources2.gyp b/third_party/closure_compiler/compiled_resources2.gyp
index bc8bc85..3c546f1 100644
--- a/third_party/closure_compiler/compiled_resources2.gyp
+++ b/third_party/closure_compiler/compiled_resources2.gyp
@@ -22,6 +22,7 @@
         '<(DEPTH)/chrome/browser/resources/chromeos/quick_unlock/compiled_resources2.gyp:*',
         '<(DEPTH)/chrome/browser/resources/chromeos/select_to_speak/compiled_resources2.gyp:*',
         '<(DEPTH)/chrome/browser/resources/chromeos/switch_access/compiled_resources2.gyp:*',
+        '<(DEPTH)/chrome/browser/resources/download_internals/compiled_resources2.gyp:*',
         '<(DEPTH)/chrome/browser/resources/extensions/compiled_resources2.gyp:*',
         '<(DEPTH)/chrome/browser/resources/md_bookmarks/compiled_resources2.gyp:*',
         '<(DEPTH)/chrome/browser/resources/md_downloads/compiled_resources2.gyp:*',
diff --git a/third_party/freetype/BUILD.gn b/third_party/freetype/BUILD.gn
index fd6c35b..0dbb1c6 100644
--- a/third_party/freetype/BUILD.gn
+++ b/third_party/freetype/BUILD.gn
@@ -45,7 +45,6 @@
     "include/freetype-custom-config/ftoption.h",
     "src/src/base/ftbase.c",
     "src/src/base/ftbitmap.c",
-    "src/src/base/ftlcdfil.c",
   ]
 
   include_dirs = []
@@ -120,6 +119,7 @@
     "src/src/base/ftgasp.c",
     "src/src/base/ftglyph.c",
     "src/src/base/ftinit.c",
+    "src/src/base/ftlcdfil.c",
     "src/src/base/ftmm.c",
     "src/src/base/ftstroke.c",
     "src/src/base/fttype1.c",
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index ce2ddbc..9e6e738 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-8-1-24
-Revision: 02e80da6090c21d6e59ac955b7f56e1ad4a9850b
+Version: VER-2-8-1-23
+Revision: 6f2b6f8f72ffb5017ab00fca83185b21f1a9f56d
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
 License File: src/docs/FTL.TXT
diff --git a/tools/perf/benchmarks/system_health.py b/tools/perf/benchmarks/system_health.py
index 0db7b45..70919c9 100644
--- a/tools/perf/benchmarks/system_health.py
+++ b/tools/perf/benchmarks/system_health.py
@@ -35,9 +35,11 @@
   """
 
   def CreateCoreTimelineBasedMeasurementOptions(self):
-    options = timeline_based_measurement.Options(
-        chrome_trace_category_filter.ChromeTraceCategoryFilter(
-            filter_string='rail,toplevel'))
+    cat_filter = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        filter_string='rail,toplevel')
+    cat_filter.AddIncludedCategory('accessibility')
+
+    options = timeline_based_measurement.Options(cat_filter)
     options.config.enable_battor_trace = True
     options.config.enable_chrome_trace = True
     options.config.enable_cpu_trace = True
@@ -45,7 +47,8 @@
         'clockSyncLatencyMetric',
         'cpuTimeMetric',
         'powerMetric',
-        'tracingMetric'
+        'tracingMetric',
+        'accessibilityMetric',
     ])
     loading_metrics_category.AugmentOptionsForLoadingMetrics(options)
     # The EQT metric depends on the same categories as the loading metric.
diff --git a/tools/perf/benchmarks/v8_browsing.py b/tools/perf/benchmarks/v8_browsing.py
index 8898cc3..5bd0493 100644
--- a/tools/perf/benchmarks/v8_browsing.py
+++ b/tools/perf/benchmarks/v8_browsing.py
@@ -207,6 +207,14 @@
             'browse:shopping:lazada',
             [story.expectations.ANDROID_ONE],
             'crbug.com/768472')
+        self.DisableStory(
+            'browse:shopping:flipkart',
+            [story.expectations.ALL_MOBILE],
+            'crbug.com/767970')
+        self.DisableStory(
+            'browse:news:cnn',
+            [story.expectations.ALL_MOBILE],
+            'crbug.com/767970')
     return StoryExpectations()
 
   @classmethod
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 7902ba1..c5a9042e 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -549,7 +549,7 @@
     waterfall, 'Linux Perf', 'linux-release', 'linux',
     swarming=[
       {
-       'gpu': '102b:0534',
+       'gpu': '10de:1cb3',
        'os': 'Ubuntu-14.04',
        'pool': 'Chrome-perf',
        'device_ids': [
diff --git a/tools/perf/page_sets/data/system_health_desktop.json b/tools/perf/page_sets/data/system_health_desktop.json
index 20502e54..993e66d 100644
--- a/tools/perf/page_sets/data/system_health_desktop.json
+++ b/tools/perf/page_sets/data/system_health_desktop.json
@@ -60,6 +60,12 @@
         "browse:social:twitter_infinite_scroll": {
             "DEFAULT": "system_health_desktop_054.wprgo"
         },
+        "browse_accessibility:tech:codesearch": {
+            "DEFAULT": "system_health_desktop_059.wprgo"
+        },
+        "browse_accessibility:tools:gmail_compose": {
+            "DEFAULT": "system_health_desktop_059.wprgo"
+        },
         "load:games:alphabetty": {
             "DEFAULT": "system_health_desktop_005.wprgo"
         },
@@ -186,6 +192,12 @@
         "load:tools:weather": {
             "DEFAULT": "system_health_desktop_004.wprgo"
         },
+        "load_accessibility:media:wikipedia": {
+            "DEFAULT": "system_health_desktop_059.wprgo"
+        },
+        "load_accessibility:shopping:amazon": {
+            "DEFAULT": "system_health_desktop_059.wprgo"
+        },
         "long_running:tools:gmail-background": {
             "DEFAULT": "system_health_desktop_028.wprgo"
         },
diff --git a/tools/perf/page_sets/data/system_health_desktop_059.wprgo.sha1 b/tools/perf/page_sets/data/system_health_desktop_059.wprgo.sha1
new file mode 100644
index 0000000..91f7431a
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_desktop_059.wprgo.sha1
@@ -0,0 +1 @@
+e9da0dbcc23e0328990f7d6aa5ec747a2e0446e4
\ No newline at end of file
diff --git a/tools/perf/page_sets/system_health/accessibility_stories.py b/tools/perf/page_sets/system_health/accessibility_stories.py
new file mode 100644
index 0000000..5f08904
--- /dev/null
+++ b/tools/perf/page_sets/system_health/accessibility_stories.py
@@ -0,0 +1,103 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from page_sets.login_helpers import google_login
+
+from page_sets.system_health import platforms
+from page_sets.system_health import story_tags
+from page_sets.system_health import system_health_story
+
+
+LONG_TEXT = """Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla
+suscipit enim ut nunc vestibulum, vitae porta dui eleifend. Donec
+condimentum ante malesuada mi sodales maximus. Vivamus mauris libero,
+placerat a lobortis quis, aliquam id lacus. Vestibulum at sodales
+odio, sed mattis est. Ut at ligula tincidunt, consequat lectus sed,
+sodales felis. Aliquam erat volutpat. Praesent id egestas ex. Donec
+laoreet blandit euismod. Nullam nec ultricies libero, sed suscipit
+ante. Cras nec libero sed sem mollis pellentesque."""
+
+
+class _AccessibilityStory(system_health_story.SystemHealthStory):
+  """Abstract base class for accessibility System Health user stories."""
+  ABSTRACT_STORY = True
+  SUPPORTED_PLATFORMS = platforms.DESKTOP_ONLY
+
+  def __init__(self, story_set, take_memory_measurement):
+    super(_AccessibilityStory, self).__init__(
+        story_set, take_memory_measurement,
+        extra_browser_args=['--force-renderer-accessibility'])
+
+
+class AccessibilityScrollingCodeSearchStory(_AccessibilityStory):
+  """Tests scrolling an element within a page."""
+  NAME = 'browse_accessibility:tech:codesearch'
+  URL = 'https://cs.chromium.org/chromium/src/ui/accessibility/platform/ax_platform_node_mac.mm'
+  TAGS = [story_tags.ACCESSIBILITY, story_tags.SCROLL]
+
+  def RunNavigateSteps(self, action_runner):
+    super(AccessibilityScrollingCodeSearchStory, self).RunNavigateSteps(
+        action_runner)
+    action_runner.WaitForElement(text='// namespace ui')
+    action_runner.ScrollElement(selector='#file_scroller', distance=1000)
+
+
+class AccessibilityWikipediaStory(_AccessibilityStory):
+  """Wikipedia page on Accessibility. Long, but very simple, clean layout."""
+  NAME = 'load_accessibility:media:wikipedia'
+  URL = 'https://en.wikipedia.org/wiki/Accessibility'
+  TAGS = [story_tags.ACCESSIBILITY]
+
+
+class AccessibilityAmazonStory(_AccessibilityStory):
+  """Amazon results page. Good example of a site with a data table."""
+  NAME = 'load_accessibility:shopping:amazon'
+  URL = 'https://www.amazon.com/gp/offer-listing/B01IENFJ14'
+  TAGS = [story_tags.ACCESSIBILITY]
+
+
+class AccessibilityGmailComposeStory(_AccessibilityStory):
+  """Tests typing a lot of text into a Gmail compose window."""
+  NAME = 'browse_accessibility:tools:gmail_compose'
+  URL = 'https://mail.google.com/mail/#inbox?compose=new'
+  TAGS = [story_tags.ACCESSIBILITY, story_tags.KEYBOARD_INPUT]
+
+  def RunNavigateSteps(self, action_runner):
+    google_login.LoginGoogleAccount(action_runner, 'googletest',
+                                    self.credentials_path)
+
+    # Navigating to https://mail.google.com immediately leads to an infinite
+    # redirection loop due to a bug in WPR (see
+    # https://github.com/chromium/web-page-replay/issues/70). We therefore first
+    # navigate to a sub-URL to set up the session and hit the resulting
+    # redirection loop. Afterwards, we can safely navigate to
+    # https://mail.google.com.
+    action_runner.tab.WaitForDocumentReadyStateToBeComplete()
+    action_runner.Navigate(
+        'https://mail.google.com/mail/mu/mp/872/trigger_redirection_loop')
+    action_runner.tab.WaitForDocumentReadyStateToBeComplete()
+
+    super(AccessibilityGmailComposeStory, self).RunNavigateSteps(
+        action_runner)
+
+    action_runner.WaitForJavaScriptCondition(
+        'document.getElementById("loading").style.display === "none"')
+
+    # Tab from the To field to the message body.
+    action_runner.WaitForElement(selector='#\\:gr')
+    action_runner.PressKey('Tab')
+    action_runner.PressKey('Tab')
+
+    # EnterText doesn't handle newlines for some reason.
+    long_text = LONG_TEXT.replace('\n', ' ')
+
+    # Enter some text
+    action_runner.EnterText(long_text, character_delay_ms=1)
+
+    # Move up a couple of lines and then enter it again, this causes
+    # a huge amount of wrapping and re-layout
+    action_runner.PressKey('Home')
+    action_runner.PressKey('ArrowUp')
+    action_runner.PressKey('ArrowUp')
+    action_runner.EnterText(long_text, character_delay_ms=1)
diff --git a/tools/perf/page_sets/system_health/story_tags.py b/tools/perf/page_sets/system_health/story_tags.py
index e296808cd..6f43e42f 100644
--- a/tools/perf/page_sets/system_health/story_tags.py
+++ b/tools/perf/page_sets/system_health/story_tags.py
@@ -11,6 +11,8 @@
 # Below are tags that describe various aspect of system health stories.
 # A story can have multiple tags. All the tags should be noun.
 
+ACCESSIBILITY = Tag(
+    'accessibility', 'Story tests performance when accessibility is enabled.')
 AUDIO_PLAYBACK = Tag(
     'audio_playback', 'Story has audio playing.')
 CANVAS_ANIMATION = Tag(
@@ -34,6 +36,8 @@
     'of JavaScript. The story uses 20Mb+ memory for javascript and local '
     'run with "v8" category enabled also shows the trace has js slices across '
     'the whole run.')
+KEYBOARD_INPUT = Tag(
+    'keyboard_input', 'Story does keyboard input.')
 SCROLL = Tag(
     'scroll', 'Story has scroll gestures & scroll animation.')
 PINCH_ZOOM = Tag(
diff --git a/tools/perf/page_sets/system_health/system_health_story.py b/tools/perf/page_sets/system_health/system_health_story.py
index ffa7b26f..10189bf5 100644
--- a/tools/perf/page_sets/system_health/system_health_story.py
+++ b/tools/perf/page_sets/system_health/system_health_story.py
@@ -42,7 +42,8 @@
   TAGS = None
   PLATFORM_SPECIFIC = False
 
-  def __init__(self, story_set, take_memory_measurement):
+  def __init__(self, story_set, take_memory_measurement,
+      extra_browser_args=None):
     case, group, _ = self.NAME.split(':')
     tags = []
     if self.TAGS:
@@ -54,7 +55,8 @@
         page_set=story_set, name=self.NAME, url=self.URL, tags=tags,
         credentials_path='../data/credentials.json',
         grouping_keys={'case': case, 'group': group},
-        platform_specific=self.PLATFORM_SPECIFIC)
+        platform_specific=self.PLATFORM_SPECIFIC,
+        extra_browser_args=extra_browser_args)
     self._take_memory_measurement = take_memory_measurement
 
   @classmethod