diff --git a/DEPS b/DEPS
index 8766d07a0..0529502 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'fba26ea9b7fc7ed45d80ba0bd923d1824fd49534',
+  'skia_revision': 'ae532f68a035e50530c846233fe60dabdd9e3414',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '19f1033a265556dec3fdd575526c6fa48e240974',
+  'v8_revision': 'a14a5f4fa219cba654c25e6deeb8f3229075e7ef',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -238,7 +238,7 @@
     Var('chromium_git') + '/native_client/src/third_party/scons-2.0.1.git' + '@' + '1c1550e17fc26355d08627fbdec13d8291227067',
 
   'src/third_party/webrtc':
-    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + 'a7461e3086866272cb69fbfa23c7fc8b0321a425', # commit position 19097
+    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '262f12e06b811d265a437d760a3add82477af106', # commit position 19232
 
   'src/third_party/openmax_dl':
     Var('chromium_git') + '/external/webrtc/deps/third_party/openmax.git' + '@' +  Var('openmax_dl_revision'),
diff --git a/android_webview/browser/aw_browser_main_parts.cc b/android_webview/browser/aw_browser_main_parts.cc
index 4d09fd4..07715b2885 100644
--- a/android_webview/browser/aw_browser_main_parts.cc
+++ b/android_webview/browser/aw_browser_main_parts.cc
@@ -120,7 +120,6 @@
 
   base::android::MemoryPressureListenerAndroid::RegisterSystemCallback(
       base::android::AttachCurrentThread());
-  DeferredGpuCommandService::SetInstance();
   breakpad::CrashDumpObserver::Create();
 
   if (crash_reporter::IsCrashReporterEnabled()) {
@@ -170,6 +169,10 @@
 
   // TODO(meacer): Remove when PlzNavigate ships.
   content::RenderFrameHost::AllowDataUrlNavigationForAndroidWebView();
+
+  // This only works because webview uses in-process gpu
+  // which is not started up early by BrowserMainLoop.
+  DeferredGpuCommandService::SetInstance();
 }
 
 bool AwBrowserMainParts::MainMessageLoopRun(int* result_code) {
diff --git a/base/trace_event/memory_allocator_dump_guid.cc b/base/trace_event/memory_allocator_dump_guid.cc
index 4df7aa46..08ac677 100644
--- a/base/trace_event/memory_allocator_dump_guid.cc
+++ b/base/trace_event/memory_allocator_dump_guid.cc
@@ -13,8 +13,6 @@
 
 namespace {
 
-bool g_use_shared_memory_guid = true;
-
 uint64_t HashString(const std::string& str) {
   uint64_t hash[(kSHA1Length + sizeof(uint64_t) - 1) / sizeof(uint64_t)] = {0};
   SHA1HashBytes(reinterpret_cast<const unsigned char*>(str.data()), str.size(),
@@ -24,15 +22,6 @@
 
 }  // namespace
 
-// static
-bool MemoryAllocatorDumpGuid::UseSharedMemoryBasedGUIDs() {
-  // TODO(hajimehoshi): This should just become the default behavior once the
-  // Mojo GUID (crbug.com/604726) is fixed.
-  if (g_use_shared_memory_guid)
-    return true;
-  return false;
-}
-
 MemoryAllocatorDumpGuid::MemoryAllocatorDumpGuid(uint64_t guid) : guid_(guid) {}
 
 MemoryAllocatorDumpGuid::MemoryAllocatorDumpGuid()
diff --git a/base/trace_event/memory_allocator_dump_guid.h b/base/trace_event/memory_allocator_dump_guid.h
index 32ce6b4..2a420a2 100644
--- a/base/trace_event/memory_allocator_dump_guid.h
+++ b/base/trace_event/memory_allocator_dump_guid.h
@@ -16,10 +16,6 @@
 
 class BASE_EXPORT MemoryAllocatorDumpGuid {
  public:
-  // If the clients of base::SharedMemory should use the global dump guid(s)
-  // created by SharedMemoryTracker.
-  static bool UseSharedMemoryBasedGUIDs();
-
   MemoryAllocatorDumpGuid();
   explicit MemoryAllocatorDumpGuid(uint64_t guid);
 
diff --git a/base/trace_event/process_memory_dump.cc b/base/trace_event/process_memory_dump.cc
index 18177f6..400c73f 100644
--- a/base/trace_event/process_memory_dump.cc
+++ b/base/trace_event/process_memory_dump.cc
@@ -430,42 +430,31 @@
     const UnguessableToken& shared_memory_guid,
     int importance,
     bool is_weak) {
-  if (MemoryAllocatorDumpGuid::UseSharedMemoryBasedGUIDs()) {
-    DCHECK(!shared_memory_guid.is_empty());
-    // New model where the global dumps created by SharedMemoryTracker are used
-    // for the clients.
+  DCHECK(!shared_memory_guid.is_empty());
+  // New model where the global dumps created by SharedMemoryTracker are used
+  // for the clients.
 
-    // The guid of the local dump created by SharedMemoryTracker for the memory
-    // segment.
-    auto local_shm_guid =
-        SharedMemoryTracker::GetDumpIdForTracing(shared_memory_guid);
+  // The guid of the local dump created by SharedMemoryTracker for the memory
+  // segment.
+  auto local_shm_guid =
+      SharedMemoryTracker::GetDumpIdForTracing(shared_memory_guid);
 
-    // The dump guid of the global dump created by the tracker for the memory
-    // segment.
-    auto global_shm_guid =
-        SharedMemoryTracker::GetGlobalDumpIdForTracing(shared_memory_guid);
+  // The dump guid of the global dump created by the tracker for the memory
+  // segment.
+  auto global_shm_guid =
+      SharedMemoryTracker::GetGlobalDumpIdForTracing(shared_memory_guid);
 
-    // Create an edge between local dump of the client and the local dump of the
-    // SharedMemoryTracker. Do not need to create the dumps here since the
-    // tracker would create them. The importance is also required here for the
-    // case of single process mode.
-    AddOwnershipEdge(client_local_dump_guid, local_shm_guid, importance);
+  // Create an edge between local dump of the client and the local dump of the
+  // SharedMemoryTracker. Do not need to create the dumps here since the tracker
+  // would create them. The importance is also required here for the case of
+  // single process mode.
+  AddOwnershipEdge(client_local_dump_guid, local_shm_guid, importance);
 
-    // TODO(ssid): Handle the case of weak dumps here. This needs a new function
-    // GetOrCreaetGlobalDump() in PMD since we need to change the behavior of
-    // the created global dump.
-    // Create an edge that overrides the edge created by SharedMemoryTracker.
-    AddOwnershipEdge(local_shm_guid, global_shm_guid, importance);
-  } else {
-    // This is the old model where the clients create global dumps for
-    // themselves.
-    if (is_weak)
-      CreateWeakSharedGlobalAllocatorDump(client_global_dump_guid);
-    else
-      CreateSharedGlobalAllocatorDump(client_global_dump_guid);
-    AddOwnershipEdge(client_local_dump_guid, client_global_dump_guid,
-                     importance);
-  }
+  // TODO(ssid): Handle the case of weak dumps here. This needs a new function
+  // GetOrCreaetGlobalDump() in PMD since we need to change the behavior of the
+  // created global dump.
+  // Create an edge that overrides the edge created by SharedMemoryTracker.
+  AddOwnershipEdge(local_shm_guid, global_shm_guid, importance);
 }
 
 void ProcessMemoryDump::AddSuballocation(const MemoryAllocatorDumpGuid& source,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
index ca88549..5090f7e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
@@ -136,6 +136,7 @@
     @Test
     @MediumTest
     @Feature({"NewTabPage", "RenderTest"})
+    @CommandLineFlags.Add("disable-features=NTPCondensedLayout")
     public void testRender() throws IOException {
         mActivityTestRule.getInstrumentation().waitForIdleSync();
         mRenderTestRule.render(mTileGridLayout, "most_visited");
diff --git a/chrome/browser/apps/app_url_redirector.cc b/chrome/browser/apps/app_url_redirector.cc
index e5ec867..58b91cf0 100644
--- a/chrome/browser/apps/app_url_redirector.cc
+++ b/chrome/browser/apps/app_url_redirector.cc
@@ -6,9 +6,15 @@
 
 #include "apps/launcher.h"
 #include "base/bind.h"
+#include "base/feature_list.h"
 #include "base/logging.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/extensions/app_launch_params.h"
+#include "chrome/browser/ui/extensions/application_launch.h"
+#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/extensions/api/url_handlers/url_handlers_parser.h"
 #include "components/navigation_interception/intercept_navigation_throttle.h"
 #include "components/navigation_interception/navigation_params.h"
@@ -35,6 +41,12 @@
     const navigation_interception::NavigationParams& params) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
+  // Redirecting for Bookmark Apps is hidden behind a feature flag.
+  if (app->from_bookmark() &&
+      !base::FeatureList::IsEnabled(features::kDesktopPWAWindowing)) {
+    return false;
+  }
+
   // Redirect top-level navigations only. This excludes iframes and webviews
   // in particular.
   if (source->IsSubframe()) {
@@ -57,6 +69,27 @@
   Profile* profile =
       Profile::FromBrowserContext(source->GetBrowserContext());
 
+  if (app->from_bookmark()) {
+    Browser* browser = chrome::FindBrowserWithWebContents(source);
+    if (browser == nullptr) {
+      DVLOG(1) << "Don't override: No browser, can't know if already in app.";
+      return false;
+    }
+
+    if (browser->app_name() ==
+        web_app::GenerateApplicationNameFromExtensionId(app->id())) {
+      DVLOG(1) << "Don't override: Already in app.";
+      return false;
+    }
+
+    AppLaunchParams launch_params(
+        profile, app.get(), extensions::LAUNCH_CONTAINER_WINDOW,
+        WindowOpenDisposition::CURRENT_TAB, extensions::SOURCE_URL_HANDLER);
+    launch_params.override_url = params.url();
+    OpenApplication(launch_params);
+    return true;
+  }
+
   DVLOG(1) << "Launching app handler with URL: "
            << params.url().spec() << " -> "
            << app->name() << "(" << app->id() << "):" << handler_id;
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index a4aa225..ba2c11e 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -107,7 +107,6 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/logging_chrome.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/grit/chromium_strings.h"
 #include "chromeos/audio/audio_devices_pref_handler_impl.h"
 #include "chromeos/audio/cras_audio_handler.h"
 #include "chromeos/cert_loader.h"
@@ -170,12 +169,10 @@
 #include "ui/base/ime/chromeos/ime_keyboard.h"
 #include "ui/base/ime/chromeos/input_method_manager.h"
 #include "ui/base/ime/chromeos/input_method_util.h"
-#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/touch/touch_device.h"
 #include "ui/chromeos/events/event_rewriter_chromeos.h"
 #include "ui/chromeos/events/pref_names.h"
 #include "ui/events/event_utils.h"
-#include "ui/message_center/message_center.h"
 
 #if BUILDFLAG(ENABLE_RLZ)
 #include "components/rlz/rlz_tracker.h"
@@ -790,11 +787,6 @@
     VLOG(1) << "Relaunching browser for user: " << account_id.Serialize()
             << " with hash: " << user_id_hash;
   }
-
-  // Set product name ("Chrome OS" or "Chromium OS") to be used in context
-  // header of new-style notification.
-  message_center::MessageCenter::Get()->SetProductOSName(
-      l10n_util::GetStringUTF16(IDS_PRODUCT_OS_NAME));
 }
 
 class GuestLanguageSetCallbackData {
diff --git a/chrome/browser/extensions/activity_log/activity_log_unittest.cc b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
index a853633..580e2c8b 100644
--- a/chrome/browser/extensions/activity_log/activity_log_unittest.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/prerender/prerender_handle.h"
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/prerender/prerender_manager_factory.h"
+#include "chrome/browser/prerender/prerender_test_utils.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
@@ -242,6 +243,9 @@
   ASSERT_TRUE(GetDatabaseEnabled());
   GURL url("http://www.google.com");
 
+  prerender::test_utils::RestorePrerenderMode restore_prerender_mode;
+  prerender::PrerenderManager::SetMode(
+      prerender::PrerenderManager::PRERENDER_MODE_ENABLED);
   prerender::PrerenderManager* prerender_manager =
       prerender::PrerenderManagerFactory::GetForBrowserContext(profile());
 
diff --git a/chrome/browser/extensions/bookmark_app_helper.cc b/chrome/browser/extensions/bookmark_app_helper.cc
index d5ec19df..901b34d 100644
--- a/chrome/browser/extensions/bookmark_app_helper.cc
+++ b/chrome/browser/extensions/bookmark_app_helper.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/bitmap_fetcher/bitmap_fetcher.h"
 #include "chrome/browser/bitmap_fetcher/bitmap_fetcher_delegate.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/convert_web_app.h"
 #include "chrome/browser/extensions/crx_installer.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/favicon_downloader.h"
@@ -31,6 +32,7 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/browser/webshare/share_target_pref_helper.h"
+#include "chrome/common/extensions/api/url_handlers/url_handlers_parser.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/common/origin_trials/chrome_origin_trial_policy.h"
@@ -357,6 +359,9 @@
   if (manifest.start_url.is_valid())
     web_app_info->app_url = manifest.start_url;
 
+  if (!manifest.scope.is_empty())
+    web_app_info->scope = manifest.scope;
+
   // If any icons are specified in the manifest, they take precedence over any
   // we picked up from the web_app stuff.
   if (!manifest.icons.empty()) {
@@ -805,6 +810,7 @@
   web_app_info.app_url = AppLaunchInfo::GetLaunchWebURL(extension);
   web_app_info.title = base::UTF8ToUTF16(extension->non_localized_name());
   web_app_info.description = base::UTF8ToUTF16(extension->description());
+  web_app_info.scope = GetScopeURLFromBookmarkApp(extension);
 
   const ExtensionIconSet& icon_set = extensions::IconsInfo::GetIcons(extension);
   std::vector<extensions::ImageLoader::ImageRepresentation> info_list;
diff --git a/chrome/browser/extensions/bookmark_app_helper_unittest.cc b/chrome/browser/extensions/bookmark_app_helper_unittest.cc
index da3146b2..277660d 100644
--- a/chrome/browser/extensions/bookmark_app_helper_unittest.cc
+++ b/chrome/browser/extensions/bookmark_app_helper_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/banners/app_banner_settings_helper.h"
+#include "chrome/browser/extensions/convert_web_app.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_service_test_base.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
@@ -29,6 +30,8 @@
 
 const char kAppUrl[] = "http://www.chromium.org";
 const char kAlternativeAppUrl[] = "http://www.notchromium.org";
+const char kAppScope[] = "http://www.chromium.org";
+const char kAppAlternativeScope[] = "http://www.chromium.org/new/";
 const char kAppTitle[] = "Test title";
 const char kAppShortName[] = "Test short name";
 const char kAlternativeAppTitle[] = "Different test title";
@@ -194,6 +197,7 @@
   EXPECT_EQ(original.title, newly_made.title);
   EXPECT_EQ(original.description, newly_made.description);
   EXPECT_EQ(original.app_url, newly_made.app_url);
+  EXPECT_EQ(original.scope, newly_made.scope);
   // There should be 6 icons, as there are three sizes which need to be
   // generated, and each will generate a 1x and 2x icon.
   EXPECT_EQ(6u, newly_made.icons.size());
@@ -360,6 +364,7 @@
   content::Manifest manifest;
   manifest.start_url = GURL(kAppUrl);
   manifest.name = base::NullableString16(base::UTF8ToUTF16(kAppTitle), false);
+  manifest.scope = GURL(kAppScope);
   helper.CompleteGetManifest(manifest);
 
   std::map<GURL, std::vector<SkBitmap> > icon_map;
@@ -374,6 +379,7 @@
   EXPECT_TRUE(extension->from_bookmark());
   EXPECT_EQ(kAppTitle, extension->name());
   EXPECT_EQ(GURL(kAppUrl), AppLaunchInfo::GetLaunchWebURL(extension));
+  EXPECT_EQ(GURL(kAppScope), GetScopeURLFromBookmarkApp(extension));
   EXPECT_FALSE(
       AppBannerSettingsHelper::GetSingleBannerEvent(
           contents.get(), manifest.start_url, manifest.start_url.spec(),
@@ -386,6 +392,7 @@
   web_app_info.app_url = GURL(kAppUrl);
   web_app_info.title = base::UTF8ToUTF16(kAppTitle);
   web_app_info.description = base::UTF8ToUTF16(kAppDescription);
+  web_app_info.scope = GURL(kAppScope);
   web_app_info.icons.push_back(
       CreateIconInfoWithBitmap(kIconSizeTiny, SK_ColorRED));
 
@@ -403,6 +410,7 @@
   EXPECT_EQ(kAppTitle, extension->name());
   EXPECT_EQ(kAppDescription, extension->description());
   EXPECT_EQ(GURL(kAppUrl), AppLaunchInfo::GetLaunchWebURL(extension));
+  EXPECT_EQ(GURL(kAppScope), GetScopeURLFromBookmarkApp(extension));
   EXPECT_FALSE(
       IconsInfo::GetIconResource(extension, kIconSizeTiny,
                                  ExtensionIconSet::MATCH_EXACTLY).empty());
@@ -428,6 +436,7 @@
   web_app_info.app_url = GURL(kAppUrl);
   web_app_info.title = base::UTF8ToUTF16(kAppTitle);
   web_app_info.description = base::UTF8ToUTF16(kAppDescription);
+  web_app_info.scope = GURL(kAppScope);
   web_app_info.icons.push_back(
       CreateIconInfoWithBitmap(kIconSizeSmall, SK_ColorRED));
 
@@ -442,6 +451,7 @@
     EXPECT_EQ(kAppTitle, extension->name());
     EXPECT_EQ(kAppDescription, extension->description());
     EXPECT_EQ(GURL(kAppUrl), AppLaunchInfo::GetLaunchWebURL(extension));
+    EXPECT_EQ(GURL(kAppScope), GetScopeURLFromBookmarkApp(extension));
     EXPECT_FALSE(extensions::IconsInfo::GetIconResource(
                      extension, kIconSizeSmall, ExtensionIconSet::MATCH_EXACTLY)
                      .empty());
@@ -449,6 +459,7 @@
 
   web_app_info.title = base::UTF8ToUTF16(kAlternativeAppTitle);
   web_app_info.icons[0] = CreateIconInfoWithBitmap(kIconSizeLarge, SK_ColorRED);
+  web_app_info.scope = GURL(kAppAlternativeScope);
 
   extensions::CreateOrUpdateBookmarkApp(service_, &web_app_info);
   content::RunAllBlockingPoolTasksUntilIdle();
@@ -461,6 +472,8 @@
     EXPECT_EQ(kAlternativeAppTitle, extension->name());
     EXPECT_EQ(kAppDescription, extension->description());
     EXPECT_EQ(GURL(kAppUrl), AppLaunchInfo::GetLaunchWebURL(extension));
+    EXPECT_EQ(GURL(kAppAlternativeScope),
+              GetScopeURLFromBookmarkApp(extension));
     EXPECT_FALSE(extensions::IconsInfo::GetIconResource(
                      extension, kIconSizeSmall, ExtensionIconSet::MATCH_EXACTLY)
                      .empty());
@@ -475,6 +488,7 @@
   web_app_info.app_url = GURL(kAppUrl);
   web_app_info.title = base::UTF8ToUTF16(kAppTitle);
   web_app_info.description = base::UTF8ToUTF16(kAppDescription);
+  web_app_info.scope = GURL(kAppScope);
 
   extensions::CreateOrUpdateBookmarkApp(service_, &web_app_info);
   content::RunAllBlockingPoolTasksUntilIdle();
diff --git a/chrome/browser/extensions/bookmark_app_url_redirector_browsertest.cc b/chrome/browser/extensions/bookmark_app_url_redirector_browsertest.cc
new file mode 100644
index 0000000..2265088f
--- /dev/null
+++ b/chrome/browser/extensions/bookmark_app_url_redirector_browsertest.cc
@@ -0,0 +1,259 @@
+// 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/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/extensions/bookmark_app_helper.h"
+#include "chrome/browser/extensions/extension_browsertest.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/web_application_info.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/test/test_utils.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/notification_types.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+
+namespace extensions {
+
+const char kAppUrlPath[] =
+    "/extensions/bookmark_apps/url_handlers/in_scope/index.html";
+const char kScopePath[] = "/extensions/bookmark_apps/url_handlers/in_scope/";
+const char kInScopeUrlPath[] =
+    "/extensions/bookmark_apps/url_handlers/in_scope/other.html";
+const char kOutOfScopeUrlPath[] =
+    "/extensions/bookmark_apps/url_handlers/out_of_scope/other.html";
+
+class BookmarkAppUrlRedirectorBrowserTest : public ExtensionBrowserTest {
+ public:
+  void SetUp() override {
+    scoped_feature_list_ = base::MakeUnique<base::test::ScopedFeatureList>();
+    scoped_feature_list_->InitAndEnableFeature(features::kDesktopPWAWindowing);
+    ExtensionBrowserTest::SetUp();
+  }
+
+  void InstallTestBookmarkApp() {
+    ASSERT_TRUE(embedded_test_server()->Start());
+    size_t num_extensions =
+        ExtensionRegistry::Get(profile())->enabled_extensions().size();
+
+    WebApplicationInfo web_app_info;
+    web_app_info.app_url = embedded_test_server()->GetURL(kAppUrlPath);
+    web_app_info.scope = embedded_test_server()->GetURL(kScopePath);
+    web_app_info.title = base::UTF8ToUTF16("Test app");
+    web_app_info.description = base::UTF8ToUTF16("Test description");
+
+    content::WindowedNotificationObserver windowed_observer(
+        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
+        content::NotificationService::AllSources());
+    extensions::CreateOrUpdateBookmarkApp(extension_service(), &web_app_info);
+    windowed_observer.Wait();
+
+    ASSERT_EQ(++num_extensions,
+              ExtensionRegistry::Get(profile())->enabled_extensions().size());
+  }
+
+  Browser* OpenTestBookmarkApp() {
+    GURL app_url = embedded_test_server()->GetURL(kAppUrlPath);
+    ui_test_utils::UrlLoadObserver url_observer(
+        app_url, content::NotificationService::AllSources());
+    ui_test_utils::NavigateToURL(browser(), app_url);
+    url_observer.Wait();
+
+    return chrome::FindLastActive();
+  }
+
+  void ResetFeatureList() { scoped_feature_list_.reset(); }
+
+ private:
+  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
+};
+
+// Tests that navigating to the Web App's app_url doesn't open a new window
+// if features::kDesktopPWAWindowing is disabled before installing the app.
+IN_PROC_BROWSER_TEST_F(BookmarkAppUrlRedirectorBrowserTest,
+                       FeatureDisable_BeforeInstall) {
+  ResetFeatureList();
+  InstallTestBookmarkApp();
+
+  int num_tabs = browser()->tab_strip_model()->count();
+  size_t num_browsers = chrome::GetBrowserCount(profile());
+
+  GURL app_url = embedded_test_server()->GetURL(kAppUrlPath);
+  ui_test_utils::UrlLoadObserver url_observer(
+      app_url, content::NotificationService::AllSources());
+  ui_test_utils::NavigateToURL(browser(), app_url);
+  url_observer.Wait();
+
+  EXPECT_EQ(num_tabs, browser()->tab_strip_model()->count());
+  EXPECT_EQ(num_browsers, chrome::GetBrowserCount(profile()));
+  EXPECT_EQ(browser(), chrome::FindLastActive());
+
+  EXPECT_EQ(app_url, browser()
+                         ->tab_strip_model()
+                         ->GetActiveWebContents()
+                         ->GetLastCommittedURL());
+}
+
+// Tests that navigating to the Web App's app_url doesn't open a new window
+// if features::kDesktopPWAWindowing is disabled after installing the app.
+IN_PROC_BROWSER_TEST_F(BookmarkAppUrlRedirectorBrowserTest,
+                       FeatureDisable_AfterInstall) {
+  InstallTestBookmarkApp();
+  ResetFeatureList();
+
+  int num_tabs = browser()->tab_strip_model()->count();
+  size_t num_browsers = chrome::GetBrowserCount(profile());
+
+  GURL app_url = embedded_test_server()->GetURL(kAppUrlPath);
+  ui_test_utils::UrlLoadObserver url_observer(
+      app_url, content::NotificationService::AllSources());
+  ui_test_utils::NavigateToURL(browser(), app_url);
+  url_observer.Wait();
+
+  EXPECT_EQ(num_tabs, browser()->tab_strip_model()->count());
+  EXPECT_EQ(num_browsers, chrome::GetBrowserCount(profile()));
+  EXPECT_EQ(browser(), chrome::FindLastActive());
+
+  EXPECT_EQ(app_url, browser()
+                         ->tab_strip_model()
+                         ->GetActiveWebContents()
+                         ->GetLastCommittedURL());
+}
+
+// Tests that navigating to the Web App's app_url opens a new browser window.
+IN_PROC_BROWSER_TEST_F(BookmarkAppUrlRedirectorBrowserTest, AppUrl) {
+  InstallTestBookmarkApp();
+
+  int num_tabs = browser()->tab_strip_model()->count();
+  size_t num_browsers = chrome::GetBrowserCount(profile());
+  GURL initial_url = browser()
+                         ->tab_strip_model()
+                         ->GetActiveWebContents()
+                         ->GetLastCommittedURL();
+
+  GURL app_url = embedded_test_server()->GetURL(kAppUrlPath);
+  ui_test_utils::UrlLoadObserver url_observer(
+      app_url, content::NotificationService::AllSources());
+  ui_test_utils::NavigateToURL(browser(), app_url);
+  url_observer.Wait();
+
+  EXPECT_EQ(num_tabs, browser()->tab_strip_model()->count());
+  EXPECT_EQ(++num_browsers, chrome::GetBrowserCount(profile()));
+  EXPECT_NE(browser(), chrome::FindLastActive());
+
+  EXPECT_EQ(initial_url, browser()
+                             ->tab_strip_model()
+                             ->GetActiveWebContents()
+                             ->GetLastCommittedURL());
+  EXPECT_EQ(app_url, chrome::FindLastActive()
+                         ->tab_strip_model()
+                         ->GetActiveWebContents()
+                         ->GetLastCommittedURL());
+}
+
+// Tests that navigating to a URL in the Web App's scope opens a new browser
+// window.
+IN_PROC_BROWSER_TEST_F(BookmarkAppUrlRedirectorBrowserTest, InScopeUrl) {
+  InstallTestBookmarkApp();
+
+  int num_tabs = browser()->tab_strip_model()->count();
+  size_t num_browsers = chrome::GetBrowserCount(profile());
+  GURL initial_url = browser()
+                         ->tab_strip_model()
+                         ->GetActiveWebContents()
+                         ->GetLastCommittedURL();
+
+  GURL in_scope_url = embedded_test_server()->GetURL(kInScopeUrlPath);
+  ui_test_utils::UrlLoadObserver url_observer(
+      in_scope_url, content::NotificationService::AllSources());
+  ui_test_utils::NavigateToURL(browser(), in_scope_url);
+  url_observer.Wait();
+
+  EXPECT_EQ(num_tabs, browser()->tab_strip_model()->count());
+  EXPECT_EQ(++num_browsers, chrome::GetBrowserCount(profile()));
+  EXPECT_NE(browser(), chrome::FindLastActive());
+
+  EXPECT_EQ(initial_url, browser()
+                             ->tab_strip_model()
+                             ->GetActiveWebContents()
+                             ->GetLastCommittedURL());
+  EXPECT_EQ(in_scope_url, chrome::FindLastActive()
+                              ->tab_strip_model()
+                              ->GetActiveWebContents()
+                              ->GetLastCommittedURL());
+}
+
+// Tests that navigating to a URL out of the Web App's scope but with the
+// same origin doesn't open a new browser window.
+IN_PROC_BROWSER_TEST_F(BookmarkAppUrlRedirectorBrowserTest, OutOfScopeUrl) {
+  InstallTestBookmarkApp();
+
+  int num_tabs = browser()->tab_strip_model()->count();
+  size_t num_browsers = chrome::GetBrowserCount(profile());
+
+  GURL out_of_scope_url = embedded_test_server()->GetURL(kOutOfScopeUrlPath);
+  ui_test_utils::UrlLoadObserver url_observer(
+      out_of_scope_url, content::NotificationService::AllSources());
+  ui_test_utils::NavigateToURL(browser(), out_of_scope_url);
+  url_observer.Wait();
+
+  EXPECT_EQ(num_tabs, browser()->tab_strip_model()->count());
+  EXPECT_EQ(num_browsers, chrome::GetBrowserCount(profile()));
+  EXPECT_EQ(browser(), chrome::FindLastActive());
+
+  EXPECT_EQ(out_of_scope_url, chrome::FindLastActive()
+                                  ->tab_strip_model()
+                                  ->GetActiveWebContents()
+                                  ->GetLastCommittedURL());
+}
+
+// Tests that navigating inside the app doesn't open new browser windows.
+IN_PROC_BROWSER_TEST_F(BookmarkAppUrlRedirectorBrowserTest, InAppNavigation) {
+  InstallTestBookmarkApp();
+  Browser* app_browser = OpenTestBookmarkApp();
+
+  int num_tabs_browser = browser()->tab_strip_model()->count();
+  int num_tabs_app_browser = app_browser->tab_strip_model()->count();
+  size_t num_browsers = chrome::GetBrowserCount(profile());
+
+  {
+    GURL in_scope_url = embedded_test_server()->GetURL(kInScopeUrlPath);
+    ui_test_utils::UrlLoadObserver url_observer(
+        in_scope_url, content::NotificationService::AllSources());
+    ui_test_utils::NavigateToURL(app_browser, in_scope_url);
+    url_observer.Wait();
+
+    EXPECT_EQ(num_tabs_browser, browser()->tab_strip_model()->count());
+    EXPECT_EQ(num_tabs_app_browser, app_browser->tab_strip_model()->count());
+    EXPECT_EQ(num_browsers, chrome::GetBrowserCount(profile()));
+    EXPECT_EQ(app_browser, chrome::FindLastActive());
+
+    EXPECT_EQ(in_scope_url, app_browser->tab_strip_model()
+                                ->GetActiveWebContents()
+                                ->GetLastCommittedURL());
+  }
+
+  {
+    GURL out_of_scope_url = embedded_test_server()->GetURL(kOutOfScopeUrlPath);
+    ui_test_utils::UrlLoadObserver url_observer(
+        out_of_scope_url, content::NotificationService::AllSources());
+    ui_test_utils::NavigateToURL(app_browser, out_of_scope_url);
+    url_observer.Wait();
+
+    EXPECT_EQ(num_tabs_browser, browser()->tab_strip_model()->count());
+    EXPECT_EQ(num_tabs_app_browser, app_browser->tab_strip_model()->count());
+    EXPECT_EQ(num_browsers, chrome::GetBrowserCount(profile()));
+    EXPECT_EQ(app_browser, chrome::FindLastActive());
+
+    EXPECT_EQ(out_of_scope_url, app_browser->tab_strip_model()
+                                    ->GetActiveWebContents()
+                                    ->GetLastCommittedURL());
+  }
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/convert_web_app.cc b/chrome/browser/extensions/convert_web_app.cc
index e8304f2..a667ba8e 100644
--- a/chrome/browser/extensions/convert_web_app.cc
+++ b/chrome/browser/extensions/convert_web_app.cc
@@ -27,6 +27,7 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "chrome/common/chrome_paths.h"
+#include "chrome/common/extensions/api/url_handlers/url_handlers_parser.h"
 #include "chrome/common/web_application_info.h"
 #include "crypto/sha2.h"
 #include "extensions/common/constants.h"
@@ -47,6 +48,7 @@
 namespace {
 
 const char kIconsDirName[] = "icons";
+const char kScopeUrlHandlerId[] = "scope";
 
 // Create the public key for the converted web app.
 //
@@ -66,6 +68,54 @@
 
 }  // namespace
 
+std::unique_ptr<base::DictionaryValue> CreateURLHandlersForBookmarkApp(
+    const GURL& scope_url,
+    const base::string16& title) {
+  auto matches = base::MakeUnique<base::ListValue>();
+  matches->AppendString(scope_url.GetOrigin().Resolve(scope_url.path()).spec() +
+                        "*");
+
+  auto scope_handler = base::MakeUnique<base::DictionaryValue>();
+  scope_handler->SetList(keys::kMatches, std::move(matches));
+  // The URL handler title is not used anywhere but we set it to the
+  // web app's title just in case.
+  scope_handler->SetString(keys::kUrlHandlerTitle, base::UTF16ToUTF8(title));
+
+  auto url_handlers = base::MakeUnique<base::DictionaryValue>();
+  // Use "scope" as the url handler's identifier.
+  url_handlers->SetDictionary(kScopeUrlHandlerId, std::move(scope_handler));
+  return url_handlers;
+}
+
+GURL GetScopeURLFromBookmarkApp(const Extension* extension) {
+  DCHECK(extension->from_bookmark());
+  const std::vector<UrlHandlerInfo>* url_handlers =
+      UrlHandlers::GetUrlHandlers(extension);
+  if (!url_handlers)
+    return GURL();
+
+  // A Bookmark app created by us should only have a url_handler with id
+  // kScopeUrlHandlerId. This URL handler should have a single pattern which
+  // corresponds to the web manifest's scope. The URL handler's pattern should
+  // be the Web Manifest's scope's origin + path with a wildcard, '*', appended
+  // to it.
+  auto handler_it = std::find_if(
+      url_handlers->begin(), url_handlers->end(),
+      [](const UrlHandlerInfo& info) { return info.id == kScopeUrlHandlerId; });
+  if (handler_it == url_handlers->end()) {
+    return GURL();
+  }
+
+  const auto& patterns = handler_it->patterns;
+  DCHECK(patterns.size() == 1);
+  const auto& pattern_iter = patterns.begin();
+  // Remove the '*' character at the end (which was added when creating the URL
+  // handler, see CreateURLHandlersForBookmarkApp()).
+  const std::string& pattern_str = pattern_iter->GetAsString();
+  DCHECK_EQ(pattern_str.back(), '*');
+  return GURL(pattern_str.substr(0, pattern_str.size() - 1));
+}
+
 // Generates a version for the converted app using the current date. This isn't
 // really needed, but it seems like useful information.
 std::string ConvertTimeToExtensionVersion(const Time& create_time) {
@@ -117,6 +167,11 @@
                                              web_app.generated_icon_color));
   }
 
+  if (!web_app.scope.is_empty()) {
+    root->SetDictionary(keys::kUrlHandlers, CreateURLHandlersForBookmarkApp(
+                                                web_app.scope, web_app.title));
+  }
+
   // Add the icons and linked icon information.
   auto icons = base::MakeUnique<base::DictionaryValue>();
   auto linked_icons = base::MakeUnique<base::ListValue>();
diff --git a/chrome/browser/extensions/convert_web_app.h b/chrome/browser/extensions/convert_web_app.h
index 7c9551a..55ee03da 100644
--- a/chrome/browser/extensions/convert_web_app.h
+++ b/chrome/browser/extensions/convert_web_app.h
@@ -10,15 +10,45 @@
 #include "base/memory/ref_counted.h"
 
 namespace base {
+class DictionaryValue;
 class FilePath;
 class Time;
 }
 
+class GURL;
 struct WebApplicationInfo;
 
 namespace extensions {
 class Extension;
 
+// Creates a DictionaryValue with a single URL handler for |scope_url| and
+// |title|. |title| is meant to appear in relevant UI surfaces but it's not used
+// anywhere yet. The resulting DictionaryValue can be used as the "url_handlers"
+// field in a Chrome Apps manifest.
+//
+// To create a URL handler that will match the same URLs as the "within
+// scope" algorithm of the Web Manifest spec, we remove everything
+// but the origin and path and append a wildcard, i.e. "*", to the result.
+// According to the Web Manifest spec, a URL |url| is within scope of
+// |scope_url| if |url|'s origin is the same as |scope_url|'s origin and
+// |url|'s path starts with |scope_url|'s path.
+// Note that this results in some unexpected URLs being within scope
+// according to the spec:
+// Suppose |scope_url| is "https://example.com/foo" and |url| is
+// "https://example.com/foobar.html", then according to the spec algorithm
+// |url| is within scope.
+// See https://github.com/w3c/manifest/issues/554 for details.
+//
+// GetScopeURLFromBookmarkApp() reverses this operation, i.e. removes
+// the '*' from the scope URL handler, to retrieve the scope for a Bookmark App.
+// So if you change this, you also have to change GetScopeURLFromBookmarkApp().
+std::unique_ptr<base::DictionaryValue> CreateURLHandlersForBookmarkApp(
+    const GURL& scope_url,
+    const base::string16& title);
+
+// Retrieves the scope URL from a Bookmark App's URL handlers.
+GURL GetScopeURLFromBookmarkApp(const Extension* extension);
+
 // Generates a version number for an extension from a time. The goal is to make
 // use of the version number to communicate the date in a human readable form,
 // while maintaining high enough resolution to change each time an app is
diff --git a/chrome/browser/extensions/convert_web_app_unittest.cc b/chrome/browser/extensions/convert_web_app_unittest.cc
index 5addbde..7fc4082 100644
--- a/chrome/browser/extensions/convert_web_app_unittest.cc
+++ b/chrome/browser/extensions/convert_web_app_unittest.cc
@@ -13,17 +13,20 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "base/path_service.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "base/version.h"
+#include "chrome/browser/extensions/bookmark_app_helper.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/common/web_application_info.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_icon_set.h"
 #include "extensions/common/extension_resource.h"
+#include "extensions/common/manifest_constants.h"
 #include "extensions/common/manifest_handlers/icons_handler.h"
 #include "extensions/common/permissions/permission_set.h"
 #include "extensions/common/permissions/permissions_data.h"
@@ -34,6 +37,8 @@
 
 namespace extensions {
 
+namespace keys = manifest_keys;
+
 namespace {
 
 // Returns an icon info corresponding to a canned icon.
@@ -87,6 +92,141 @@
 
 }  // namespace
 
+TEST(ExtensionFromWebApp, GetScopeURLFromBookmarkApp) {
+  base::ScopedTempDir extensions_dir;
+  ASSERT_TRUE(extensions_dir.CreateUniqueTempDir());
+
+  base::DictionaryValue manifest;
+  manifest.SetString(keys::kName, "Test App");
+  manifest.SetString(keys::kVersion, "0");
+  manifest.SetString(keys::kLaunchWebURL, "http://aaronboodman.com/gearpad/");
+
+  // Create a "url_handlers" dictionary with one URL handler generated from
+  // the scope.
+  // {
+  //   "scope": {
+  //     "matches": [ "http://aaronboodman.com/gearpad/*" ],
+  //     "title": "Test App"
+  //   },
+  // }
+  GURL scope_url = GURL("http://aaronboodman.com/gearpad/");
+  manifest.SetDictionary(keys::kUrlHandlers,
+                         CreateURLHandlersForBookmarkApp(
+                             scope_url, base::ASCIIToUTF16("Test App")));
+
+  std::string error;
+  scoped_refptr<Extension> bookmark_app =
+      Extension::Create(extensions_dir.GetPath(), Manifest::INTERNAL, manifest,
+                        Extension::FROM_BOOKMARK, &error);
+  ASSERT_TRUE(bookmark_app.get());
+
+  EXPECT_EQ(scope_url, GetScopeURLFromBookmarkApp(bookmark_app.get()));
+}
+
+TEST(ExtensionFromWebApp, GetScopeURLFromBookmarkApp_NoURLHandlers) {
+  base::ScopedTempDir extensions_dir;
+  ASSERT_TRUE(extensions_dir.CreateUniqueTempDir());
+
+  base::DictionaryValue manifest;
+  manifest.SetString(keys::kName, "Test App");
+  manifest.SetString(keys::kVersion, "0");
+  manifest.SetString(keys::kLaunchWebURL, "http://aaronboodman.com/gearpad/");
+  manifest.SetDictionary(keys::kUrlHandlers,
+                         base::MakeUnique<base::DictionaryValue>());
+
+  std::string error;
+  scoped_refptr<Extension> bookmark_app =
+      Extension::Create(extensions_dir.GetPath(), Manifest::INTERNAL, manifest,
+                        Extension::FROM_BOOKMARK, &error);
+  ASSERT_TRUE(bookmark_app.get());
+
+  EXPECT_EQ(GURL(), GetScopeURLFromBookmarkApp(bookmark_app.get()));
+}
+
+TEST(ExtensionFromWebApp, GetScopeURLFromBookmarkApp_WrongURLHandler) {
+  base::ScopedTempDir extensions_dir;
+  ASSERT_TRUE(extensions_dir.CreateUniqueTempDir());
+
+  base::DictionaryValue manifest;
+  manifest.SetString(keys::kName, "Test App");
+  manifest.SetString(keys::kVersion, "0");
+  manifest.SetString(keys::kLaunchWebURL, "http://aaronboodman.com/gearpad/");
+
+  // Create a "url_handlers" dictionary with one URL handler not generated
+  // from the scope.
+  // {
+  //   "test_url_handler": {
+  //     "matches": [ "http://*.aaronboodman.com/" ],
+  //     "title": "test handler"
+  //   }
+  // }
+  auto test_matches = base::MakeUnique<base::ListValue>();
+  test_matches->AppendString("http://*.aaronboodman.com/");
+
+  auto test_handler = base::MakeUnique<base::DictionaryValue>();
+  test_handler->SetList(keys::kMatches, std::move(test_matches));
+  test_handler->SetString(keys::kUrlHandlerTitle, "test handler");
+
+  auto url_handlers = base::MakeUnique<base::DictionaryValue>();
+  url_handlers->SetDictionary("test_url_handler", std::move(test_handler));
+  manifest.SetDictionary(keys::kUrlHandlers, std::move(url_handlers));
+
+  std::string error;
+  scoped_refptr<Extension> bookmark_app =
+      Extension::Create(extensions_dir.GetPath(), Manifest::INTERNAL, manifest,
+                        Extension::FROM_BOOKMARK, &error);
+  ASSERT_TRUE(bookmark_app.get());
+
+  EXPECT_EQ(GURL(), GetScopeURLFromBookmarkApp(bookmark_app.get()));
+}
+
+TEST(ExtensionFromWebApp, GetScopeURLFromBookmarkApp_ExtraURLHandler) {
+  base::ScopedTempDir extensions_dir;
+  ASSERT_TRUE(extensions_dir.CreateUniqueTempDir());
+
+  base::DictionaryValue manifest;
+  manifest.SetString(keys::kName, "Test App");
+  manifest.SetString(keys::kVersion, "0");
+  manifest.SetString(keys::kLaunchWebURL, "http://aaronboodman.com/gearpad/");
+
+  // Create a "url_handlers" dictionary with two URL handlers. One for
+  // the scope and and extra one for testing.
+  // {
+  //   "scope": {
+  //     "matches": [ "http://aaronboodman.com/gearpad/*" ],
+  //     "title": "Test App"
+  //   },
+  //   "test_url_handler": {
+  //     "matches": [ "http://*.aaronboodman.com/" ],
+  //     "title": "test handler"
+  //   }
+  // }
+  GURL scope_url = GURL("http://aaronboodman.com/gearpad/");
+  std::unique_ptr<base::DictionaryValue> url_handlers =
+      CreateURLHandlersForBookmarkApp(scope_url,
+                                      base::ASCIIToUTF16("Test App"));
+
+  auto test_matches = base::MakeUnique<base::ListValue>();
+  test_matches->AppendString("http://*.aaronboodman.com/");
+
+  auto test_handler = base::MakeUnique<base::DictionaryValue>();
+  test_handler->SetList(keys::kMatches, std::move(test_matches));
+  test_handler->SetString(keys::kUrlHandlerTitle, "test handler");
+
+  url_handlers->SetDictionary("test_url_handler", std::move(test_handler));
+  manifest.SetDictionary(keys::kUrlHandlers, std::move(url_handlers));
+
+  std::string error;
+  scoped_refptr<Extension> bookmark_app =
+      Extension::Create(extensions_dir.GetPath(), Manifest::INTERNAL, manifest,
+                        Extension::FROM_BOOKMARK, &error);
+  ASSERT_TRUE(bookmark_app.get());
+
+  // Check that we can retrieve the scope even if there is an extra
+  // url handler.
+  EXPECT_EQ(scope_url, GetScopeURLFromBookmarkApp(bookmark_app.get()));
+}
+
 TEST(ExtensionFromWebApp, GenerateVersion) {
   EXPECT_EQ("2010.1.1.0",
             ConvertTimeToExtensionVersion(
@@ -108,6 +248,7 @@
   web_app.description =
       base::ASCIIToUTF16("The best text editor in the universe!");
   web_app.app_url = GURL("http://aaronboodman.com/gearpad/");
+  web_app.scope = GURL("http://aaronboodman.com/gearpad/");
 
   const int sizes[] = {16, 48, 128};
   for (size_t i = 0; i < arraysize(sizes); ++i) {
@@ -134,6 +275,7 @@
   EXPECT_EQ(base::UTF16ToUTF8(web_app.title), extension->name());
   EXPECT_EQ(base::UTF16ToUTF8(web_app.description), extension->description());
   EXPECT_EQ(web_app.app_url, AppLaunchInfo::GetFullLaunchURL(extension.get()));
+  EXPECT_EQ(web_app.scope, GetScopeURLFromBookmarkApp(extension.get()));
   EXPECT_EQ(0u,
             extension->permissions_data()->active_permissions().apis().size());
   ASSERT_EQ(0u, extension->web_extent().patterns().size());
@@ -179,10 +321,31 @@
   EXPECT_EQ(base::UTF16ToUTF8(web_app.title), extension->name());
   EXPECT_EQ("", extension->description());
   EXPECT_EQ(web_app.app_url, AppLaunchInfo::GetFullLaunchURL(extension.get()));
+  EXPECT_TRUE(GetScopeURLFromBookmarkApp(extension.get()).is_empty());
   EXPECT_EQ(0u, IconsInfo::GetIcons(extension.get()).map().size());
   EXPECT_EQ(0u,
             extension->permissions_data()->active_permissions().apis().size());
   ASSERT_EQ(0u, extension->web_extent().patterns().size());
 }
 
+// Tests that a scope not ending in "/" works correctly.
+// The tested behavior is unexpected but is working correctly according
+// to the Web Manifest spec. https://github.com/w3c/manifest/issues/554
+TEST(ExtensionFromWebApp, ScopeDoesNotEndInSlash) {
+  base::ScopedTempDir extensions_dir;
+  ASSERT_TRUE(extensions_dir.CreateUniqueTempDir());
+
+  WebApplicationInfo web_app;
+  web_app.title = base::ASCIIToUTF16("Gearpad");
+  web_app.description =
+      base::ASCIIToUTF16("The best text editor in the universe!");
+  web_app.app_url = GURL("http://aaronboodman.com/gearpad/");
+  web_app.scope = GURL("http://aaronboodman.com/gear");
+
+  scoped_refptr<Extension> extension = ConvertWebAppToExtension(
+      web_app, GetTestTime(1978, 12, 11, 0, 0, 0, 0), extensions_dir.GetPath());
+  ASSERT_TRUE(extension.get());
+  EXPECT_EQ(web_app.scope, GetScopeURLFromBookmarkApp(extension.get()));
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/crx_installer_browsertest.cc b/chrome/browser/extensions/crx_installer_browsertest.cc
index cb7c50df..7425df7 100644
--- a/chrome/browser/extensions/crx_installer_browsertest.cc
+++ b/chrome/browser/extensions/crx_installer_browsertest.cc
@@ -141,6 +141,7 @@
   web_app_info.title = base::UTF8ToUTF16(title);
   web_app_info.description = base::UTF8ToUTF16(description);
   web_app_info.app_url = GURL(app_url);
+  web_app_info.scope = GURL(app_url);
 
   web_app_info.icons.push_back(CreateIconInfoWithBitmap(size));
 
diff --git a/chrome/browser/extensions/extension_sync_data.cc b/chrome/browser/extensions/extension_sync_data.cc
index 43def385..209cb21 100644
--- a/chrome/browser/extensions/extension_sync_data.cc
+++ b/chrome/browser/extensions/extension_sync_data.cc
@@ -7,6 +7,7 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/stringprintf.h"
+#include "chrome/browser/extensions/convert_web_app.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/common/extensions/manifest_handlers/app_icon_color_info.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
@@ -125,6 +126,7 @@
   if (is_app_ && extension.from_bookmark()) {
     bookmark_app_description_ = extension.description();
     bookmark_app_url_ = AppLaunchInfo::GetLaunchWebURL(&extension).spec();
+    bookmark_app_scope_ = GetScopeURLFromBookmarkApp(&extension).spec();
     bookmark_app_icon_color_ = AppIconColorInfo::GetIconColorString(&extension);
     extensions::LinkedAppIcons icons =
         LinkedAppIcons::GetLinkedAppIcons(&extension);
@@ -219,6 +221,9 @@
   if (!bookmark_app_description_.empty())
     specifics->set_bookmark_app_description(bookmark_app_description_);
 
+  if (!bookmark_app_scope_.empty())
+    specifics->set_bookmark_app_scope(bookmark_app_scope_);
+
   if (!bookmark_app_icon_color_.empty())
     specifics->set_bookmark_app_icon_color(bookmark_app_icon_color_);
 
@@ -295,6 +300,7 @@
 
   bookmark_app_url_ = specifics.bookmark_app_url();
   bookmark_app_description_ = specifics.bookmark_app_description();
+  bookmark_app_scope_ = specifics.bookmark_app_url();
   bookmark_app_icon_color_ = specifics.bookmark_app_icon_color();
 
   for (int i = 0; i < specifics.linked_app_icons_size(); ++i) {
diff --git a/chrome/browser/extensions/extension_sync_data.h b/chrome/browser/extensions/extension_sync_data.h
index 24f7d40..5578b7f1 100644
--- a/chrome/browser/extensions/extension_sync_data.h
+++ b/chrome/browser/extensions/extension_sync_data.h
@@ -118,6 +118,7 @@
   const std::string& bookmark_app_description() const {
     return bookmark_app_description_;
   }
+  const std::string& bookmark_app_scope() const { return bookmark_app_scope_; }
   const std::string& bookmark_app_icon_color() const {
     return bookmark_app_icon_color_;
   }
@@ -168,6 +169,7 @@
   extensions::LaunchType launch_type_;
   std::string bookmark_app_url_;
   std::string bookmark_app_description_;
+  std::string bookmark_app_scope_;
   std::string bookmark_app_icon_color_;
   std::vector<LinkedAppIconInfo> linked_icons_;
 };
diff --git a/chrome/browser/extensions/extension_sync_service.cc b/chrome/browser/extensions/extension_sync_service.cc
index fd538cb..602a43b 100644
--- a/chrome/browser/extensions/extension_sync_service.cc
+++ b/chrome/browser/extensions/extension_sync_service.cc
@@ -570,6 +570,7 @@
   web_app_info.title = base::UTF8ToUTF16(extension_sync_data.name());
   web_app_info.description =
       base::UTF8ToUTF16(extension_sync_data.bookmark_app_description());
+  web_app_info.scope = GURL(extension_sync_data.bookmark_app_scope());
   if (!extension_sync_data.bookmark_app_icon_color().empty()) {
     extensions::image_util::ParseHexColorString(
         extension_sync_data.bookmark_app_icon_color(),
diff --git a/chrome/browser/predictors/autocomplete_action_predictor_unittest.cc b/chrome/browser/predictors/autocomplete_action_predictor_unittest.cc
index 49cef54..7c9bed4dd 100644
--- a/chrome/browser/predictors/autocomplete_action_predictor_unittest.cc
+++ b/chrome/browser/predictors/autocomplete_action_predictor_unittest.cc
@@ -18,6 +18,8 @@
 #include "base/time/time.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/prerender/prerender_field_trial.h"
+#include "chrome/browser/prerender/prerender_manager.h"
+#include "chrome/browser/prerender/prerender_test_utils.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/history/core/browser/history_service.h"
@@ -348,6 +350,9 @@
 
   AutocompleteMatch match;
   match.type = AutocompleteMatchType::HISTORY_URL;
+  prerender::test_utils::RestorePrerenderMode restore_prerender_mode;
+  prerender::PrerenderManager::SetMode(
+      prerender::PrerenderManager::PRERENDER_MODE_NOSTATE_PREFETCH);
 
   for (size_t i = 0; i < arraysize(test_url_db); ++i) {
     match.destination_url = GURL(test_url_db[i].url);
@@ -362,6 +367,9 @@
 
   AutocompleteMatch match;
   match.type = AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED;
+  prerender::test_utils::RestorePrerenderMode restore_prerender_mode;
+  prerender::PrerenderManager::SetMode(
+      prerender::PrerenderManager::PRERENDER_MODE_NOSTATE_PREFETCH);
 
   for (size_t i = 0; i < arraysize(test_url_db); ++i) {
     match.destination_url = GURL(test_url_db[i].url);
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
index 295cfec..312d2bb9 100644
--- a/chrome/browser/prerender/prerender_browsertest.cc
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -599,6 +599,10 @@
     test_utils::PrerenderInProcessBrowserTest::SetUpOnMainThread();
     prerender::PrerenderManager::SetMode(
         prerender::PrerenderManager::PRERENDER_MODE_ENABLED);
+    prerender::PrerenderManager::SetInstantMode(
+        prerender::PrerenderManager::PRERENDER_MODE_ENABLED);
+    prerender::PrerenderManager::SetOmniboxMode(
+        prerender::PrerenderManager::PRERENDER_MODE_ENABLED);
     const testing::TestInfo* const test_info =
         testing::UnitTest::GetInstance()->current_test_info();
     // This one test fails with the host resolver redirecting all hosts.
diff --git a/chrome/browser/prerender/prerender_field_trial.cc b/chrome/browser/prerender/prerender_field_trial.cc
index 56835cf..a152c60e 100644
--- a/chrome/browser/prerender/prerender_field_trial.cc
+++ b/chrome/browser/prerender/prerender_field_trial.cc
@@ -71,7 +71,7 @@
 void ConfigurePrerender() {
   PrerenderManager::PrerenderManagerMode overall_mode =
       ParsePrerenderMode(kNoStatePrefetchFeatureModeParameterName,
-                         PrerenderManager::PRERENDER_MODE_ENABLED);
+                         PrerenderManager::PRERENDER_MODE_DISABLED);
 
   PrerenderManager::SetMode(overall_mode);
   PrerenderManager::SetInstantMode(ParsePrerenderMode(
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc
index f0b3455..53bd31e4 100644
--- a/chrome/browser/prerender/prerender_manager.cc
+++ b/chrome/browser/prerender/prerender_manager.cc
@@ -166,11 +166,11 @@
 
 // static
 PrerenderManager::PrerenderManagerMode PrerenderManager::mode_ =
-    PRERENDER_MODE_ENABLED;
+    PRERENDER_MODE_DISABLED;
 PrerenderManager::PrerenderManagerMode PrerenderManager::instant_mode_ =
-    PRERENDER_MODE_ENABLED;
+    PRERENDER_MODE_DISABLED;
 PrerenderManager::PrerenderManagerMode PrerenderManager::omnibox_mode_ =
-    PRERENDER_MODE_ENABLED;
+    PRERENDER_MODE_DISABLED;
 
 struct PrerenderManager::NavigationRecord {
   NavigationRecord(const GURL& url, base::TimeTicks time, Origin origin)
diff --git a/chrome/browser/prerender/prerender_unittest.cc b/chrome/browser/prerender/prerender_unittest.cc
index 9eac021b..2cc5e9e 100644
--- a/chrome/browser/prerender/prerender_unittest.cc
+++ b/chrome/browser/prerender/prerender_unittest.cc
@@ -462,22 +462,16 @@
 
 TEST_F(PrerenderTest, PrerenderRespectsDisableFlag) {
   test_utils::RestorePrerenderMode restore_prerender_mode;
-  ASSERT_TRUE(PrerenderManager::IsAnyPrerenderingPossible());
-  ASSERT_EQ(PrerenderManager::PRERENDER_MODE_ENABLED,
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(kNoStatePrefetchFeature);
+  prerender::ConfigurePrerender();
+  EXPECT_FALSE(PrerenderManager::IsAnyPrerenderingPossible());
+  EXPECT_EQ(PrerenderManager::PRERENDER_MODE_DISABLED,
             PrerenderManager::GetMode(ORIGIN_NONE));
-
-  {
-    base::test::ScopedFeatureList scoped_feature_list;
-    scoped_feature_list.InitAndDisableFeature(kNoStatePrefetchFeature);
-    prerender::ConfigurePrerender();
-    EXPECT_FALSE(PrerenderManager::IsAnyPrerenderingPossible());
-    EXPECT_EQ(PrerenderManager::PRERENDER_MODE_DISABLED,
-              PrerenderManager::GetMode(ORIGIN_NONE));
-    EXPECT_EQ(PrerenderManager::PRERENDER_MODE_DISABLED,
-              PrerenderManager::GetMode(ORIGIN_OMNIBOX));
-    EXPECT_EQ(PrerenderManager::PRERENDER_MODE_DISABLED,
-              PrerenderManager::GetMode(ORIGIN_INSTANT));
-  }
+  EXPECT_EQ(PrerenderManager::PRERENDER_MODE_DISABLED,
+            PrerenderManager::GetMode(ORIGIN_OMNIBOX));
+  EXPECT_EQ(PrerenderManager::PRERENDER_MODE_DISABLED,
+            PrerenderManager::GetMode(ORIGIN_INSTANT));
 }
 
 TEST_F(PrerenderTest, PrerenderRespectsFieldTrialParameters) {
@@ -603,6 +597,7 @@
 TEST_F(PrerenderTest, PrerenderRespectsThirdPartyCookiesPref) {
   GURL url("http://www.google.com/");
   test_utils::RestorePrerenderMode restore_prerender_mode;
+  PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_ENABLED);
   ASSERT_TRUE(PrerenderManager::IsAnyPrerenderingPossible());
 
   profile()->GetPrefs()->SetBoolean(prefs::kBlockThirdPartyCookies, true);
@@ -614,6 +609,7 @@
 TEST_F(PrerenderTest, OfflinePrerenderIgnoresThirdPartyCookiesPref) {
   GURL url("http://www.google.com/");
   test_utils::RestorePrerenderMode restore_prerender_mode;
+  PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_ENABLED);
   ASSERT_TRUE(PrerenderManager::IsAnyPrerenderingPossible());
 
   profile()->GetPrefs()->SetBoolean(prefs::kBlockThirdPartyCookies, true);
@@ -715,6 +711,7 @@
 
 TEST_F(PrerenderTest, PrerenderDisabledOnLowEndDevice) {
   GURL url("http://www.google.com/");
+  prerender_manager()->SetMode(PrerenderManager::PRERENDER_MODE_ENABLED);
   ASSERT_TRUE(PrerenderManager::IsAnyPrerenderingPossible());
   prerender_manager()->SetIsLowEndDevice(true);
   EXPECT_FALSE(AddSimplePrerender(url));
@@ -724,6 +721,7 @@
 
 TEST_F(PrerenderTest, OfflinePrerenderPossibleOnLowEndDevice) {
   GURL url("http://www.google.com/");
+  prerender_manager()->SetMode(PrerenderManager::PRERENDER_MODE_ENABLED);
   ASSERT_TRUE(PrerenderManager::IsAnyPrerenderingPossible());
 
   prerender_manager()->SetIsLowEndDevice(true);
@@ -1393,6 +1391,8 @@
 
 TEST_F(PrerenderTest, OmniboxNotAllowedWhenDisabled) {
   DisablePrerender();
+  test_utils::RestorePrerenderMode restore_prerender_mode;
+  PrerenderManager::SetOmniboxMode(PrerenderManager::PRERENDER_MODE_ENABLED);
   EXPECT_FALSE(prerender_manager()->AddPrerenderFromOmnibox(
       GURL("http://www.example.com"), nullptr, gfx::Size()));
   histogram_tester().ExpectUniqueSample("Prerender.FinalStatus",
diff --git a/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc b/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc
index 57dd9473..180eb1f 100644
--- a/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc
@@ -370,7 +370,7 @@
   ASSERT_TRUE(AppsMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, BookmarkApp) {
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, BookmarkAppBasic) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameApps());
 
@@ -378,7 +378,8 @@
       GetExtensionRegistry(GetProfile(0))->enabled_extensions().size();
 
   WebApplicationInfo web_app_info;
-  web_app_info.app_url = GURL("http://www.chromium.org");
+  web_app_info.app_url = GURL("http://www.chromium.org/");
+  web_app_info.scope = GURL("http://www.chromium.org/");
   web_app_info.title = base::UTF8ToUTF16("Test name");
   web_app_info.description = base::UTF8ToUTF16("Test description");
   ++num_extensions;
@@ -401,6 +402,36 @@
   }
 }
 
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, BookmarkAppMinimal) {
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AllProfilesHaveSameApps());
+
+  size_t num_extensions =
+      GetExtensionRegistry(GetProfile(0))->enabled_extensions().size();
+
+  WebApplicationInfo web_app_info;
+  web_app_info.app_url = GURL("http://www.chromium.org/");
+  web_app_info.title = base::UTF8ToUTF16("Test name");
+  ++num_extensions;
+  {
+    content::WindowedNotificationObserver windowed_observer(
+        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
+        content::NotificationService::AllSources());
+    extensions::CreateOrUpdateBookmarkApp(GetExtensionService(GetProfile(0)),
+                                          &web_app_info);
+    windowed_observer.Wait();
+    EXPECT_EQ(num_extensions,
+              GetExtensionRegistry(GetProfile(0))->enabled_extensions().size());
+  }
+  {
+    // Wait for the synced app to install.
+    content::WindowedNotificationObserver windowed_observer(
+        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
+        base::Bind(&AllProfilesHaveSameApps));
+    windowed_observer.Wait();
+  }
+}
+
 // TODO(akalin): Add tests exercising:
 //   - Offline installation/uninstallation behavior
 //   - App-specific properties
diff --git a/chrome/browser/ui/extensions/application_launch.cc b/chrome/browser/ui/extensions/application_launch.cc
index b4405a80..f7cacafa 100644
--- a/chrome/browser/ui/extensions/application_launch.cc
+++ b/chrome/browser/ui/extensions/application_launch.cc
@@ -124,6 +124,23 @@
                                         ExtensionRegistry::TERMINATED);
 }
 
+bool IsAllowedToOverrideURL(const extensions::Extension* extension,
+                            const GURL& override_url) {
+  if (extension->web_extent().MatchesURL(override_url))
+    return true;
+
+  if (override_url.GetOrigin() == extension->url())
+    return true;
+
+  if (extension->from_bookmark() &&
+      extensions::AppLaunchInfo::GetFullLaunchURL(extension).GetOrigin() ==
+          override_url.GetOrigin()) {
+    return true;
+  }
+
+  return false;
+}
+
 // Get the launch URL for a given extension, with optional override/fallback.
 // |override_url|, if non-empty, will be preferred over the extension's
 // launch url.
@@ -134,8 +151,7 @@
 
   GURL url;
   if (!override_url.is_empty()) {
-    DCHECK(extension->web_extent().MatchesURL(override_url) ||
-           override_url.GetOrigin() == extension->url());
+    DCHECK(IsAllowedToOverrideURL(extension, override_url));
     url = override_url;
   } else {
     url = extensions::AppLaunchInfo::GetFullLaunchURL(extension);
diff --git a/chrome/browser/ui/search/instant_search_prerenderer_unittest.cc b/chrome/browser/ui/search/instant_search_prerenderer_unittest.cc
index 55cc8da..abcf4e7 100644
--- a/chrome/browser/ui/search/instant_search_prerenderer_unittest.cc
+++ b/chrome/browser/ui/search/instant_search_prerenderer_unittest.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/prerender/prerender_manager_factory.h"
 #include "chrome/browser/prerender/prerender_origin.h"
 #include "chrome/browser/prerender/prerender_tab_helper.h"
+#include "chrome/browser/prerender/prerender_test_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/instant_service.h"
 #include "chrome/browser/search/instant_unittest_base.h"
@@ -187,12 +188,25 @@
   InstantSearchPrerendererTest() {}
 
  protected:
+  using RestorePrerenderMode = prerender::test_utils::RestorePrerenderMode;
+
   void SetUp() override {
     ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial("EmbeddedSearch",
                                                        "Group1 strk:20"));
+
+    // Prerender mode is stored in a few static variables. Remember the default
+    // mode to restore it later in TearDown() to avoid affecting other tests.
+    restore_prerender_mode_ = base::MakeUnique<RestorePrerenderMode>();
+    PrerenderManager::SetInstantMode(PrerenderManager::PRERENDER_MODE_ENABLED);
+
     InstantUnitTestBase::SetUp();
   }
 
+  void TearDown() override {
+    InstantUnitTestBase::TearDown();
+    restore_prerender_mode_.reset();
+  }
+
   void Init(bool prerender_search_results_base_page,
             bool call_did_finish_load) {
     AddTab(browser(), GURL(url::kAboutBlankURL));
@@ -250,6 +264,7 @@
 
  private:
   MockEmbeddedSearchClient mock_embedded_search_client_;
+  std::unique_ptr<RestorePrerenderMode> restore_prerender_mode_;
 };
 
 TEST_F(InstantSearchPrerendererTest, GetSearchTermsFromPrerenderedPage) {
diff --git a/chrome/common/extensions/api/_manifest_features.json b/chrome/common/extensions/api/_manifest_features.json
index dbb6b7d..de4250f 100644
--- a/chrome/common/extensions/api/_manifest_features.json
+++ b/chrome/common/extensions/api/_manifest_features.json
@@ -259,6 +259,6 @@
   },
   "url_handlers": {
     "channel": "stable",
-    "extension_types": ["platform_app"]
+    "extension_types": ["hosted_app", "platform_app"]
   }
 }
diff --git a/chrome/common/extensions/api/url_handlers/url_handlers_parser.cc b/chrome/common/extensions/api/url_handlers/url_handlers_parser.cc
index 4f971b9..90bf29d6 100644
--- a/chrome/common/extensions/api/url_handlers/url_handlers_parser.cc
+++ b/chrome/common/extensions/api/url_handlers/url_handlers_parser.cc
@@ -134,6 +134,11 @@
 }
 
 bool UrlHandlersParser::Parse(Extension* extension, base::string16* error) {
+  if (extension->GetType() == Manifest::TYPE_HOSTED_APP &&
+      !extension->from_bookmark()) {
+    *error = base::ASCIIToUTF16(merrors::kUrlHandlersInHostedApps);
+    return false;
+  }
   std::unique_ptr<UrlHandlers> info(new UrlHandlers);
   const base::DictionaryValue* all_handlers = NULL;
   if (!extension->manifest()->GetDictionary(
@@ -142,7 +147,7 @@
     return false;
   }
 
-  DCHECK(extension->is_platform_app());
+  DCHECK(extension->is_platform_app() || extension->from_bookmark());
 
   for (base::DictionaryValue::Iterator iter(*all_handlers); !iter.IsAtEnd();
        iter.Advance()) {
diff --git a/chrome/common/web_application_info.h b/chrome/common/web_application_info.h
index 5fa31ef1..4083eb5a 100644
--- a/chrome/common/web_application_info.h
+++ b/chrome/common/web_application_info.h
@@ -45,6 +45,9 @@
   // The launch URL for the app.
   GURL app_url;
 
+  // Scope for the app. Dictates what URLs will be opened in the app.
+  GURL scope;
+
   // Set of available icons.
   std::vector<IconInfo> icons;
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 4853a9b..06d7529 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1905,6 +1905,7 @@
         "../browser/extensions/background_scripts_apitest.cc",
         "../browser/extensions/background_xhr_browsertest.cc",
         "../browser/extensions/bookmark_app_helper_browsertest.cc",
+        "../browser/extensions/bookmark_app_url_redirector_browsertest.cc",
         "../browser/extensions/browsertest_util.cc",
         "../browser/extensions/browsertest_util.h",
         "../browser/extensions/browsertest_util_browsertest.cc",
diff --git a/chrome/test/data/extensions/bookmark_apps/url_handlers/in_scope/index.html b/chrome/test/data/extensions/bookmark_apps/url_handlers/in_scope/index.html
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/chrome/test/data/extensions/bookmark_apps/url_handlers/in_scope/index.html
diff --git a/chrome/test/data/extensions/bookmark_apps/url_handlers/in_scope/other.html b/chrome/test/data/extensions/bookmark_apps/url_handlers/in_scope/other.html
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/chrome/test/data/extensions/bookmark_apps/url_handlers/in_scope/other.html
diff --git a/chrome/test/data/extensions/bookmark_apps/url_handlers/out_of_scope/other.html b/chrome/test/data/extensions/bookmark_apps/url_handlers/out_of_scope/other.html
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/chrome/test/data/extensions/bookmark_apps/url_handlers/out_of_scope/other.html
diff --git a/components/sync/protocol/app_specifics.proto b/components/sync/protocol/app_specifics.proto
index a7234e6..52440cf 100644
--- a/components/sync/protocol/app_specifics.proto
+++ b/components/sync/protocol/app_specifics.proto
@@ -91,4 +91,7 @@
   // This is information about linked icons (that is, icons that are downloaded
   // from outside the app's bundle of files.
   repeated LinkedAppIconInfo linked_app_icons = 9;
+
+  // This is the scope of the bookmark app.
+  optional string bookmark_app_scope = 10;
 }
diff --git a/components/sync/protocol/proto_visitors.h b/components/sync/protocol/proto_visitors.h
index 3cd5a423..8ffaca5 100644
--- a/components/sync/protocol/proto_visitors.h
+++ b/components/sync/protocol/proto_visitors.h
@@ -124,6 +124,7 @@
   VISIT(bookmark_app_description);
   VISIT(bookmark_app_icon_color);
   VISIT_REP(linked_app_icons);
+  VISIT(bookmark_app_scope);
 }
 
 VISIT_PROTO_FIELDS(const sync_pb::ArcPackageSpecifics& proto) {
diff --git a/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc b/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc
index f7fb38a..e858ea85 100644
--- a/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc
+++ b/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc
@@ -80,62 +80,62 @@
 
 void RecordEQTAccuracy(base::TimeDelta queueing_time,
                        base::TimeDelta expected_queueing_time) {
-  float expected_queueing_time_ms = expected_queueing_time.InMillisecondsF();
+  float queueing_time_ms = queueing_time.InMillisecondsF();
 
-  if (expected_queueing_time_ms < 10) {
+  if (queueing_time_ms < 10) {
     UMA_HISTOGRAM_TIMES(
         "RendererScheduler."
-        "QueueingDurationWhenExpectedQueueingTime_LessThan.10ms",
-        queueing_time);
+        "ExpectedQueueingTimeWhenQueueingTime_LessThan.10ms",
+        expected_queueing_time);
   }
 
-  if (expected_queueing_time_ms < 150) {
+  if (queueing_time_ms < 150) {
     UMA_HISTOGRAM_TIMES(
         "RendererScheduler."
-        "QueueingDurationWhenExpectedQueueingTime_LessThan.150ms",
-        queueing_time);
+        "ExpectedQueueingTimeWhenQueueingTime_LessThan.150ms",
+        expected_queueing_time);
   }
 
-  if (expected_queueing_time_ms < 300) {
+  if (queueing_time_ms < 300) {
     UMA_HISTOGRAM_TIMES(
         "RendererScheduler."
-        "QueueingDurationWhenExpectedQueueingTime_LessThan.300ms",
-        queueing_time);
+        "ExpectedQueueingTimeWhenQueueingTime_LessThan.300ms",
+        expected_queueing_time);
   }
 
-  if (expected_queueing_time_ms < 450) {
+  if (queueing_time_ms < 450) {
     UMA_HISTOGRAM_TIMES(
         "RendererScheduler."
-        "QueueingDurationWhenExpectedQueueingTime_LessThan.450ms",
-        queueing_time);
+        "ExpectedQueueingTimeWhenQueueingTime_LessThan.450ms",
+        expected_queueing_time);
   }
 
-  if (expected_queueing_time_ms > 10) {
+  if (queueing_time_ms > 10) {
     UMA_HISTOGRAM_TIMES(
         "RendererScheduler."
-        "QueueingDurationWhenExpectedQueueingTime_GreaterThan.10ms",
-        queueing_time);
+        "ExpectedQueueingTimeWhenQueueingTime_GreaterThan.10ms",
+        expected_queueing_time);
   }
 
-  if (expected_queueing_time_ms > 150) {
+  if (queueing_time_ms > 150) {
     UMA_HISTOGRAM_TIMES(
         "RendererScheduler."
-        "QueueingDurationWhenExpectedQueueingTime_GreaterThan.150ms",
-        queueing_time);
+        "ExpectedQueueingTimeWhenQueueingTime_GreaterThan.150ms",
+        expected_queueing_time);
   }
 
-  if (expected_queueing_time_ms > 300) {
+  if (queueing_time_ms > 300) {
     UMA_HISTOGRAM_TIMES(
         "RendererScheduler."
-        "QueueingDurationWhenExpectedQueueingTime_GreaterThan.300ms",
-        queueing_time);
+        "ExpectedQueueingTimeWhenQueueingTime_GreaterThan.300ms",
+        expected_queueing_time);
   }
 
-  if (expected_queueing_time_ms > 450) {
+  if (queueing_time_ms > 450) {
     UMA_HISTOGRAM_TIMES(
         "RendererScheduler."
-        "QueueingDurationWhenExpectedQueueingTime_GreaterThan.450ms",
-        queueing_time);
+        "ExpectedQueueingTimeWhenQueueingTime_GreaterThan.450ms",
+        expected_queueing_time);
   }
 }
 
diff --git a/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc b/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc
index ca8efa8a..459fac3 100644
--- a/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc
+++ b/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc
@@ -1300,11 +1300,15 @@
 
 TEST_F(RenderWidgetHostLatencyTrackerTest, ExpectedQueueingTimeAccuracy) {
   // These numbers are sensitive to where the histogram buckets are.
-  int event_timestamps_ms[] = {11, 25, 35};
+  base::TimeTicks event_timestamp =
+      base::TimeTicks() + base::TimeDelta::FromMilliseconds(11);
+  int expected_queueing_time_ms = 1;
+  base::TimeDelta expected_queueing_time =
+      base::TimeDelta::FromMilliseconds(expected_queueing_time_ms);
 
-  for (float expected_queueing_time_ms : {2, 15, 200, 400}) {
-    base::TimeDelta expected_queueing_time =
-        base::TimeDelta::FromMilliseconds(expected_queueing_time_ms);
+  for (float queueing_time_ms : {2, 15, 200, 400}) {
+    base::TimeDelta queueing_time =
+        base::TimeDelta::FromMilliseconds(queueing_time_ms);
     SyntheticWebTouchEvent event;
     // Touch start.
     event.PressPoint(1, 1);
@@ -1319,22 +1323,15 @@
     fake_latency.set_source_event_type(ui::SourceEventType::TOUCH);
     fake_latency.AddLatencyNumberWithTimestamp(
         ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT,
-        tracker()->latency_component_id(), 0,
-        base::TimeTicks() +
-            base::TimeDelta::FromMilliseconds(event_timestamps_ms[0]),
-        1);
+        tracker()->latency_component_id(), 0, event_timestamp, 1);
 
     fake_latency.AddLatencyNumberWithTimestamp(
         ui::INPUT_EVENT_LATENCY_RENDERER_MAIN_COMPONENT, 0, 0,
-        base::TimeTicks() +
-            base::TimeDelta::FromMilliseconds(event_timestamps_ms[1]),
-        1);
+        event_timestamp + queueing_time, 1);
 
     fake_latency.AddLatencyNumberWithTimestamp(
         ui::INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT, 0, 0,
-        base::TimeTicks() +
-            base::TimeDelta::FromMilliseconds(event_timestamps_ms[2]),
-        1);
+        event_timestamp + queueing_time, 1);
 
     // Call ComputeInputLatencyHistograms directly to avoid OnInputEventAck
     // overwriting components.
@@ -1346,51 +1343,44 @@
                                INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
   }
 
-  EXPECT_THAT(
-      histogram_tester().GetAllSamples(
-          "RendererScheduler."
-          "QueueingDurationWhenExpectedQueueingTime_LessThan.10ms"),
-      ElementsAre(Bucket(event_timestamps_ms[1] - event_timestamps_ms[0], 1)));
-
-  EXPECT_THAT(
-      histogram_tester().GetAllSamples(
-          "RendererScheduler."
-          "QueueingDurationWhenExpectedQueueingTime_LessThan.150ms"),
-      ElementsAre(Bucket(event_timestamps_ms[1] - event_timestamps_ms[0], 2)));
-
-  EXPECT_THAT(
-      histogram_tester().GetAllSamples(
-          "RendererScheduler."
-          "QueueingDurationWhenExpectedQueueingTime_LessThan.300ms"),
-      ElementsAre(Bucket(event_timestamps_ms[1] - event_timestamps_ms[0], 3)));
-
-  EXPECT_THAT(
-      histogram_tester().GetAllSamples(
-          "RendererScheduler."
-          "QueueingDurationWhenExpectedQueueingTime_LessThan.450ms"),
-      ElementsAre(Bucket(event_timestamps_ms[1] - event_timestamps_ms[0], 4)));
-
-  EXPECT_THAT(
-      histogram_tester().GetAllSamples(
-          "RendererScheduler."
-          "QueueingDurationWhenExpectedQueueingTime_GreaterThan.10ms"),
-      ElementsAre(Bucket(event_timestamps_ms[1] - event_timestamps_ms[0], 3)));
-
-  EXPECT_THAT(
-      histogram_tester().GetAllSamples(
-          "RendererScheduler."
-          "QueueingDurationWhenExpectedQueueingTime_GreaterThan.150ms"),
-      ElementsAre(Bucket(event_timestamps_ms[1] - event_timestamps_ms[0], 2)));
-
-  EXPECT_THAT(
-      histogram_tester().GetAllSamples(
-          "RendererScheduler."
-          "QueueingDurationWhenExpectedQueueingTime_GreaterThan.300ms"),
-      ElementsAre(Bucket(event_timestamps_ms[1] - event_timestamps_ms[0], 1)));
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  "RendererScheduler."
+                  "ExpectedQueueingTimeWhenQueueingTime_LessThan.10ms"),
+              ElementsAre(Bucket(expected_queueing_time_ms, 1)));
 
   EXPECT_THAT(histogram_tester().GetAllSamples(
                   "RendererScheduler."
-                  "QueueingDurationWhenExpectedQueueingTime_GreaterThan.450ms"),
+                  "ExpectedQueueingTimeWhenQueueingTime_LessThan.150ms"),
+              ElementsAre(Bucket(expected_queueing_time_ms, 2)));
+
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  "RendererScheduler."
+                  "ExpectedQueueingTimeWhenQueueingTime_LessThan.300ms"),
+              ElementsAre(Bucket(expected_queueing_time_ms, 3)));
+
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  "RendererScheduler."
+                  "ExpectedQueueingTimeWhenQueueingTime_LessThan.450ms"),
+              ElementsAre(Bucket(expected_queueing_time_ms, 4)));
+
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  "RendererScheduler."
+                  "ExpectedQueueingTimeWhenQueueingTime_GreaterThan.10ms"),
+              ElementsAre(Bucket(expected_queueing_time_ms, 3)));
+
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  "RendererScheduler."
+                  "ExpectedQueueingTimeWhenQueueingTime_GreaterThan.150ms"),
+              ElementsAre(Bucket(expected_queueing_time_ms, 2)));
+
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  "RendererScheduler."
+                  "ExpectedQueueingTimeWhenQueueingTime_GreaterThan.300ms"),
+              ElementsAre(Bucket(expected_queueing_time_ms, 1)));
+
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  "RendererScheduler."
+                  "ExpectedQueueingTimeWhenQueueingTime_GreaterThan.450ms"),
               ElementsAre());
 }
 
diff --git a/extensions/common/manifest_constants.cc b/extensions/common/manifest_constants.cc
index f03ad79..3bd1ec7 100644
--- a/extensions/common/manifest_constants.cc
+++ b/extensions/common/manifest_constants.cc
@@ -745,6 +745,8 @@
 const char kUnrecognizedManifestKey[] = "Unrecognized manifest key '*'.";
 const char kUnrecognizedManifestProperty[] =
     "Unrecognized property '*' of manifest key '*'.";
+const char kUrlHandlersInHostedApps[] =
+    "'url_handlers' cannot be used in Hosted Apps.";
 const char kWebRequestConflictsWithLazyBackground[] =
     "The 'webRequest' API cannot be used with event pages.";
 #if defined(OS_CHROMEOS)
diff --git a/extensions/common/manifest_constants.h b/extensions/common/manifest_constants.h
index 03d20a6..7ec835b 100644
--- a/extensions/common/manifest_constants.h
+++ b/extensions/common/manifest_constants.h
@@ -496,6 +496,7 @@
 extern const char kRulesFileIsInvalid[];
 extern const char kUnrecognizedManifestKey[];
 extern const char kUnrecognizedManifestProperty[];
+extern const char kUrlHandlersInHostedApps[];
 extern const char kWebRequestConflictsWithLazyBackground[];
 #if defined(OS_CHROMEOS)
 extern const char kDuplicateActionHandlerFound[];
diff --git a/headless/app/headless_shell.cc b/headless/app/headless_shell.cc
index 1ecf4cc..cc8ae9b 100644
--- a/headless/app/headless_shell.cc
+++ b/headless/app/headless_shell.cc
@@ -21,6 +21,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/task_scheduler/post_task.h"
 #include "content/public/app/content_main.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/common/content_switches.h"
 #include "headless/app/headless_shell.h"
 #include "headless/app/headless_shell_switches.h"
@@ -31,6 +32,7 @@
 #include "net/base/ip_address.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_util.h"
+#include "ui/base/ui_base_switches.h"
 #include "ui/gfx/geometry/size.h"
 
 #if defined(OS_WIN)
@@ -83,6 +85,11 @@
       browser_->CreateBrowserContextBuilder();
   // TODO(eseckler): These switches should also affect BrowserContexts that
   // are created via DevTools later.
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(::switches::kLang)) {
+    context_builder.SetAcceptLanguage(
+        base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+            ::switches::kLang));
+  }
   DeterministicHttpProtocolHandler* http_handler = nullptr;
   DeterministicHttpProtocolHandler* https_handler = nullptr;
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
@@ -153,6 +160,10 @@
       web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get());
     }
   }
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDeterministicFetch)) {
+    devtools_client_->GetNetwork()->GetExperimental()->RemoveObserver(this);
+  }
   web_contents_->RemoveObserver(this);
   web_contents_ = nullptr;
   browser_context_->Close();
@@ -160,6 +171,7 @@
 }
 
 void HeadlessShell::DevToolsTargetReady() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get());
   devtools_client_->GetInspector()->GetExperimental()->AddObserver(this);
   devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
@@ -169,6 +181,7 @@
 
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kDeterministicFetch)) {
+    devtools_client_->GetNetwork()->GetExperimental()->AddObserver(this);
     devtools_client_->GetNetwork()
         ->GetExperimental()
         ->SetRequestInterceptionEnabled(
@@ -250,6 +263,7 @@
 }
 
 void HeadlessShell::PollReadyState() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   // We need to check the current location in addition to the ready state to
   // be sure the expected page is ready.
   devtools_client_->GetRuntime()->Evaluate(
@@ -293,10 +307,11 @@
 // network::Observer implementation:
 void HeadlessShell::OnRequestIntercepted(
     const headless::network::RequestInterceptedParams& params) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (params.GetIsNavigationRequest()) {
     deterministic_dispatcher_->NavigationRequested(
         base::MakeUnique<ShellNavigationRequest>(weak_factory_.GetWeakPtr(),
-                                                 params));
+                                                 params.GetInterceptionId()));
     return;
   }
   devtools_client_->GetNetwork()->GetExperimental()->ContinueInterceptedRequest(
@@ -329,6 +344,7 @@
 }
 
 void HeadlessShell::FetchDom() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   devtools_client_->GetRuntime()->Evaluate(
       "document.body.outerHTML",
       base::Bind(&HeadlessShell::OnDomFetched, weak_factory_.GetWeakPtr()));
@@ -349,6 +365,7 @@
 }
 
 void HeadlessShell::InputExpression() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   // Note that a real system should read user input asynchronously, because
   // otherwise all other browser activity is suspended (e.g., page loading).
   printf(">>> ");
@@ -379,6 +396,7 @@
 }
 
 void HeadlessShell::CaptureScreenshot() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   devtools_client_->GetPage()->GetExperimental()->CaptureScreenshot(
       page::CaptureScreenshotParams::Builder().Build(),
       base::Bind(&HeadlessShell::OnScreenshotCaptured,
@@ -397,6 +415,7 @@
 }
 
 void HeadlessShell::PrintToPDF() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   devtools_client_->GetPage()->GetExperimental()->PrintToPDF(
       page::PrintToPDFParams::Builder()
           .SetDisplayHeaderFooter(true)
@@ -418,6 +437,7 @@
 void HeadlessShell::WriteFile(const std::string& file_path_switch,
                               const std::string& default_file_name,
                               const std::string& base64_data) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   base::FilePath file_name =
       base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
           file_path_switch);
@@ -437,6 +457,7 @@
 void HeadlessShell::OnFileOpened(const std::string& base64_data,
                                  const base::FilePath file_name,
                                  base::File::Error error_code) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (!file_proxy_->IsValid()) {
     LOG(ERROR) << "Writing to file " << file_name.value()
                << " was unsuccessful, could not open file: "
@@ -469,6 +490,7 @@
                                   const size_t length,
                                   base::File::Error error_code,
                                   int write_result) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (write_result < static_cast<int>(length)) {
     // TODO(eseckler): Support recovering from partial writes.
     LOG(ERROR) << "Writing to file " << file_name.value()
diff --git a/headless/app/shell_navigation_request.cc b/headless/app/shell_navigation_request.cc
index b63ba4e..64d5840 100644
--- a/headless/app/shell_navigation_request.cc
+++ b/headless/app/shell_navigation_request.cc
@@ -4,38 +4,73 @@
 
 #include "headless/app/shell_navigation_request.h"
 
+#include "content/public/browser/browser_thread.h"
 #include "headless/app/headless_shell.h"
 
 namespace headless {
 
 ShellNavigationRequest::ShellNavigationRequest(
     base::WeakPtr<HeadlessShell> headless_shell,
-    const network::RequestInterceptedParams& params)
-    : headless_shell_(headless_shell),
-      interception_id_(params.GetInterceptionId()) {}
+    const std::string& interception_id)
+    : headless_shell_(
+          base::MakeUnique<base::WeakPtr<HeadlessShell>>(headless_shell)),
+      interception_id_(interception_id) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+}
 
 ShellNavigationRequest::~ShellNavigationRequest() {}
 
 void ShellNavigationRequest::StartProcessing(base::Closure done_callback) {
-  if (!headless_shell_)
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  // The devtools bindings can only be called on the UI thread.
+  content::BrowserThread::PostTask(
+      content::BrowserThread::UI, FROM_HERE,
+      base::Bind(&ShellNavigationRequest::StartProcessingOnUiThread,
+                 base::Passed(std::move(headless_shell_)), interception_id_,
+                 std::move(done_callback)));
+}
+
+// static
+void ShellNavigationRequest::StartProcessingOnUiThread(
+    std::unique_ptr<base::WeakPtr<HeadlessShell>> headless_shell,
+    std::string interception_id,
+    base::Closure done_callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (!headless_shell)
     return;
 
   // Allow the navigation to proceed.
-  headless_shell_->devtools_client()
+  (*headless_shell)
+      ->devtools_client()
       ->GetNetwork()
       ->GetExperimental()
       ->ContinueInterceptedRequest(
           headless::network::ContinueInterceptedRequestParams::Builder()
-              .SetInterceptionId(interception_id_)
+              .SetInterceptionId(interception_id)
               .Build(),
           base::Bind(&ShellNavigationRequest::ContinueInterceptedRequestResult,
-                     done_callback));
+                     std::move(done_callback)));
 }
 
 // static
 void ShellNavigationRequest::ContinueInterceptedRequestResult(
     base::Closure done_callback,
     std::unique_ptr<network::ContinueInterceptedRequestResult>) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  // The |done_callback| must be fired on the IO thread.
+  content::BrowserThread::PostTask(
+      content::BrowserThread::IO, FROM_HERE,
+      base::Bind(
+          &ShellNavigationRequest::ContinueInterceptedRequestResultOnIoThread,
+          std::move(done_callback)));
+}
+
+// static
+void ShellNavigationRequest::ContinueInterceptedRequestResultOnIoThread(
+    base::Closure done_callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   done_callback.Run();
 }
 
diff --git a/headless/app/shell_navigation_request.h b/headless/app/shell_navigation_request.h
index 1199f28..1abad6e 100644
--- a/headless/app/shell_navigation_request.h
+++ b/headless/app/shell_navigation_request.h
@@ -19,20 +19,31 @@
 class ShellNavigationRequest : public NavigationRequest {
  public:
   ShellNavigationRequest(base::WeakPtr<HeadlessShell> headless_shell,
-                         const network::RequestInterceptedParams& params);
+                         const std::string& interception_id);
 
   ~ShellNavigationRequest() override;
 
   void StartProcessing(base::Closure done_callback) override;
 
  private:
+  static void StartProcessingOnUiThread(
+      std::unique_ptr<base::WeakPtr<HeadlessShell>> headless_shell,
+      std::string interception_id,
+      base::Closure done_callback);
+
   // Note the navigation likely isn't done when this is called, however we
   // expect it will have been committed and the initial resource load requested.
   static void ContinueInterceptedRequestResult(
       base::Closure done_callback,
       std::unique_ptr<network::ContinueInterceptedRequestResult>);
 
-  base::WeakPtr<HeadlessShell> headless_shell_;
+  static void ContinueInterceptedRequestResultOnIoThread(
+      base::Closure done_callback);
+
+  // Yuck we need to post a weak pointer from the IO -> UI threads but WeakPtr
+  // is super finicky about which threads it's touched on. By boxing this up in
+  // a unique_ptr we can pass it about and only touch it on the UI thread.
+  std::unique_ptr<base::WeakPtr<HeadlessShell>> headless_shell_;
   std::string interception_id_;
 };
 
diff --git a/headless/lib/browser/headless_browser_context_impl.cc b/headless/lib/browser/headless_browser_context_impl.cc
index 9debdd07..bdcaad176 100644
--- a/headless/lib/browser/headless_browser_context_impl.cc
+++ b/headless/lib/browser/headless_browser_context_impl.cc
@@ -92,6 +92,13 @@
 HeadlessBrowserContextImpl::~HeadlessBrowserContextImpl() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
+  // Inform observers that we're going away.
+  {
+    base::AutoLock lock(observers_lock_);
+    for (auto& observer : observers_)
+      observer.OnHeadlessBrowserContextDestruct();
+  }
+
   // Destroy all web contents before shutting down storage partitions.
   web_contents_map_.clear();
 
diff --git a/headless/lib/browser/headless_content_browser_client.cc b/headless/lib/browser/headless_content_browser_client.cc
index 0b24e77..f528710 100644
--- a/headless/lib/browser/headless_content_browser_client.cc
+++ b/headless/lib/browser/headless_content_browser_client.cc
@@ -28,6 +28,7 @@
 #include "headless/lib/browser/headless_devtools_manager_delegate.h"
 #include "headless/lib/browser/headless_quota_permission_context.h"
 #include "headless/lib/headless_macros.h"
+#include "net/base/url_util.h"
 #include "storage/browser/quota/quota_settings.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/ui_base_switches.h"
@@ -254,8 +255,18 @@
     bool expired_previous_decision,
     const base::Callback<void(content::CertificateRequestResultType)>&
         callback) {
-  if (!callback.is_null())
+  if (!callback.is_null()) {
+    // If --allow-insecure-localhost is specified, and the request
+    // was for localhost, then the error was not fatal.
+    bool allow_localhost = base::CommandLine::ForCurrentProcess()->HasSwitch(
+        ::switches::kAllowInsecureLocalhost);
+    if (allow_localhost && net::IsLocalhost(request_url.host())) {
+      callback.Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE);
+      return;
+    }
+
     callback.Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY);
+  }
 }
 
 void HeadlessContentBrowserClient::ResourceDispatcherHostCreated() {
diff --git a/headless/lib/browser/headless_network_delegate.cc b/headless/lib/browser/headless_network_delegate.cc
index d6722a6..c99c015 100644
--- a/headless/lib/browser/headless_network_delegate.cc
+++ b/headless/lib/browser/headless_network_delegate.cc
@@ -21,9 +21,17 @@
 
 HeadlessNetworkDelegate::HeadlessNetworkDelegate(
     HeadlessBrowserContextImpl* headless_browser_context)
-    : headless_browser_context_(headless_browser_context) {}
+    : headless_browser_context_(headless_browser_context) {
+  base::AutoLock lock(lock_);
+  if (headless_browser_context_)
+    headless_browser_context_->AddObserver(this);
+}
 
-HeadlessNetworkDelegate::~HeadlessNetworkDelegate() {}
+HeadlessNetworkDelegate::~HeadlessNetworkDelegate() {
+  base::AutoLock lock(lock_);
+  if (headless_browser_context_)
+    headless_browser_context_->RemoveObserver(this);
+}
 
 int HeadlessNetworkDelegate::OnBeforeURLRequest(
     net::URLRequest* request,
@@ -62,7 +70,8 @@
 void HeadlessNetworkDelegate::OnCompleted(net::URLRequest* request,
                                           bool started,
                                           int net_error) {
-  if (net_error != net::OK)
+  base::AutoLock lock(lock_);
+  if (headless_browser_context_ && net_error != net::OK)
     headless_browser_context_->NotifyUrlRequestFailed(request, net_error);
 }
 
@@ -98,4 +107,9 @@
   return true;
 }
 
+void HeadlessNetworkDelegate::OnHeadlessBrowserContextDestruct() {
+  base::AutoLock lock(lock_);
+  headless_browser_context_ = nullptr;
+}
+
 }  // namespace headless
diff --git a/headless/lib/browser/headless_network_delegate.h b/headless/lib/browser/headless_network_delegate.h
index fc11b76..ccfb5f6 100644
--- a/headless/lib/browser/headless_network_delegate.h
+++ b/headless/lib/browser/headless_network_delegate.h
@@ -7,6 +7,8 @@
 
 #include "base/macros.h"
 
+#include "base/synchronization/lock.h"
+#include "headless/public/headless_browser_context.h"
 #include "net/base/network_delegate_impl.h"
 
 namespace headless {
@@ -14,13 +16,15 @@
 
 // We use the HeadlessNetworkDelegate to remove DevTools request headers before
 // requests are actually fetched and for reporting failed network requests.
-class HeadlessNetworkDelegate : public net::NetworkDelegateImpl {
+class HeadlessNetworkDelegate : public net::NetworkDelegateImpl,
+                                public HeadlessBrowserContext::Observer {
  public:
   explicit HeadlessNetworkDelegate(
       HeadlessBrowserContextImpl* headless_browser_context);
   ~HeadlessNetworkDelegate() override;
 
  private:
+  // net::NetworkDelegateImpl implementation:
   int OnBeforeURLRequest(net::URLRequest* request,
                          const net::CompletionCallback& callback,
                          GURL* new_url) override;
@@ -69,6 +73,10 @@
                        const base::FilePath& original_path,
                        const base::FilePath& absolute_path) const override;
 
+  // HeadlessBrowserContext::Observer implementation:
+  void OnHeadlessBrowserContextDestruct() override;
+
+  base::Lock lock_;  // Protects |headless_browser_context_|.
   HeadlessBrowserContextImpl* headless_browser_context_;  // Not owned.
 
   DISALLOW_COPY_AND_ASSIGN(HeadlessNetworkDelegate);
diff --git a/headless/lib/browser/headless_url_request_context_getter.cc b/headless/lib/browser/headless_url_request_context_getter.cc
index 49cb38e..a2dbb0d 100644
--- a/headless/lib/browser/headless_url_request_context_getter.cc
+++ b/headless/lib/browser/headless_url_request_context_getter.cc
@@ -11,6 +11,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/task_scheduler/post_task.h"
 #include "content/public/browser/browser_thread.h"
+#include "headless/lib/browser/headless_browser_context_impl.h"
 #include "headless/lib/browser/headless_browser_context_options.h"
 #include "headless/lib/browser/headless_network_delegate.h"
 #include "net/dns/mapped_host_resolver.h"
@@ -56,9 +57,15 @@
     proxy_config_service_ =
         net::ProxyService::CreateSystemProxyConfigService(io_task_runner_);
   }
+  base::AutoLock lock(lock_);
+  headless_browser_context_->AddObserver(this);
 }
 
-HeadlessURLRequestContextGetter::~HeadlessURLRequestContextGetter() {}
+HeadlessURLRequestContextGetter::~HeadlessURLRequestContextGetter() {
+  base::AutoLock lock(lock_);
+  if (headless_browser_context_)
+    headless_browser_context_->RemoveObserver(this);
+}
 
 net::URLRequestContext*
 HeadlessURLRequestContextGetter::GetURLRequestContext() {
@@ -76,8 +83,12 @@
     } else {
       builder.set_proxy_config_service(std::move(proxy_config_service_));
     }
-    builder.set_network_delegate(
-        base::MakeUnique<HeadlessNetworkDelegate>(headless_browser_context_));
+
+    {
+      base::AutoLock lock(lock_);
+      builder.set_network_delegate(
+          base::MakeUnique<HeadlessNetworkDelegate>(headless_browser_context_));
+    }
 
     if (!host_resolver_rules_.empty()) {
       std::unique_ptr<net::HostResolver> host_resolver(
@@ -111,4 +122,9 @@
   return url_request_context_->host_resolver();
 }
 
+void HeadlessURLRequestContextGetter::OnHeadlessBrowserContextDestruct() {
+  base::AutoLock lock(lock_);
+  headless_browser_context_ = nullptr;
+}
+
 }  // namespace headless
diff --git a/headless/lib/browser/headless_url_request_context_getter.h b/headless/lib/browser/headless_url_request_context_getter.h
index 5afab88..92341b3 100644
--- a/headless/lib/browser/headless_url_request_context_getter.h
+++ b/headless/lib/browser/headless_url_request_context_getter.h
@@ -30,7 +30,9 @@
 class HeadlessBrowserContextOptions;
 class HeadlessBrowserContextImpl;
 
-class HeadlessURLRequestContextGetter : public net::URLRequestContextGetter {
+class HeadlessURLRequestContextGetter
+    : public net::URLRequestContextGetter,
+      public HeadlessBrowserContext::Observer {
  public:
   HeadlessURLRequestContextGetter(
       scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
@@ -48,6 +50,9 @@
 
   net::HostResolver* host_resolver() const;
 
+  // HeadlessBrowserContext::Observer implementation:
+  void OnHeadlessBrowserContextDestruct() override;
+
  protected:
   ~HeadlessURLRequestContextGetter() override;
 
@@ -66,7 +71,9 @@
   std::unique_ptr<net::URLRequestContext> url_request_context_;
   content::ProtocolHandlerMap protocol_handlers_;
   content::URLRequestInterceptorScopedVector request_interceptors_;
-  net::NetLog* net_log_;                                  // Not owned.
+  net::NetLog* net_log_;  // Not owned
+
+  base::Lock lock_;  // Protects |headless_browser_context_|.
   HeadlessBrowserContextImpl* headless_browser_context_;  // Not owned.
 
   DISALLOW_COPY_AND_ASSIGN(HeadlessURLRequestContextGetter);
diff --git a/headless/lib/headless_browser_browsertest.cc b/headless/lib/headless_browser_browsertest.cc
index 1206725..7bb186f 100644
--- a/headless/lib/headless_browser_browsertest.cc
+++ b/headless/lib/headless_browser_browsertest.cc
@@ -13,6 +13,8 @@
 #include "base/threading/thread_restrictions.h"
 #include "content/public/browser/permission_manager.h"
 #include "content/public/browser/permission_type.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test.h"
 #include "headless/lib/browser/headless_browser_context_impl.h"
@@ -913,4 +915,26 @@
       EvaluateScript(web_contents, "window.print()")->HasExceptionDetails());
 }
 
+IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, AllowInsecureLocalhostFlag) {
+  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED);
+  https_server.ServeFilesFromSourceDirectory("headless/test/data");
+  ASSERT_TRUE(https_server.Start());
+  GURL test_url = https_server.GetURL("/hello.html");
+
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kAllowInsecureLocalhost);
+
+  HeadlessBrowserContext* browser_context =
+      browser()->CreateBrowserContextBuilder().Build();
+
+  HeadlessWebContentsImpl* web_contents =
+      HeadlessWebContentsImpl::From(browser_context->CreateWebContentsBuilder()
+                                        .SetInitialURL(test_url)
+                                        .Build());
+
+  // If the certificate fails to validate, this should fail.
+  EXPECT_TRUE(WaitForLoad(web_contents));
+}
+
 }  // namespace headless
diff --git a/headless/lib/headless_devtools_client_browsertest.cc b/headless/lib/headless_devtools_client_browsertest.cc
index b6291622..5557274 100644
--- a/headless/lib/headless_devtools_client_browsertest.cc
+++ b/headless/lib/headless_devtools_client_browsertest.cc
@@ -459,11 +459,7 @@
   }
 };
 
-#if defined(OS_WIN)
-DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(TargetDomainCreateAndDeletePageTest);
-#else
 HEADLESS_ASYNC_DEVTOOLED_TEST_F(TargetDomainCreateAndDeletePageTest);
-#endif
 
 class TargetDomainCreateAndDeleteBrowserContextTest
     : public HeadlessAsyncDevTooledBrowserTest {
@@ -531,12 +527,7 @@
   std::string browser_context_id_;
 };
 
-#if defined(OS_WIN)
-DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(
-    TargetDomainCreateAndDeleteBrowserContextTest);
-#else
 HEADLESS_ASYNC_DEVTOOLED_TEST_F(TargetDomainCreateAndDeleteBrowserContextTest);
-#endif
 
 class TargetDomainDisposeContextFailsIfInUse
     : public HeadlessAsyncDevTooledBrowserTest {
@@ -612,13 +603,7 @@
   std::string page_id_;
 };
 
-// Test is flaky on Linux debug (https://crbug.com/751180)
-#if defined(OS_WIN) || defined(OS_LINUX)
-DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(
-    TargetDomainDisposeContextFailsIfInUse);
-#else
 HEADLESS_ASYNC_DEVTOOLED_TEST_F(TargetDomainDisposeContextFailsIfInUse);
-#endif
 
 class TargetDomainCreateTwoContexts : public HeadlessAsyncDevTooledBrowserTest,
                                       public target::ExperimentalObserver,
@@ -890,11 +875,7 @@
   int context_closed_count_ = 0;
 };
 
-#if defined(OS_WIN)
-DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(TargetDomainCreateTwoContexts);
-#else
 HEADLESS_ASYNC_DEVTOOLED_TEST_F(TargetDomainCreateTwoContexts);
-#endif
 
 class HeadlessDevToolsNavigationControlTest
     : public HeadlessAsyncDevTooledBrowserTest,
@@ -1448,6 +1429,8 @@
   }
 
   void OnLoadEventFired(const page::LoadEventFiredParams&) override {
+    browser_context_->RemoveObserver(this);
+
     base::AutoLock lock(lock_);
     EXPECT_EQ("iframe.html", url_that_failed_to_load_);
     FinishAsynchronousTest();
diff --git a/headless/lib/headless_web_contents_browsertest.cc b/headless/lib/headless_web_contents_browsertest.cc
index 0f2ed053..8403fce 100644
--- a/headless/lib/headless_web_contents_browsertest.cc
+++ b/headless/lib/headless_web_contents_browsertest.cc
@@ -117,6 +117,8 @@
   EXPECT_EQ(expected_bounds.size(),
             child->web_contents()->GetContainerBounds().size());
 #endif  // !defined(OS_MACOSX)
+
+  browser_context->RemoveObserver(&observer);
 }
 
 class HeadlessWindowOpenTabSocketTest : public HeadlessBrowserTest,
@@ -239,6 +241,8 @@
 
   RunAsynchronousTest();
   EXPECT_EQ("Embedder sent us: One", message_);
+
+  browser_context->RemoveObserver(this);
 }
 
 class HeadlessNoDevToolsTabSocketTest : public HeadlessBrowserTest,
diff --git a/headless/public/headless_browser_context.h b/headless/public/headless_browser_context.h
index a2cc3b1a..6cb289f2 100644
--- a/headless/public/headless_browser_context.h
+++ b/headless/public/headless_browser_context.h
@@ -88,6 +88,9 @@
   // thread.
   virtual void UrlRequestFailed(net::URLRequest* request, int net_error) {}
 
+  // Indicates the HeadlessBrowserContext is about to be deleted.
+  virtual void OnHeadlessBrowserContextDestruct() {}
+
  protected:
   virtual ~Observer() {}
 };
diff --git a/ios/chrome/browser/content_suggestions/BUILD.gn b/ios/chrome/browser/content_suggestions/BUILD.gn
index c0fe0e0..e4c37000 100644
--- a/ios/chrome/browser/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/content_suggestions/BUILD.gn
@@ -40,6 +40,7 @@
     "//ios/chrome/browser/ntp_snippets",
     "//ios/chrome/browser/ntp_tiles",
     "//ios/chrome/browser/reading_list",
+    "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/alert_coordinator",
     "//ios/chrome/browser/ui/collection_view/cells",
diff --git a/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.mm b/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.mm
index 70069b59..4517196 100644
--- a/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.mm
@@ -10,7 +10,6 @@
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/ntp_snippets/content_suggestions_service.h"
-#include "components/ntp_snippets/ntp_snippets_constants.h"
 #include "components/ntp_snippets/remote/remote_suggestions_scheduler.h"
 #include "components/ntp_tiles/metrics.h"
 #include "components/ntp_tiles/most_visited_sites.h"
@@ -28,6 +27,7 @@
 #include "ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h"
 #include "ios/chrome/browser/ntp_tiles/ios_most_visited_sites_factory.h"
 #include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
+#include "ios/chrome/browser/tabs/tab_constants.h"
 #import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h"
 #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
@@ -196,7 +196,7 @@
   // Use a referrer with a specific URL to mark this entry as coming from
   // ContentSuggestions.
   web::Referrer referrer;
-  referrer.url = GURL(ntp_snippets::kContentSuggestionsApiScope);
+  referrer.url = GURL(tab_constants::kDoNotConsiderForMostVisited);
 
   [self.URLLoader loadURL:suggestionItem.URL
                  referrer:referrer
diff --git a/ios/chrome/browser/resources/Settings.bundle/Experimental.plist b/ios/chrome/browser/resources/Settings.bundle/Experimental.plist
index e579a27..608365a 100644
--- a/ios/chrome/browser/resources/Settings.bundle/Experimental.plist
+++ b/ios/chrome/browser/resources/Settings.bundle/Experimental.plist
@@ -78,16 +78,6 @@
 			<key>Type</key>
 			<string>PSToggleSwitchSpecifier</string>
 			<key>Title</key>
-			<string>Fake Cloud Policy Type</string>
-			<key>Key</key>
-			<string>FakeCloudPolicyType</string>
-			<key>DefaultValue</key>
-			<false/>
-		</dict>
-		<dict>
-			<key>Type</key>
-			<string>PSToggleSwitchSpecifier</string>
-			<key>Title</key>
 			<string>Force First Run</string>
 			<key>Key</key>
 			<string>FirstRunForceEnabled</string>
diff --git a/ios/chrome/browser/tabs/BUILD.gn b/ios/chrome/browser/tabs/BUILD.gn
index ad2aa9e..06514de 100644
--- a/ios/chrome/browser/tabs/BUILD.gn
+++ b/ios/chrome/browser/tabs/BUILD.gn
@@ -6,6 +6,7 @@
   sources = [
     "legacy_tab_helper.h",
     "tab.h",
+    "tab_constants.h",
     "tab_delegate.h",
     "tab_dialog_delegate.h",
     "tab_headers_delegate.h",
@@ -39,6 +40,7 @@
     "legacy_tab_helper.mm",
     "tab.h",
     "tab.mm",
+    "tab_constants.cc",
     "tab_helper_util.mm",
     "tab_model.mm",
     "tab_model_closing_web_state_observer.h",
diff --git a/ios/chrome/browser/tabs/tab.mm b/ios/chrome/browser/tabs/tab.mm
index f0bc0c1..774a17e 100644
--- a/ios/chrome/browser/tabs/tab.mm
+++ b/ios/chrome/browser/tabs/tab.mm
@@ -73,6 +73,7 @@
 #import "ios/chrome/browser/snapshots/snapshot_overlay_provider.h"
 #import "ios/chrome/browser/snapshots/web_controller_snapshot_helper.h"
 #import "ios/chrome/browser/tabs/legacy_tab_helper.h"
+#include "ios/chrome/browser/tabs/tab_constants.h"
 #import "ios/chrome/browser/tabs/tab_delegate.h"
 #import "ios/chrome/browser/tabs/tab_dialog_delegate.h"
 #import "ios/chrome/browser/tabs/tab_headers_delegate.h"
@@ -163,10 +164,6 @@
 const char kRendererTerminationStateHistogram[] =
     "Tab.StateAtRendererTermination";
 
-// Referrer used for clicks on article suggestions on the NTP.
-const char kChromeContentSuggestionsReferrer[] =
-    "https://www.googleapis.com/auth/chrome-content-suggestions";
-
 // Enum corresponding to UMA's TabForegroundState, for
 // Tab.StateAtRendererTermination. Must be kept in sync with the UMA enum.
 enum class RendererTerminationTabState {
@@ -882,7 +879,7 @@
   // Clicks on content suggestions on the NTP should not contribute to the
   // Most Visited tiles in the NTP.
   const bool considerForNTPMostVisited =
-      referrer.url != GURL(kChromeContentSuggestionsReferrer);
+      referrer.url != GURL(tab_constants::kDoNotConsiderForMostVisited);
   history::HistoryAddPageArgs args(
       url, item->GetTimestamp(), &_tabHistoryContext, item->GetUniqueID(),
       referrer.url, redirects, item->GetTransitionType(),
diff --git a/ios/chrome/browser/tabs/tab_constants.cc b/ios/chrome/browser/tabs/tab_constants.cc
new file mode 100644
index 0000000..c8b967f
--- /dev/null
+++ b/ios/chrome/browser/tabs/tab_constants.cc
@@ -0,0 +1,10 @@
+// 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 "ios/chrome/browser/tabs/tab_constants.h"
+
+namespace tab_constants {
+const char kDoNotConsiderForMostVisited[] =
+    "chrome://do_not_consider_for_most_visited";
+}
diff --git a/ios/chrome/browser/tabs/tab_constants.h b/ios/chrome/browser/tabs/tab_constants.h
new file mode 100644
index 0000000..a591a0cb
--- /dev/null
+++ b/ios/chrome/browser/tabs/tab_constants.h
@@ -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.
+
+#ifndef IOS_CHROME_BROWSER_TABS_TAB_CONSTANTS_H_
+#define IOS_CHROME_BROWSER_TABS_TAB_CONSTANTS_H_
+
+namespace tab_constants {
+// URL used in referrer to signal that the navigation should not be taken into
+// consideration when computing the Most Visited sites.
+extern const char kDoNotConsiderForMostVisited[];
+}  // namespace tab_constants
+
+#endif  // IOS_CHROME_BROWSER_TABS_TAB_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm
index 18ee041..5edcfca6 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm
@@ -162,7 +162,6 @@
     _promoAdded = NO;
     _dataSource = dataSource;
     _dataSource.dataSink = self;
-    _sectionIdentifiersFromContentSuggestions = [[NSMutableSet alloc] init];
   }
   return self;
 }
@@ -588,6 +587,7 @@
 - (void)resetModels {
   [self.collectionViewController loadModel];
   self.sectionInfoBySectionIdentifier = [[NSMutableDictionary alloc] init];
+  self.sectionIdentifiersFromContentSuggestions = [[NSMutableSet alloc] init];
 }
 
 // Runs the additional action for the section identified by |sectionInfo|.
diff --git a/ios/chrome/browser/ui/reading_list/BUILD.gn b/ios/chrome/browser/ui/reading_list/BUILD.gn
index 7d6b9be..2bcb6572 100644
--- a/ios/chrome/browser/ui/reading_list/BUILD.gn
+++ b/ios/chrome/browser/ui/reading_list/BUILD.gn
@@ -34,6 +34,7 @@
     "//ios/chrome/browser/favicon",
     "//ios/chrome/browser/feature_engagement",
     "//ios/chrome/browser/reading_list",
+    "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/alert_coordinator",
     "//ios/chrome/browser/ui/favicon",
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_coordinator.mm b/ios/chrome/browser/ui/reading_list/reading_list_coordinator.mm
index f97210f..d3d3cffa 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_coordinator.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_coordinator.mm
@@ -18,6 +18,7 @@
 #include "ios/chrome/browser/reading_list/reading_list_download_service.h"
 #include "ios/chrome/browser/reading_list/reading_list_download_service_factory.h"
 #include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
+#include "ios/chrome/browser/tabs/tab_constants.h"
 #import "ios/chrome/browser/ui/alert_coordinator/action_sheet_coordinator.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_collection_view_item.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_mediator.h"
@@ -251,8 +252,13 @@
 
   [readingListCollectionViewController willBeDismissed];
 
+  // Use a referrer with a specific URL to signal that this entry should not be
+  // taken into account for the Most Visited tiles.
+  web::Referrer referrer;
+  referrer.url = GURL(tab_constants::kDoNotConsiderForMostVisited);
+
   [self.URLLoader loadURL:entry->URL()
-                 referrer:web::Referrer()
+                 referrer:referrer
                transition:ui::PAGE_TRANSITION_AUTO_BOOKMARK
         rendererInitiated:NO];
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 8ba7ea4f..6066f78 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1457,6 +1457,30 @@
             ]
         }
     ],
+    "NTPCondensedLayout": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "CondensedLayoutWithSmallLogo",
+                    "params": {
+                        "condensed_layout_logo_height": "50",
+                        "condensed_layout_show_logo": "true",
+                        "condensed_tile_layout_for_small_screens_enabled": "true"
+                    },
+                    "enable_features": [
+                        "NTPCondensedLayout",
+                        "NTPCondensedTileLayout"
+                    ],
+                    "disable_features": [
+                        "NTPShowGoogleGInOmnibox"
+                    ]
+                }
+            ]
+        }
+    ],
     "NTPFaviconsFromNewServer": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 527b1d9..01c8bfd 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -612,11 +612,27 @@
 # ====== DevTools test migration failures from here ======
 # Temporary failures (these tests are moves from html to js)
 # See crbug.com/667560 for details
+crbug.com/667560 http/tests/devtools/console/console-bind-fake.js [ Skip ]
+crbug.com/667560 http/tests/devtools/console/console-call-getter-on-proto.js [ Skip ]
+crbug.com/667560 http/tests/devtools/console/console-command-clear.js [ Skip ]
+crbug.com/667560 http/tests/devtools/console/console-control-characters.js [ Skip ]
+crbug.com/667560 http/tests/devtools/console/console-correct-suggestions.js [ Skip ]
+crbug.com/667560 http/tests/devtools/console/console-css-unterminated-comment.js [ Skip ]
+crbug.com/667560 http/tests/devtools/console/console-dir-deprecated.js [ Skip ]
+crbug.com/667560 http/tests/devtools/console/console-dir-global.js [ Skip ]
+crbug.com/667560 http/tests/devtools/console/console-edit-property-value.js [ Skip ]
 
 ### virtual/mojo-loading/http/tests/devtools
-
-### Manually fix after migration
-crbug.com/667560 virtual/mojo-loading/http/tests/devtools/console/console-dir.js [ NeedsManualRebaseline ]
+crbug.com/667560 virtual/mojo-loading/http/tests/devtools/console/console-bind-fake.js [ Skip ]
+crbug.com/667560 virtual/mojo-loading/http/tests/devtools/console/console-call-getter-on-proto.js [ Skip ]
+crbug.com/667560 virtual/mojo-loading/http/tests/devtools/console/console-command-clear.js [ Skip ]
+crbug.com/667560 virtual/mojo-loading/http/tests/devtools/console/console-control-characters.js [ Skip ]
+crbug.com/667560 virtual/mojo-loading/http/tests/devtools/console/console-correct-suggestions.js [ Skip ]
+crbug.com/667560 virtual/mojo-loading/http/tests/devtools/console/console-css-unterminated-comment.js [ Skip ]
+crbug.com/667560 virtual/mojo-loading/http/tests/devtools/console/console-dir-deprecated.js [ Skip ]
+crbug.com/667560 virtual/mojo-loading/http/tests/devtools/console/console-dir-global.js [ Skip ]
+crbug.com/667560 virtual/mojo-loading/http/tests/devtools/console/console-dir.js [ Skip ]
+crbug.com/667560 virtual/mojo-loading/http/tests/devtools/console/console-edit-property-value.js [ Skip ]
 
 # ====== DevTools test migration failures until here ======
 
@@ -3086,6 +3102,4 @@
 crbug.com/750310 [ Win7 Debug ] virtual/off-main-thread-fetch/http/tests/inspector/network/network-blocked-reason.html [ Timeout ]
 
 # Sheriff failures 2017-08-03
-crbug.com/751906 http/tests/devtools/console/console-correct-suggestions.js [ Timeout Pass ]
-crbug.com/751906 virtual/mojo-loading/http/tests/devtools/console/console-correct-suggestions.js [ Timeout Pass ]
 crbug.com/751955 external/wpt/webusb/idlharness.https.html [ Timeout Pass ]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css-fonts/variations/font-parse-numeric-stretch-style-weight.html b/third_party/WebKit/LayoutTests/external/wpt/css-fonts/variations/font-parse-numeric-stretch-style-weight.html
index e4ca2d39..60f3b6b29 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css-fonts/variations/font-parse-numeric-stretch-style-weight.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css-fonts/variations/font-parse-numeric-stretch-style-weight.html
@@ -25,8 +25,8 @@
 };
 
 var styleInvalidTests = {
-  'weight': ['100 400'],
-  'stretch': ['100% 110%', '0%', '100% 150%']
+  'weight': ['100 400', 'calc(0 - 100)', 'calc(200 + 801)'],
+  'stretch': ['100% 110%', '0%', '100% 150%', 'calc(1 + 10%)']
 };
 
 function testParseStyle() {
@@ -64,6 +64,9 @@
     ['100%', '100%'],
     ['110%', '110%'],
     ['111.5%', '111.5%'],
+    [ "50% 200%", "50% 200%" ],
+    [ "0.1% 1%", "0.1% 1%" ],
+    [ "900% 901%", "900% 901%" ],
     ['ultra-condensed', 'ultra-condensed'],
     ['ultra-expanded', 'ultra-expanded'],
   ],
@@ -82,8 +85,8 @@
     'a b c',
   ],
   'stretch': [
-    '0%', '60% 70% 80%', 'a%', 'a b c', '0.1', '-60% 80%', 'ultra-expannnned',
-    '50% 0'
+    '-0.5%', '-1%', '0%', 'calc(0% - 10%)', '60% 70% 80%', 'a%', 'a b c', '0.1',
+    '-60% 80%', 'ultra-expannnned', '50% 0'
   ],
 };
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-bind-fake.js b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-bind-fake.js
index acb0e308..cb3a0046 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-bind-fake.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-bind-fake.js
@@ -1,22 +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.
+<html>
+<head>
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../http/tests/inspector/console-test.js"></script>
+<script>
+var foo = 'fooValue';
 
-(async function() {
-  TestRunner.addResult(`Tests that overriding Function.prototype.bind does not break inspector.\n`);
+Function.prototype.bind = function () { throw ":P"; };
 
-  await TestRunner.loadModule('console_test_runner');
-  await TestRunner.showPanel('console');
+function test()
+{
+    InspectorTest.evaluateInConsole("foo", step1);
 
-  await TestRunner.evaluateInPagePromise(`
-     var foo = 'fooValue';
-    Function.prototype.bind = function () { throw ":P"; };
-  `);
+    function step1()
+    {
+        InspectorTest.dumpConsoleMessages();
+        InspectorTest.completeTest();
+    }
+}
+</script>
+</head>
 
-  ConsoleTestRunner.evaluateInConsole('foo', step1);
+<body onload="runTest()">
+<p>
+Tests that overriding Function.prototype.bind does not break inspector.
+</p>
 
-  function step1() {
-    ConsoleTestRunner.dumpConsoleMessages();
-    TestRunner.completeTest();
-  }
-})();
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-call-getter-on-proto-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-call-getter-on-proto-expected.txt
index ad4c2dcb..c7870da 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-call-getter-on-proto-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-call-getter-on-proto-expected.txt
@@ -1,6 +1,7 @@
+CONSOLE MESSAGE: line 21: [object Object]
 Tests that calling getter on prototype will call it on the object.
 
-console-call-getter-on-proto.js:27 B {value: 239}
+console-call-getter-on-proto.html:21 B {value: 239}
     foo: 239
     value: 239
     __proto__: A
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-call-getter-on-proto.js b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-call-getter-on-proto.js
index 5fba12f..327cb864 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-call-getter-on-proto.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-call-getter-on-proto.js
@@ -1,54 +1,58 @@
-// 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.
-
-(async function() {
-  TestRunner.addResult(`Tests that calling getter on prototype will call it on the object.\n`);
-
-  await TestRunner.loadModule('console_test_runner');
-  await TestRunner.showPanel('console');
-
-  await TestRunner.evaluateInPagePromise(`
-     function logObject()
-    {
-        var A = function() { this.value = 239; }
-        A.prototype = {
-            constructor: A,
-            get foo()
-            {
-                return this.value;
-            }
+<html>
+<head>
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../http/tests/inspector/console-test.js"></script>
+<script>
+function logObject()
+{
+    var A = function() { this.value = 239; }
+    A.prototype = {
+        constructor: A,
+        get foo()
+        {
+            return this.value;
         }
-        var B = function() { A.call(this); }
-        B.prototype = {
-            constructor: B,
-            __proto__: A.prototype
-        }
-        console.log(new B());
     }
-  `);
+    var B = function() { A.call(this); }
+    B.prototype = {
+        constructor: B,
+        __proto__: A.prototype
+    }
+    console.log(new B());
+}
 
-  TestRunner.evaluateInPage('logObject()', step2);
+function test()
+{
+    InspectorTest.evaluateInPage("logObject()", step2);
+    function step2()
+    {
+        InspectorTest.expandConsoleMessages(step3);
+    }
+    function expandTreeElementFilter(treeElement)
+    {
+        var name = treeElement.nameElement && treeElement.nameElement.textContent;
+        return name === "__proto__";
+    }
+    function step3()
+    {
+        InspectorTest.expandConsoleMessages(step4, expandTreeElementFilter);
+    }
+    function step4()
+    {
+        InspectorTest.expandGettersInConsoleMessages(step5);
+    }
+    function step5()
+    {
+        InspectorTest.dumpConsoleMessages(false, false, InspectorTest.textContentWithLineBreaks);
+        InspectorTest.completeTest();
+    }
+}
+</script>
+</head>
 
-  function step2() {
-    ConsoleTestRunner.expandConsoleMessages(step3);
-  }
-
-  function expandTreeElementFilter(treeElement) {
-    var name = treeElement.nameElement && treeElement.nameElement.textContent;
-    return name === '__proto__';
-  }
-
-  function step3() {
-    ConsoleTestRunner.expandConsoleMessages(step4, expandTreeElementFilter);
-  }
-
-  function step4() {
-    ConsoleTestRunner.expandGettersInConsoleMessages(step5);
-  }
-
-  function step5() {
-    ConsoleTestRunner.dumpConsoleMessages(false, false, TestRunner.textContentWithLineBreaks);
-    TestRunner.completeTest();
-  }
-})();
+<body onload="runTest()">
+<p>
+Tests that calling getter on prototype will call it on the object.
+</p>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-command-clear-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-command-clear-expected.txt
index 5636c991..dc9c35e 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-command-clear-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-command-clear-expected.txt
@@ -1,9 +1,12 @@
+CONSOLE MESSAGE: line 9: one
+CONSOLE MESSAGE: line 10: two
+CONSOLE MESSAGE: line 11: three
 Tests that console is cleared upon clear() eval in console.
 
 === Before clear ===
-console-command-clear.js:14 one
-console-command-clear.js:15 two
-console-command-clear.js:16 three
+console-command-clear.html:9 one
+console-command-clear.html:10 two
+console-command-clear.html:11 three
 === After clear ===
 VM:1 Console was cleared
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-command-clear.js b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-command-clear.js
index 5b704d56..ed79daa 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-command-clear.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-command-clear.js
@@ -1,31 +1,40 @@
-// 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.
+<html>
+<head>
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../http/tests/inspector/console-test.js"></script>
+<script>
 
-(async function() {
-  TestRunner.addResult(`Tests that console is cleared upon clear() eval in console.\n`);
+function log() {
+    // Fill console.
+    console.log("one");
+    console.log("two");
+    console.log("three");
+}
 
-  await TestRunner.loadModule('console_test_runner');
-  await TestRunner.showPanel('console');
+log();
 
-  await TestRunner.evaluateInPagePromise(`
-     function log() {
-        // Fill console.
-        console.log("one");
-        console.log("two");
-        console.log("three");
+function test()
+{
+
+    InspectorTest.addResult("=== Before clear ==="); 
+    InspectorTest.dumpConsoleMessages();
+
+    function callback()
+    {
+        InspectorTest.addResult("=== After clear ==="); 
+        InspectorTest.dumpConsoleMessages();
+        InspectorTest.completeTest();
     }
-    log();
-  `);
+    InspectorTest.evaluateInConsole("clear()", callback);
+}
 
-  TestRunner.addResult('=== Before clear ===');
-  ConsoleTestRunner.dumpConsoleMessages();
+</script>
+</head>
 
-  function callback() {
-    TestRunner.addResult('=== After clear ===');
-    ConsoleTestRunner.dumpConsoleMessages();
-    TestRunner.completeTest();
-  }
+<body onload="runTest()">
+<p>
+    Tests that console is cleared upon clear() eval in console.
+</p>
 
-  ConsoleTestRunner.evaluateInConsole('clear()', callback);
-})();
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-control-characters.js b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-control-characters.js
index ce574d1..afdc95a 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-control-characters.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-control-characters.js
@@ -1,17 +1,28 @@
-// 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.
+<html>
+<head>
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../http/tests/inspector/console-test.js"></script>
+<script>
 
-(async function() {
-  TestRunner.addResult(`Verify that control characters are substituted with printable characters.\n`);
+function test()
+{
+    // The following command has control character.
+    InspectorTest.evaluateInConsole("var\u001D i = 0;", onEvaluated);
 
-  await TestRunner.loadModule('console_test_runner');
-  await TestRunner.showPanel('console');
+    function onEvaluated()
+    {
+        InspectorTest.dumpConsoleMessages();
+        InspectorTest.completeTest();
+    }
+}
 
-  ConsoleTestRunner.evaluateInConsole('var\u001d i = 0;', onEvaluated);
+</script>
+</head>
 
-  function onEvaluated() {
-    ConsoleTestRunner.dumpConsoleMessages();
-    TestRunner.completeTest();
-  }
-})();
+<body onload="runTest()">
+<p>
+    Verify that control characters are substituted with printable characters.
+</p>
+
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-correct-suggestions.js b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-correct-suggestions.js
index f68a830..b13a31f 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-correct-suggestions.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-correct-suggestions.js
@@ -1,127 +1,123 @@
-// 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.
+<html>
+<head>
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../http/tests/inspector/console-test.js"></script>
+<script>
+function templateString()
+{
+    console.log("The template string should not run and you should not see this log");
+    return {
+        shouldNotFindThis:56
+    };
+}
 
-(async function() {
-  TestRunner.addResult(`Tests that console correctly finds suggestions in complicated cases.\n`);
+function shouldNotFindThisFunction() { }
+function shouldFindThisFunction() { }
+window["should not find this"] = true;
 
-  await TestRunner.loadModule('console_test_runner');
-  await TestRunner.showPanel('console');
-
-  await TestRunner.evaluateInPagePromise(`
-     function templateString()
+var myMap = new Map([['first', 1], ['second', 2], ['third', 3], ['shouldNotFindThis', 4]]);
+var complicatedObject = {
+    'foo-bar': true,
+    '"double-qouted"': true,
+    "'single-qouted'": true,
+    "notDangerous();": true
+}
+function test()
+{
+    var consoleEditor;
+    function testCompletions(text, expected, force)
     {
-        console.log("The template string should not run and you should not see this log");
-        return {
-            shouldNotFindThis:56
-        };
-    }
-    function shouldNotFindThisFunction() { }
-    function shouldFindThisFunction() { }
-    window["should not find this"] = true;
-    var myMap = new Map([['first', 1], ['second', 2], ['third', 3], ['shouldNotFindThis', 4]]);
-    var complicatedObject = {
-        'foo-bar': true,
-        '"double-qouted"': true,
-        "'single-qouted'": true,
-        "notDangerous();": true
-    }
-  `);
+        var cursorPosition = text.indexOf('|');
+        if (cursorPosition < 0)
+            cursorPosition = Infinity;
+        consoleEditor.setText(text.replace('|', ''));
+        consoleEditor.setSelection(TextUtils.TextRange.createFromLocation(0, cursorPosition));
+        consoleEditor._autocompleteController.autocomplete(force);
+        return InspectorTest.addSnifferPromise(consoleEditor._autocompleteController, "_onSuggestionsShownForTest").then(checkExpected);
 
-  var consoleEditor;
-
-  function testCompletions(text, expected, force) {
-    var cursorPosition = text.indexOf('|');
-
-    if (cursorPosition < 0)
-      cursorPosition = Infinity;
-
-    consoleEditor.setText(text.replace('|', ''));
-    consoleEditor.setSelection(TextUtils.TextRange.createFromLocation(0, cursorPosition));
-    consoleEditor._autocompleteController.autocomplete(force);
-    return TestRunner.addSnifferPromise(consoleEditor._autocompleteController, '_onSuggestionsShownForTest').then(checkExpected);
-
-    function checkExpected(suggestions) {
-      var completions = new Map(suggestions.map(suggestion => [suggestion.text, suggestion]));
-      var message = 'Checking \'' + text.replace('\n', '\\n').replace('\r', '\\r') + '\'';
-
-      if (force)
-        message += ' forcefully';
-
-      TestRunner.addResult(message);
-
-      for (var i = 0; i < expected.length; i++) {
-        if (completions.has(expected[i])) {
-          if (completions.get(expected[i]).title)
-            TestRunner.addResult('Found: ' + expected[i] + ', displayed as ' + completions.get(expected[i]).title);
-          else
-            TestRunner.addResult('Found: ' + expected[i]);
-        } else {
-          TestRunner.addResult('Not Found: ' + expected[i]);
+        function checkExpected(suggestions)
+        {
+            var completions = new Map(suggestions.map(suggestion => [suggestion.text, suggestion]));
+            var message = "Checking '" + text.replace('\n', '\\n').replace('\r', '\\r') + "'";
+            if (force)
+                message += " forcefully";
+            InspectorTest.addResult(message);
+            for (var i = 0; i < expected.length; i++) {
+                if (completions.has(expected[i])) {
+                    if (completions.get(expected[i]).title)
+                        InspectorTest.addResult("Found: " + expected[i] + ", displayed as " + completions.get(expected[i]).title);
+                    else
+                        InspectorTest.addResult("Found: " + expected[i]);
+                } else {
+                    InspectorTest.addResult("Not Found: " + expected[i]);
+                }
+            }
+            InspectorTest.addResult("");
         }
-      }
-
-      TestRunner.addResult('');
     }
-  }
+    function sequential(tests)
+    {
+        var promise = Promise.resolve();
+        for (var i = 0; i < tests.length; i++)
+            promise = promise.then(tests[i]);
+        return promise;
+    }
 
-  function sequential(tests) {
-    var promise = Promise.resolve();
+    sequential([
+        InspectorTest.waitUntilConsoleEditorLoaded().then(e => consoleEditor = e),
+        () => testCompletions("window.do", ["document"]),
+        () => testCompletions("win", ["window"]),
+        () => testCompletions('window["doc', ['"document"]']),
+        () => testCompletions('window["document"].bo', ["body"]),
+        () => testCompletions('window["document"]["body"].textC', ["textContent"]),
+        () => testCompletions('document.body.inner', ["innerText", "innerHTML"]),
+        () => testCompletions('document["body"][window.do', ["document"]),
+        () => testCompletions('document["body"][window["document"].body.childNodes[0].text', ["textContent"]),
+        () => testCompletions("templateString`asdf`should", ["shouldNotFindThis"]),
+        () => testCompletions("window.document.BODY", ["body"]),
+        () => testCompletions("window.dOcUmE", ["document"]),
+        () => testCompletions("window.node", ["NodeList", "AudioNode", "GainNode"]),
+        () => testCompletions("32", ["Float32Array", "Int32Array"]),
+        () => testCompletions("window.32", ["Float32Array", "Int32Array"]),
+        () => testCompletions("", ["window"], false),
+        () => testCompletions("", ["window"], true),
+        () => testCompletions('"string g', ["getComputedStyle"], false),
+        () => testCompletions("`template string docu", ["document"], false),
+        () => testCompletions("`${do", ["document"], false),
+        () => testCompletions("// do", ["document"], false),
+        () => testCompletions('["should', ["shouldNotFindThisFunction"]),
+        () => testCompletions("shou", ["should not find this"]),
+        () => testCompletions('myMap.get(', ['"first")', '"second")', '"third")']),
+        () => testCompletions('myMap.get(\'', ['\'first\')', '\'second\')', '\'third\')']),
+        () => testCompletions('myMap.set(\'firs', ['\'first\', ']),
+        () => testCompletions('myMap.set(should', ['shouldFindThisFunction', 'shouldNotFindThis', '\"shouldNotFindThis\")']),
+        () => testCompletions('myMap.delete(\'', ['\'first\')', '\'second\')', '\'third\')']),
+        () => testCompletions("document.   bo", ["body"]),
+        () => testCompletions("document.\tbo", ["body"]),
+        () => testCompletions("document.\nbo", ["body"]),
+        () => testCompletions("document.\r\nbo", ["body"]),
+        () => testCompletions("document   [    'bo", ["'body']"]),
+        () => testCompletions("function hey(should", ["shouldNotFindThisFunction"]),
+        () => testCompletions("var should", ["shouldNotFindThisFunction"]),
+        () => testCompletions("document[[win", ["window"]),
+        () => testCompletions("document[   [win", ["window"]),
+        () => testCompletions("document[   [  win", ["window"]),
+        () => testCompletions('I|mag', ['Image', 'Infinity']),
+        () => testCompletions('var x = (do|);', ['document']),
+        () => testCompletions('complicatedObject["foo', ['"foo-bar"]']),
+        () => testCompletions('complicatedObject["foo-', ['"foo-bar"]']),
+        () => testCompletions('complicatedObject["foo-bar', ['"foo-bar"]']),
+        () => testCompletions('complicatedObject["\'sing', ['"\'single-qouted\'"]']),
+        () => testCompletions('complicatedObject[\'\\\'sing', ['\'\\\'single-qouted\\\'\']']),
+        () => testCompletions('complicatedObject["\'single-qou', ['"\'single-qouted\'"]']),
+        () => testCompletions('complicatedObject["\\"double-qouted\\"', ['"\\"double-qouted\\""]']),
+        () => testCompletions('complicatedObject["notDangerous();', ['"notDangerous();"]']),
+    ]).then(InspectorTest.completeTest);
 
-    for (var i = 0; i < tests.length; i++)
-      promise = promise.then(tests[i]);
-
-    return promise;
-  }
-
-  sequential([
-    () => ConsoleTestRunner.waitUntilConsoleEditorLoaded().then(e => consoleEditor = e),
-    () => testCompletions('window.do', ['document']),
-    () => testCompletions('win', ['window']),
-    () => testCompletions('window["doc', ['"document"]']),
-    () => testCompletions('window["document"].bo', ['body']),
-    () => testCompletions('window["document"]["body"].textC', ['textContent']),
-    () => testCompletions('document.body.inner', ['innerText', 'innerHTML']),
-    () => testCompletions('document["body"][window.do', ['document']),
-    () => testCompletions('document["body"][window["document"].body.childNodes[0].text', ['textContent']),
-    () => testCompletions('templateString`asdf`should', ['shouldNotFindThis']),
-    () => testCompletions('window.document.BODY', ['body']),
-    () => testCompletions('window.dOcUmE', ['document']),
-    () => testCompletions('window.node', ['NodeList', 'AudioNode', 'GainNode']),
-    () => testCompletions('32', ['Float32Array', 'Int32Array']),
-    () => testCompletions('window.32', ['Float32Array', 'Int32Array']),
-    () => testCompletions('', ['window'], false),
-    () => testCompletions('', ['window'], true),
-    () => testCompletions('"string g', ['getComputedStyle'], false),
-    () => testCompletions('`template string docu', ['document'], false),
-    () => testCompletions('`${do', ['document'], false),
-    () => testCompletions('// do', ['document'], false),
-    () => testCompletions('["should', ['shouldNotFindThisFunction']),
-    () => testCompletions('shou', ['should not find this']),
-    () => testCompletions('myMap.get(', ['"first")', '"second")', '"third")']),
-    () => testCompletions('myMap.get(\'', ['\'first\')', '\'second\')', '\'third\')']),
-    () => testCompletions('myMap.set(\'firs', ['\'first\', ']),
-    () => testCompletions('myMap.set(should', ['shouldFindThisFunction', 'shouldNotFindThis', '"shouldNotFindThis")']),
-    () => testCompletions('myMap.delete(\'', ['\'first\')', '\'second\')', '\'third\')']),
-    () => testCompletions('document.   bo', ['body']),
-    () => testCompletions('document.\tbo', ['body']),
-    () => testCompletions('document.\nbo', ['body']),
-    () => testCompletions('document.\r\nbo', ['body']),
-    () => testCompletions('document   [    \'bo', ['\'body\']']),
-    () => testCompletions('function hey(should', ['shouldNotFindThisFunction']),
-    () => testCompletions('var should', ['shouldNotFindThisFunction']),
-    () => testCompletions('document[[win', ['window']),
-    () => testCompletions('document[   [win', ['window']),
-    () => testCompletions('document[   [  win', ['window']),
-    () => testCompletions('I|mag', ['Image', 'Infinity']),
-    () => testCompletions('var x = (do|);', ['document']),
-    () => testCompletions('complicatedObject["foo', ['"foo-bar"]']),
-    () => testCompletions('complicatedObject["foo-', ['"foo-bar"]']),
-    () => testCompletions('complicatedObject["foo-bar', ['"foo-bar"]']),
-    () => testCompletions('complicatedObject["\'sing', ['"\'single-qouted\'"]']),
-    () => testCompletions('complicatedObject[\'\\\'sing', ['\'\\\'single-qouted\\\'\']']),
-    () => testCompletions('complicatedObject["\'single-qou', ['"\'single-qouted\'"]']),
-    () => testCompletions('complicatedObject["\\"double-qouted\\"', ['"\\"double-qouted\\""]']),
-    () => testCompletions('complicatedObject["notDangerous();', ['"notDangerous();"]'])
-  ]).then(TestRunner.completeTest);
-})();
+}
+</script>
+</head>
+<body onload="runTest()">
+<p>Tests that console correctly finds suggestions in complicated cases.</p>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-css-unterminated-comment-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-css-unterminated-comment-expected.txt
new file mode 100644
index 0000000..3343832
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-css-unterminated-comment-expected.txt
@@ -0,0 +1 @@
+Tests that unterminated comment in CSS generates a warning.
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-css-unterminated-comment.js b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-css-unterminated-comment.js
new file mode 100644
index 0000000..28d47c1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-css-unterminated-comment.js
@@ -0,0 +1,30 @@
+<html>
+<head>
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../http/tests/inspector/console-test.js"></script>
+
+<style>/* unterminated comment </style>
+<style>/*</style>
+
+<style>/* terminated comment */</style>
+<style>/* terminated comment *//*</style>
+
+<script>
+
+function test()
+{
+    InspectorTest.consoleModel.messages().forEach(function(message)
+    {
+        InspectorTest.addResult(message.message + " (line " + message.line + ")");
+    });
+
+    InspectorTest.completeTest();
+}
+
+</script>
+</head>
+
+<body onload="runTest()">
+<p id="p">Tests that unterminated comment in CSS generates a warning.</p>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-dir-deprecated-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-dir-deprecated-expected.txt
index 0cb9383..159fd40 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-dir-deprecated-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-dir-deprecated-expected.txt
@@ -1,5 +1,7 @@
+CONSOLE MESSAGE: line 9: [object Window]
+CONSOLE MESSAGE: line 10: [object ShadowRoot]
 Tests that console does not log deprecated warning messages while dir-dumping objects.
 
-console-dir-deprecated.js:17 Window
-console-dir-deprecated.js:18 #document-fragment
+console-dir-deprecated.html:9 Window
+console-dir-deprecated.html:10 #document-fragment
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-dir-deprecated.js b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-dir-deprecated.js
index c52ed56..d672dc0 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-dir-deprecated.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-dir-deprecated.js
@@ -1,28 +1,33 @@
-// 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.
+<html>
+<head>
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../http/tests/inspector/console-test.js"></script>
+<script>
 
-(async function() {
-  TestRunner.addResult(`Tests that console does not log deprecated warning messages while dir-dumping objects.\n`);
+function logObjects()
+{
+    console.dir(window);
+    console.dir(document.getElementById("foo").createShadowRoot());
+}
 
-  await TestRunner.loadModule('console_test_runner');
-  await TestRunner.showPanel('console');
-
-  await TestRunner.loadHTML(`
-    <div id="foo"></div>
-  `);
-  await TestRunner.evaluateInPagePromise(`
-     function logObjects()
+function test()
+{
+    InspectorTest.evaluateInPage("logObjects()", step2);
+    function step2()
     {
-        console.dir(window);
-        console.dir(document.getElementById("foo").createShadowRoot());
+        InspectorTest.dumpConsoleMessages();
+        InspectorTest.completeTest();
     }
-  `);
+}
 
-  TestRunner.evaluateInPage('logObjects()', step2);
+</script>
+</head>
 
-  function step2() {
-    ConsoleTestRunner.dumpConsoleMessages();
-    TestRunner.completeTest();
-  }
-})();
+<body onload="runTest()">
+<div id="foo"></div>
+<p>
+Tests that console does not log deprecated warning messages while dir-dumping objects.
+</p>
+
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-dir-global-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-dir-global-expected.txt
index 4111acf..e221eb1 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-dir-global-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-dir-global-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: line 9: [object Window]
 Tests that console dumps global object with properties.
 
 {
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-dir-global.js b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-dir-global.js
index 9183a1d..c3ce2fa5 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-dir-global.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-dir-global.js
@@ -1,58 +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.
+<html>
+<head>
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../http/tests/inspector/console-test.js"></script>
+<script>
 
-(async function() {
-  TestRunner.addResult(`Tests that console dumps global object with properties.\n`);
+function doit()
+{
+    console.dir(window);
+    runTest();
+}
 
-  await TestRunner.loadModule('console_test_runner');
-  await TestRunner.showPanel('console');
+function test()
+{
+    InspectorTest.RuntimeAgent.evaluate("window", "console", false).then(evalCallback);
 
-  await TestRunner.evaluateInPagePromise(`
-    function doit()
+    function evalCallback(result)
     {
-        console.dir(window);
-    };
-  `);
-
-  TestRunner.RuntimeAgent.evaluate('window', 'console', false).then(evalCallback);
-
-  function evalCallback(result) {
-    if (!result) {
-      testController.notifyDone('Exception');
-      return;
+        if (!result) {
+            testController.notifyDone("Exception");
+            return;
+        }
+        if (result.type === "error")
+            testController.notifyDone("Exception:" + result);
+        var objectProxy = InspectorTest.runtimeModel.createRemoteObject(result);
+        objectProxy.getOwnProperties(false, getPropertiesCallback);
     }
 
-    if (result.type === 'error')
-      testController.notifyDone('Exception:' + result);
-
-    var objectProxy = TestRunner.runtimeModel.createRemoteObject(result);
-    objectProxy.getOwnProperties(false, getPropertiesCallback);
-  }
-
-  function getPropertiesCallback(properties) {
-    properties.sort(ObjectUI.ObjectPropertiesSection.CompareProperties);
-
-    var golden = {
-      'window': 1,
-      'document': 1,
-      'eval': 1,
-      'console': 1,
-      'frames': 1,
-      'Array': 1,
-      'doit': 1
-    };
-
-    var result = {};
-
-    for (var i = 0; i < properties.length; ++i) {
-      var name = properties[i].name;
-
-      if (golden[name])
-        result[name] = 1;
+    function getPropertiesCallback(properties)
+    {
+        properties.sort(ObjectUI.ObjectPropertiesSection.CompareProperties);
+        var golden = { "window": 1, "document": 1, "eval": 1, "console": 1, "frames": 1, "Array": 1, "doit": 1 }; 
+        var result = {};
+        for (var i = 0; i < properties.length; ++i) {
+            var name = properties[i].name;
+            if (golden[name])
+                result[name] = 1;
+        }
+        InspectorTest.addObject(result);
+        InspectorTest.completeTest();
     }
+}
 
-    TestRunner.addObject(result);
-    TestRunner.completeTest();
-  }
-})();
+</script>
+</head>
+
+<body onload="doit()">
+<p>
+Tests that console dumps global object with properties.
+</p>
+
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-dir.js b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-dir.js
index 6ad6c2a0..219ae2f 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-dir.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-dir.js
@@ -1,71 +1,83 @@
-// 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.
+<html>
+<head>
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../http/tests/inspector/console-test.js"></script>
 
-(async function() {
-  TestRunner.addResult(`Tests that console logging dumps proper messages.\n`);
+<script>
+function onload()
+{
+    console.dir(["test1", "test2"]);
+    console.dir(document.childNodes);
+    console.dir(document.evaluate("//head", document, null, XPathResult.ANY_TYPE, null));
 
-  await TestRunner.loadModule('console_test_runner');
-  await TestRunner.showPanel('console');
+    // Object with properties containing whitespaces
+    var obj = { $foo5_: 0 };
+    obj[" a b "] = " a b ";
+    obj["c d"] = "c d";
+    obj[""] = "";
+    obj["  "] = "  ";
+    obj["a\n\nb\nc"] = "a\n\nb\nc";
+    obj["negZero"] = -0;
+    console.dir(obj);
 
-  await TestRunner.evaluateInPagePromise(`
-    function onload()
+    // This should correctly display information about the function.
+    console.dir(function() {});
+
+    // Test function inferred name in prototype constructor.
+    var outer = { inner: function() {} };
+    console.dir(new outer.inner());
+
+    // Test "No Properties" placeholder.
+    console.dir({ __proto__: null });
+    console.dir({ foo: { __proto__: null }});
+    // Test "No Scopes" placeholder.
+    console.dir(Object.getOwnPropertyDescriptor(Object.prototype, "__proto__").get);
+
+    // Test big typed array: should be no crash or timeout.
+    var bigTypedArray = new Uint8Array(new ArrayBuffer(400 * 1000 * 1000));
+    bigTypedArray["FAIL"] = "FAIL: Object.getOwnPropertyNames() should not have been run";
+    console.dir(bigTypedArray);
+
+    // document.createEvent("Event") has a special property "isTrusted" flagged "Unforgeable".
+    var event = document.createEvent("Event");
+    Object.defineProperty(event, "timeStamp", {value: 0})
+    console.dir(event);
+
+    runTest();
+}
+//# sourceURL=console-dir.html
+</script>
+
+<script>
+function test()
+{
+    InspectorTest.expandConsoleMessages(step1, expandTreeElementFilter);
+
+    function expandTreeElementFilter(treeElement)
     {
-        console.dir(["test1", "test2"]);
-        console.dir(document.childNodes);
-        console.dir(document.evaluate("//head", document, null, XPathResult.ANY_TYPE, null));
-
-        // Object with properties containing whitespaces
-        var obj = { $foo5_: 0 };
-        obj[" a b "] = " a b ";
-        obj["c d"] = "c d";
-        obj[""] = "";
-        obj["  "] = "  ";
-        obj["a\n\nb\nc"] = "a\n\nb\nc";
-        obj["negZero"] = -0;
-        console.dir(obj);
-
-        // This should correctly display information about the function.
-        console.dir(function() {});
-
-        // Test function inferred name in prototype constructor.
-        var outer = { inner: function() {} };
-        console.dir(new outer.inner());
-
-        // Test "No Properties" placeholder.
-        console.dir({ __proto__: null });
-        console.dir({ foo: { __proto__: null }});
-        // Test "No Scopes" placeholder.
-        console.dir(Object.getOwnPropertyDescriptor(Object.prototype, "__proto__").get);
-
-        // Test big typed array: should be no crash or timeout.
-        var bigTypedArray = new Uint8Array(new ArrayBuffer(400 * 1000 * 1000));
-        bigTypedArray["FAIL"] = "FAIL: Object.getOwnPropertyNames() should not have been run";
-        console.dir(bigTypedArray);
-
-        // document.createEvent("Event") has a special property "isTrusted" flagged "Unforgeable".
-        var event = document.createEvent("Event");
-        Object.defineProperty(event, "timeStamp", {value: 0})
-        console.dir(event);
-
-        runTest();
+        var name = treeElement.nameElement && treeElement.nameElement.textContent;
+        return name === "foo" || treeElement.title === "<function scope>";
     }
-    //# sourceURL=console-dir.js
-  `);
 
-  ConsoleTestRunner.expandConsoleMessages(step1, expandTreeElementFilter);
+    function step1()
+    {
+        InspectorTest.expandConsoleMessages(dumpConsoleMessages, expandTreeElementFilter);
+    }
 
-  function expandTreeElementFilter(treeElement) {
-    var name = treeElement.nameElement && treeElement.nameElement.textContent;
-    return name === 'foo' || treeElement.title === '<function scope>';
-  }
+    function dumpConsoleMessages()
+    {
+        InspectorTest.dumpConsoleMessagesIgnoreErrorStackFrames();
+        InspectorTest.completeTest();
+    }
+}
 
-  function step1() {
-    ConsoleTestRunner.expandConsoleMessages(dumpConsoleMessages, expandTreeElementFilter);
-  }
+</script>
+</head>
 
-  function dumpConsoleMessages() {
-    ConsoleTestRunner.dumpConsoleMessagesIgnoreErrorStackFrames();
-    TestRunner.completeTest();
-  }
-})();
+<body onload="onload()">
+<p>
+Tests that console logging dumps proper messages.
+</p>
+
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-edit-property-value-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-edit-property-value-expected.txt
index 06548df6..ffc9ff9 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-edit-property-value-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-edit-property-value-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: line 10: [object Object]
 Tests that property values can be edited inline in the console via double click.
 
 Node was hidden after dblclick: true
@@ -5,7 +6,7 @@
 Node was hidden after dblclick: true
 Node was hidden after dblclick: true
 logToConsole()
-console-edit-property-value.js:15 {a: 1, b: "foo", c: null, d: 2}
+console-edit-property-value.html:10 {a: 1, b: "foo", c: null, d: 2}
     a: 3
     b: "foo"
     c: (3) [1, 2, 3]
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-edit-property-value.js b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-edit-property-value.js
index b104647..f4fcc8b 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-edit-property-value.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-edit-property-value.js
@@ -1,69 +1,86 @@
-// 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.
+<html>
+<head>
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../http/tests/inspector/console-test.js"></script>
+<script>
 
-(async function() {
-  TestRunner.addResult(`Tests that property values can be edited inline in the console via double click.\n`);
+function logToConsole()
+{
+    var obj = {a: 1, b: "foo", c: null, d: 2};
+    console.log(obj);
+}
 
-  await TestRunner.loadModule('console_test_runner');
-  await TestRunner.showPanel('console');
+var test = function()
+{
+    InspectorTest.evaluateInConsole("logToConsole()", step1);
 
-  await TestRunner.evaluateInPagePromise(`
-     function logToConsole()
+    function step1()
     {
-        var obj = {a: 1, b: "foo", c: null, d: 2};
-        console.log(obj);
+        InspectorTest.expandConsoleMessages(step2);
     }
-  `);
 
-  ConsoleTestRunner.evaluateInConsole('logToConsole()', step1);
+    function step2()
+    {
+        var valueElements = getValueElements();
+        doubleClickTypeAndEnter(valueElements[0], "1 + 2");
+        InspectorTest.waitForRemoteObjectsConsoleMessages(step3);
+    }
 
-  function step1() {
-    ConsoleTestRunner.expandConsoleMessages(step2);
-  }
+    function step3()
+    {
+        var valueElements = getValueElements();
+        doubleClickTypeAndEnter(valueElements[1], "nonExistingValue");
+        InspectorTest.waitForRemoteObjectsConsoleMessages(step4);
+    }
 
-  function step2() {
-    var valueElements = getValueElements();
-    doubleClickTypeAndEnter(valueElements[0], '1 + 2');
-    ConsoleTestRunner.waitForRemoteObjectsConsoleMessages(step3);
-  }
+    function step4()
+    {
+        var valueElements = getValueElements();
+        doubleClickTypeAndEnter(valueElements[2], "[1, 2, 3]");
+        InspectorTest.waitForRemoteObjectsConsoleMessages(step5);
+    }
 
-  function step3() {
-    var valueElements = getValueElements();
-    doubleClickTypeAndEnter(valueElements[1], 'nonExistingValue');
-    ConsoleTestRunner.waitForRemoteObjectsConsoleMessages(step4);
-  }
+    function step5()
+    {
+        var valueElements = getValueElements();
+        doubleClickTypeAndEnter(valueElements[3], "{x: 2}");
+        InspectorTest.waitForRemoteObjectsConsoleMessages(step6);
+    }
 
-  function step4() {
-    var valueElements = getValueElements();
-    doubleClickTypeAndEnter(valueElements[2], '[1, 2, 3]');
-    ConsoleTestRunner.waitForRemoteObjectsConsoleMessages(step5);
-  }
+    function step6()
+    {
+        InspectorTest.dumpConsoleMessagesIgnoreErrorStackFrames();
+        InspectorTest.completeTest();
+    }
 
-  function step5() {
-    var valueElements = getValueElements();
-    doubleClickTypeAndEnter(valueElements[3], '{x: 2}');
-    ConsoleTestRunner.waitForRemoteObjectsConsoleMessages(step6);
-  }
+    function getValueElements()
+    {
+        var messageElement = Console.ConsoleView.instance()._visibleViewMessages[1].element();
+        return messageElement.querySelector(".console-message-text *").shadowRoot.querySelectorAll(".value");
+    }
 
-  function step6() {
-    ConsoleTestRunner.dumpConsoleMessagesIgnoreErrorStackFrames();
-    TestRunner.completeTest();
-  }
+    function doubleClickTypeAndEnter(node, text)
+    {
+        var event = document.createEvent("MouseEvent");
+        event.initMouseEvent("dblclick", true, true, null, 2);
+        node.dispatchEvent(event);
 
-  function getValueElements() {
-    var messageElement = Console.ConsoleView.instance()._visibleViewMessages[1].element();
-    return messageElement.querySelector('.console-message-text *').shadowRoot.querySelectorAll('.value');
-  }
+        InspectorTest.addResult("Node was hidden after dblclick: " + node.classList.contains("hidden"));
 
-  function doubleClickTypeAndEnter(node, text) {
-    var event = document.createEvent('MouseEvent');
-    event.initMouseEvent('dblclick', true, true, null, 2);
-    node.dispatchEvent(event);
-    TestRunner.addResult('Node was hidden after dblclick: ' + node.classList.contains('hidden'));
-    var messageElement = Console.ConsoleView.instance()._visibleViewMessages[1].element();
-    var editPrompt = messageElement.querySelector('.console-message-text *').shadowRoot.querySelector('.text-prompt');
-    editPrompt.textContent = text;
-    editPrompt.dispatchEvent(TestRunner.createKeyEvent('Enter'));
-  }
-})();
+        var messageElement = Console.ConsoleView.instance()._visibleViewMessages[1].element();
+        var editPrompt = messageElement.querySelector(".console-message-text *").shadowRoot.querySelector(".text-prompt");
+        editPrompt.textContent = text;
+        editPrompt.dispatchEvent(InspectorTest.createKeyEvent("Enter"));
+    }
+}
+
+</script>
+</head>
+
+<body onload="runTest()">
+<p>
+Tests that property values can be edited inline in the console via double click.
+</p>
+
+</body>
+</html>
diff --git a/third_party/WebKit/Source/core/css/BUILD.gn b/third_party/WebKit/Source/core/css/BUILD.gn
index c7aeee5..c71e1f2 100644
--- a/third_party/WebKit/Source/core/css/BUILD.gn
+++ b/third_party/WebKit/Source/core/css/BUILD.gn
@@ -581,9 +581,11 @@
     "properties/CSSShorthandPropertyAPIFontVariant.cpp",
     "properties/CSSShorthandPropertyAPIGridArea.cpp",
     "properties/CSSShorthandPropertyAPIGridColumn.cpp",
+    "properties/CSSShorthandPropertyAPIGridGap.cpp",
     "properties/CSSShorthandPropertyAPIGridRow.cpp",
     "properties/CSSShorthandPropertyAPIListStyle.cpp",
     "properties/CSSShorthandPropertyAPIMargin.cpp",
+    "properties/CSSShorthandPropertyAPIMarker.cpp",
     "properties/CSSShorthandPropertyAPIOffset.cpp",
     "properties/CSSShorthandPropertyAPIOutline.cpp",
     "properties/CSSShorthandPropertyAPIOverflow.cpp",
diff --git a/third_party/WebKit/Source/core/css/CSSProperties.json5 b/third_party/WebKit/Source/core/css/CSSProperties.json5
index f8578fea..80d7367 100644
--- a/third_party/WebKit/Source/core/css/CSSProperties.json5
+++ b/third_party/WebKit/Source/core/css/CSSProperties.json5
@@ -3900,6 +3900,7 @@
       name: "grid-gap",
       longhands: ["grid-row-gap", "grid-column-gap"],
       api_class: true,
+      api_methods: ["parseShorthand"],
       runtime_flag: "CSSGridLayout",
     },
     {
@@ -3931,6 +3932,7 @@
       name: "marker",
       longhands: ["marker-start", "marker-mid", "marker-end"],
       api_class: true,
+      api_methods: ["parseShorthand"],
     },
     {
       name: "offset",
diff --git a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
index 86433e1..3f8479c 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
@@ -813,7 +813,8 @@
       parsed_value = ConsumeFontDisplay(range_);
       break;
     case CSSPropertyFontStretch:
-      parsed_value = CSSPropertyFontUtils::ConsumeFontStretch(range_);
+      parsed_value = CSSPropertyFontUtils::ConsumeFontStretch(
+          range_, kCSSFontFaceRuleMode);
       break;
     case CSSPropertyFontStyle:
       parsed_value = CSSPropertyFontUtils::ConsumeFontStyle(range_);
@@ -1378,18 +1379,6 @@
   }
 
   switch (property) {
-    case CSSPropertyMarker: {
-      const CSSValue* marker = ParseSingleValue(CSSPropertyMarkerStart);
-      if (!marker || !range_.AtEnd())
-        return false;
-      AddParsedProperty(CSSPropertyMarkerStart, CSSPropertyMarker, *marker,
-                        important);
-      AddParsedProperty(CSSPropertyMarkerMid, CSSPropertyMarker, *marker,
-                        important);
-      AddParsedProperty(CSSPropertyMarkerEnd, CSSPropertyMarker, *marker,
-                        important);
-      return true;
-    }
     case CSSPropertyBorder:
       return ConsumeBorder(important);
     case CSSPropertyBackgroundRepeat:
@@ -1414,23 +1403,6 @@
       return ConsumeBackgroundShorthand(backgroundShorthand(), important);
     case CSSPropertyWebkitMask:
       return ConsumeBackgroundShorthand(webkitMaskShorthand(), important);
-    case CSSPropertyGridGap: {
-      DCHECK(RuntimeEnabledFeatures::CSSGridLayoutEnabled());
-      DCHECK_EQ(shorthandForProperty(CSSPropertyGridGap).length(), 2u);
-      CSSValue* row_gap = ConsumeLengthOrPercent(range_, context_->Mode(),
-                                                 kValueRangeNonNegative);
-      CSSValue* column_gap = ConsumeLengthOrPercent(range_, context_->Mode(),
-                                                    kValueRangeNonNegative);
-      if (!row_gap || !range_.AtEnd())
-        return false;
-      if (!column_gap)
-        column_gap = row_gap;
-      AddParsedProperty(CSSPropertyGridRowGap, CSSPropertyGridGap, *row_gap,
-                        important);
-      AddParsedProperty(CSSPropertyGridColumnGap, CSSPropertyGridGap,
-                        *column_gap, important);
-      return true;
-    }
     case CSSPropertyGridTemplate:
       return ConsumeGridTemplateShorthand(CSSPropertyGridTemplate, important);
     case CSSPropertyGrid:
diff --git a/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIFontStretch.cpp b/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIFontStretch.cpp
index 8b4dc7be..49734d64 100644
--- a/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIFontStretch.cpp
+++ b/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIFontStretch.cpp
@@ -4,15 +4,16 @@
 
 #include "core/css/properties/CSSPropertyAPIFontStretch.h"
 
+#include "core/css/parser/CSSParserContext.h"
 #include "core/css/properties/CSSPropertyFontUtils.h"
 
 namespace blink {
 
 const CSSValue* CSSPropertyAPIFontStretch::parseSingleValue(
     CSSParserTokenRange& range,
-    const CSSParserContext&,
+    const CSSParserContext& context,
     const CSSParserLocalContext&) {
-  return CSSPropertyFontUtils::ConsumeFontStretch(range);
+  return CSSPropertyFontUtils::ConsumeFontStretch(range, context.Mode());
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/properties/CSSPropertyFontUtils.cpp b/third_party/WebKit/Source/core/css/properties/CSSPropertyFontUtils.cpp
index 1f5d23c..7a6b862 100644
--- a/third_party/WebKit/Source/core/css/properties/CSSPropertyFontUtils.cpp
+++ b/third_party/WebKit/Source/core/css/properties/CSSPropertyFontUtils.cpp
@@ -126,15 +126,28 @@
   return value_list;
 }
 
-CSSValue* CSSPropertyFontUtils::ConsumeFontStretch(CSSParserTokenRange& range) {
+CSSValue* CSSPropertyFontUtils::ConsumeFontStretch(
+    CSSParserTokenRange& range,
+    const CSSParserMode& parser_mode) {
   CSSIdentifierValue* parsed_keyword = ConsumeFontStretchKeywordOnly(range);
   if (parsed_keyword)
     return parsed_keyword;
-  CSSPrimitiveValue* percent =
+
+  CSSPrimitiveValue* start_percent =
       CSSPropertyParserHelpers::ConsumePercent(range, kValueRangeNonNegative);
-  if (!percent || percent->GetFloatValue() <= 0)
+  if (!start_percent || start_percent->GetFloatValue() <= 0)
     return nullptr;
-  return percent;
+
+  // In a non-font-face context, more than one percentage is not allowed.
+  if (parser_mode != kCSSFontFaceRuleMode || range.AtEnd())
+    return start_percent;
+
+  CSSPrimitiveValue* end_percent =
+      CSSPropertyParserHelpers::ConsumePercent(range, kValueRangeNonNegative);
+  if (!end_percent || end_percent->GetFloatValue() <= 0)
+    return nullptr;
+
+  return CombineToRangeListOrNull(start_percent, end_percent);
 }
 
 CSSValue* CSSPropertyFontUtils::ConsumeFontWeight(
@@ -149,8 +162,7 @@
       (token.NumericValue() < 1 || token.NumericValue() > 1000))
     return nullptr;
 
-  CSSPrimitiveValue* start_weight = nullptr;
-  start_weight =
+  CSSPrimitiveValue* start_weight =
       CSSPropertyParserHelpers::ConsumeNumber(range, kValueRangeNonNegative);
   if (!start_weight || start_weight->GetFloatValue() < 1 ||
       start_weight->GetFloatValue() > 1000)
@@ -159,22 +171,16 @@
   // In a non-font-face context, more than one number is not allowed. Return
   // what we have. If there is trailing garbage, the AtEnd() check in
   // CSSPropertyParser::ParseValueStart will catch that.
-  if (parser_mode != kCSSFontFaceRuleMode)
+  if (parser_mode != kCSSFontFaceRuleMode || range.AtEnd())
     return start_weight;
 
-  CSSPrimitiveValue* end_weight = nullptr;
-  if (!range.AtEnd()) {
-    end_weight =
-        CSSPropertyParserHelpers::ConsumeNumber(range, kValueRangeNonNegative);
-    if (!end_weight || end_weight->GetFloatValue() < 1 ||
-        end_weight->GetFloatValue() > 1000)
-      return nullptr;
-  }
+  CSSPrimitiveValue* end_weight =
+      CSSPropertyParserHelpers::ConsumeNumber(range, kValueRangeNonNegative);
+  if (!end_weight || end_weight->GetFloatValue() < 1 ||
+      end_weight->GetFloatValue() > 1000)
+    return nullptr;
 
-  if (end_weight)
-    return CombineToRangeListOrNull(start_weight, end_weight);
-
-  return start_weight;
+  return CombineToRangeListOrNull(start_weight, end_weight);
 }
 
 // TODO(bugsnash): move this to the FontFeatureSettings API when it is no longer
diff --git a/third_party/WebKit/Source/core/css/properties/CSSPropertyFontUtils.h b/third_party/WebKit/Source/core/css/properties/CSSPropertyFontUtils.h
index 44f98e5..9eb2accb 100644
--- a/third_party/WebKit/Source/core/css/properties/CSSPropertyFontUtils.h
+++ b/third_party/WebKit/Source/core/css/properties/CSSPropertyFontUtils.h
@@ -35,7 +35,8 @@
 
   static CSSIdentifierValue* ConsumeFontStretchKeywordOnly(
       CSSParserTokenRange&);
-  static CSSValue* ConsumeFontStretch(CSSParserTokenRange&);
+  static CSSValue* ConsumeFontStretch(CSSParserTokenRange&,
+                                      const CSSParserMode&);
   static CSSValue* ConsumeFontStyle(CSSParserTokenRange&);
   static CSSValue* ConsumeFontWeight(CSSParserTokenRange&,
                                      const CSSParserMode&);
diff --git a/third_party/WebKit/Source/core/css/properties/CSSShorthandPropertyAPIGridGap.cpp b/third_party/WebKit/Source/core/css/properties/CSSShorthandPropertyAPIGridGap.cpp
new file mode 100644
index 0000000..3cddfba9
--- /dev/null
+++ b/third_party/WebKit/Source/core/css/properties/CSSShorthandPropertyAPIGridGap.cpp
@@ -0,0 +1,39 @@
+// 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 "core/css/properties/CSSShorthandPropertyAPIGridGap.h"
+
+#include "core/StylePropertyShorthand.h"
+#include "core/css/parser/CSSParserContext.h"
+#include "core/css/parser/CSSPropertyParserHelpers.h"
+#include "platform/RuntimeEnabledFeatures.h"
+
+namespace blink {
+
+bool CSSShorthandPropertyAPIGridGap::parseShorthand(
+    bool important,
+    CSSParserTokenRange& range,
+    const CSSParserContext& context,
+    bool use_legacy_parsing,
+    HeapVector<CSSProperty, 256>& properties) {
+  DCHECK(RuntimeEnabledFeatures::CSSGridLayoutEnabled());
+  DCHECK_EQ(shorthandForProperty(CSSPropertyGridGap).length(), 2u);
+  CSSValue* row_gap = CSSPropertyParserHelpers::ConsumeLengthOrPercent(
+      range, context.Mode(), kValueRangeNonNegative);
+  CSSValue* column_gap = CSSPropertyParserHelpers::ConsumeLengthOrPercent(
+      range, context.Mode(), kValueRangeNonNegative);
+  if (!row_gap || !range.AtEnd())
+    return false;
+  if (!column_gap)
+    column_gap = row_gap;
+  CSSPropertyParserHelpers::AddProperty(
+      CSSPropertyGridRowGap, CSSPropertyGridGap, *row_gap, important,
+      CSSPropertyParserHelpers::IsImplicitProperty::kNotImplicit, properties);
+  CSSPropertyParserHelpers::AddProperty(
+      CSSPropertyGridColumnGap, CSSPropertyGridGap, *column_gap, important,
+      CSSPropertyParserHelpers::IsImplicitProperty::kNotImplicit, properties);
+  return true;
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/properties/CSSShorthandPropertyAPIMarker.cpp b/third_party/WebKit/Source/core/css/properties/CSSShorthandPropertyAPIMarker.cpp
new file mode 100644
index 0000000..ff22dc04
--- /dev/null
+++ b/third_party/WebKit/Source/core/css/properties/CSSShorthandPropertyAPIMarker.cpp
@@ -0,0 +1,38 @@
+// 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 "core/css/properties/CSSShorthandPropertyAPIMarker.h"
+
+#include "core/css/parser/CSSPropertyParserHelpers.h"
+
+namespace blink {
+
+bool CSSShorthandPropertyAPIMarker::parseShorthand(
+    bool important,
+    CSSParserTokenRange& range,
+    const CSSParserContext& context,
+    bool use_legacy_parsing,
+    HeapVector<CSSProperty, 256>& properties) {
+  bool needs_legacy_parsing = false;
+  const CSSValue* marker = CSSPropertyParserHelpers::ParseLonghandViaAPI(
+      CSSPropertyMarkerStart, CSSPropertyMarker, context, range,
+      needs_legacy_parsing);
+  if (!marker || !range.AtEnd())
+    return false;
+
+  DCHECK(!needs_legacy_parsing);
+
+  CSSPropertyParserHelpers::AddProperty(
+      CSSPropertyMarkerStart, CSSPropertyMarker, *marker, important,
+      CSSPropertyParserHelpers::IsImplicitProperty::kNotImplicit, properties);
+  CSSPropertyParserHelpers::AddProperty(
+      CSSPropertyMarkerMid, CSSPropertyMarker, *marker, important,
+      CSSPropertyParserHelpers::IsImplicitProperty::kNotImplicit, properties);
+  CSSPropertyParserHelpers::AddProperty(
+      CSSPropertyMarkerEnd, CSSPropertyMarker, *marker, important,
+      CSSPropertyParserHelpers::IsImplicitProperty::kNotImplicit, properties);
+  return true;
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/devtools/front_end/console_test_runner/ConsoleTestRunner.js b/third_party/WebKit/Source/devtools/front_end/console_test_runner/ConsoleTestRunner.js
index d00e6e6..cef7ac9 100644
--- a/third_party/WebKit/Source/devtools/front_end/console_test_runner/ConsoleTestRunner.js
+++ b/third_party/WebKit/Source/devtools/front_end/console_test_runner/ConsoleTestRunner.js
@@ -217,7 +217,7 @@
 };
 
 /**
- * @param {function(!Element):string|undefined} messageFormatter
+ * @param {function(!Element):string} messageFormatter
  * @param {!Element} node
  * @return {string}
  */
@@ -261,7 +261,7 @@
     printOriginatingCommand, dumpClassNames, messageFormatter) {
   TestRunner.addResults(ConsoleTestRunner.dumpConsoleMessagesIntoArray(
       printOriginatingCommand, dumpClassNames,
-      ConsoleTestRunner.formatterIgnoreStackFrameUrls.bind(this, messageFormatter)));
+      messageFormatter ? ConsoleTestRunner.formatterIgnoreStackFrameUrls.bind(this, messageFormatter) : undefined));
 };
 
 ConsoleTestRunner.dumpConsoleMessagesWithStyles = function() {
@@ -410,11 +410,11 @@
 ConsoleTestRunner.waitUntilConsoleEditorLoaded = function() {
   var fulfill;
   var promise = new Promise(x => (fulfill = x));
-  var prompt = Console.ConsoleView.instance()._prompt;
-  if (prompt._editor)
-    fulfill(prompt._editor);
+  var editor = Console.ConsoleView.instance()._prompt._editor;
+  if (editor)
+    fulfill(editor);
   else
-    TestRunner.addSniffer(Console.ConsolePrompt.prototype, '_editorSetForTest', _ => fulfill(prompt._editor));
+    TestRunner.addSniffer(Console.ConsolePrompt.prototype, '_editorSetForTest', _ => fulfill(editor));
   return promise;
 };
 
diff --git a/third_party/WebKit/Source/devtools/scripts/migrate_test/migrate_test.js b/third_party/WebKit/Source/devtools/scripts/migrate_test/migrate_test.js
index f9face9..4c52bd49 100644
--- a/third_party/WebKit/Source/devtools/scripts/migrate_test/migrate_test.js
+++ b/third_party/WebKit/Source/devtools/scripts/migrate_test/migrate_test.js
@@ -56,13 +56,9 @@
     helperScripts.push(filename);
   });
 
-  const testsPath = path.resolve(__dirname, 'tests.txt');
-  const newToOldTests = new Map(fs.readFileSync(testsPath, 'utf-8').split('\n').map(line => line.split(' ').reverse()));
-  const originalTestPath = path.resolve(
-      __dirname, '..', '..', '..', '..', 'LayoutTests', newToOldTests.get(inputPath.slice(inputPath.indexOf('http/'))));
-
-  const srcResourcePaths = resourceScripts.map(s => path.resolve(path.dirname(originalTestPath), s));
-  const destResourcePaths = resourceScripts.map(s => path.resolve(path.dirname(inputPath), s));
+  const outPath = migrateUtils.getOutPath(inputPath);
+  const srcResourcePaths = resourceScripts.map(s => path.resolve(path.dirname(inputPath), s));
+  const destResourcePaths = resourceScripts.map(s => path.resolve(path.dirname(outPath), s));
   const relativeResourcePaths = destResourcePaths.map(p => p.slice(p.indexOf('/http/tests') + '/http/tests'.length));
 
   let outputCode;
@@ -90,9 +86,16 @@
 
   console.log(outputCode);
   if (!DRY_RUN) {
-    fs.writeFileSync(inputPath, outputCode);
+    mkdirp.sync(path.dirname(outPath));
+
+    fs.writeFileSync(outPath, outputCode);
+    const expectationsPath = inputPath.replace('.html', '-expected.txt');
+    copyExpectations(expectationsPath, outPath);
     copyResourceScripts(srcResourcePaths, destResourcePaths);
-    console.log('Migrated: ', inputPath);
+
+    fs.unlinkSync(inputPath);
+    fs.unlinkSync(expectationsPath);
+    console.log('Migrated to: ', outPath);
   }
 }
 
@@ -176,11 +179,12 @@
    * Create test header based on extracted data
    */
   const headerLines = [];
-  headerLines.push(createExpressionNode(`TestRunner.addResult(\`${bodyText}\\n\`);`));
+  headerLines.push(createExpressionNode(`TestRunner.addResult('${bodyText}\\n');`));
   headerLines.push(createNewLineNode());
   for (const helper of allTestHelpers) {
     headerLines.push(createAwaitExpressionNode(`await TestRunner.loadModule('${helper}');`));
   }
+  headerLines.push(createAwaitExpressionNode(`await TestRunner.loadPanel('${panel}');`));
   headerLines.push(createAwaitExpressionNode(`await TestRunner.showPanel('${panel}');`));
 
   if (domFixture) {
diff --git a/third_party/WebKit/Source/devtools/scripts/migrate_test/tests.txt b/third_party/WebKit/Source/devtools/scripts/migrate_test/tests.txt
index a45b311d..a7acb6b 100644
--- a/third_party/WebKit/Source/devtools/scripts/migrate_test/tests.txt
+++ b/third_party/WebKit/Source/devtools/scripts/migrate_test/tests.txt
@@ -1,8 +1,18 @@
 inspector/console/alert-toString-exception.html 
 inspector/console/console-api-on-call-frame.html 
+inspector/console/console-bind-fake.html http/tests/devtools/console/console-bind-fake.js
+inspector/console/console-call-getter-on-proto.html http/tests/devtools/console/console-call-getter-on-proto.js
 inspector/console/console-clear-function.html 
+inspector/console/console-command-clear.html http/tests/devtools/console/console-command-clear.js
 inspector/console/console-command-copy.html 
 inspector/console/console-context-selector.html 
+inspector/console/console-control-characters.html http/tests/devtools/console/console-control-characters.js
 inspector/console/console-copy-treeoutline.html 
 inspector/console/console-copy-truncated-text.html 
+inspector/console/console-correct-suggestions.html http/tests/devtools/console/console-correct-suggestions.js
+inspector/console/console-css-unterminated-comment.html http/tests/devtools/console/console-css-unterminated-comment.js
+inspector/console/console-dir-deprecated.html http/tests/devtools/console/console-dir-deprecated.js
+inspector/console/console-dir-global.html http/tests/devtools/console/console-dir-global.js
+inspector/console/console-dir.html http/tests/devtools/console/console-dir.js
+inspector/console/console-edit-property-value.html http/tests/devtools/console/console-edit-property-value.js
 inspector/console/console-error-on-call-frame.html 
\ No newline at end of file
diff --git a/third_party/WebKit/Source/devtools/scripts/migrate_test/transform.js b/third_party/WebKit/Source/devtools/scripts/migrate_test/transform.js
deleted file mode 100644
index 2cf305b..0000000
--- a/third_party/WebKit/Source/devtools/scripts/migrate_test/transform.js
+++ /dev/null
@@ -1,89 +0,0 @@
-// 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.
-
-'use strict';
-
-const fs = require('fs');
-const path = require('path');
-
-const childProcess = require('child_process');
-
-const utils = require('../utils');
-
-const MIGRATE_SCRIPT_PATH = path.resolve(__dirname, 'migrate_test.js');
-const TESTS_PATH = path.resolve(__dirname, 'tests.txt');
-const TEST_EXPECTATIONS_PATH = path.resolve(__dirname, '..', '..', '..', '..', 'LayoutTests', 'TestExpectations');
-const FLAG_EXPECTATIONS_PATH = path.resolve(__dirname, '..', '..', '..', '..', 'LayoutTests', 'FlagExpectations');
-
-function main() {
-  const tests = fs.readFileSync(TESTS_PATH, 'utf-8').split('\n').map(line => line.split(' '));
-  const oldToNewTests = new Map(tests);
-  const testCount = oldToNewTests.size;
-  const migratedTests = new Set();
-
-  for (const [_, testPath] of tests) {
-    if (!testPath)
-      continue;
-    const fullTestPath = path.resolve(__dirname, '..', '..', '..', '..', 'LayoutTests', testPath);
-    try {
-      childProcess.execSync(`node ${MIGRATE_SCRIPT_PATH} ${fullTestPath}`)
-    } catch (err) {
-      console.log(err.stdout.toString());
-      continue;
-    }
-
-    for (const [oldTest, newTest] of oldToNewTests) {
-      if (testPath === newTest)
-        oldToNewTests.delete(oldTest);
-    }
-    migratedTests.add(testPath);
-  }
-
-  console.log(`Successfully migrated: ${migratedTests.size} of ${testCount}`);
-
-  const updatedTests = Array.from(oldToNewTests.entries()).map(line => line.join(' ')).join('\n');
-  console.log(updatedTests);
-
-  // Update TestExpectations
-  const testExpectations = fs.readFileSync(TEST_EXPECTATIONS_PATH, 'utf-8');
-  const updatedTestExpecationLines = [];
-  let seenStartSentinel = false;
-  let seenEndSentinel = false;
-  for (const line of testExpectations.split('\n')) {
-    if (line === '# ====== DevTools test migration failures from here ======') {
-      seenStartSentinel = true;
-      updatedTestExpecationLines.push(line);
-      continue;
-    }
-    if (line === '# ====== DevTools test migration failures until here ======') {
-      seenEndSentinel = true;
-      updatedTestExpecationLines.push(line);
-      continue;
-    }
-    if (seenEndSentinel) {
-      updatedTestExpecationLines.push(line);
-      continue;
-    }
-    if (!seenStartSentinel) {
-      updatedTestExpecationLines.push(line);
-      continue;
-    }
-    let skipLine = false;
-    for (const test of migratedTests) {
-      if (line.indexOf(test) !== -1) {
-        skipLine = true;
-        break;
-      }
-    }
-    if (!skipLine)
-      updatedTestExpecationLines.push(line);
-  }
-
-  fs.writeFileSync(TEST_EXPECTATIONS_PATH, updatedTestExpecationLines.join('\n'));
-
-  // Update tests.txt
-  fs.writeFileSync(TESTS_PATH, updatedTests);
-}
-
-main();
diff --git a/third_party/WebKit/Source/modules/mediastream/UserMediaClientImpl.cpp b/third_party/WebKit/Source/modules/mediastream/UserMediaClientImpl.cpp
index 7d451af6..731d7bd 100644
--- a/third_party/WebKit/Source/modules/mediastream/UserMediaClientImpl.cpp
+++ b/third_party/WebKit/Source/modules/mediastream/UserMediaClientImpl.cpp
@@ -45,6 +45,8 @@
 void UserMediaClientImpl::RequestUserMedia(UserMediaRequest* request) {
   if (client_)
     client_->RequestUserMedia(request);
+  else
+    request->FailUASpecific("NotSupportedError", String(), String());
 }
 
 void UserMediaClientImpl::CancelUserMediaRequest(UserMediaRequest* request) {
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/directory_owners_extractor.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/directory_owners_extractor.py
index 0372145..a01d46ba 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/directory_owners_extractor.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/directory_owners_extractor.py
@@ -66,6 +66,10 @@
         assert not self.filesystem.isabs(start_directory)
         directory = self.finder.path_from_chromium_base(start_directory)
         external_root = self.finder.path_from_layout_tests('external')
+        # Changes to LayoutTests/TestExpectations itself should be skipped and
+        # not raise an assertion.
+        if directory == self.finder.layout_tests_dir():
+            return None, None
         assert directory.startswith(external_root)
         while directory != external_root:
             owners_file = self.filesystem.join(directory, 'OWNERS')
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/directory_owners_extractor_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/directory_owners_extractor_unittest.py
index 10f9214..91911a51 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/directory_owners_extractor_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/directory_owners_extractor_unittest.py
@@ -83,6 +83,8 @@
     def test_find_and_extract_owners_out_of_tree(self):
         with self.assertRaises(AssertionError):
             self.extractor.find_and_extract_owners('third_party/WebKit/LayoutTests/other')
+        self.assertEqual(self.extractor.find_and_extract_owners('third_party/WebKit/LayoutTests'),
+                         (None, None))
 
     def test_extract_owners(self):
         self.filesystem.files = {
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 3a1426f..5f698a1 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -63231,6 +63231,18 @@
   </summary>
 </histogram>
 
+<histogram name="RendererScheduler.ExpectedQueueingTimeWhenQueueingTime"
+    units="ms">
+  <owner>tdresser@chromium.org</owner>
+  <summary>
+    Expected queueing time for events which were queued on the main thread
+    longer or shorter than a threshold, given by the QueueingTimeThreshold
+    suffix.
+
+    Team: input-dev@chromium.org.
+  </summary>
+</histogram>
+
 <histogram name="RendererScheduler.ExpectedTaskQueueingDuration" units="ms">
   <owner>tdresser@chromium.org</owner>
   <summary>
@@ -63326,6 +63338,10 @@
 
 <histogram name="RendererScheduler.QueueingDurationWhenExpectedQueueingTime"
     units="ms">
+  <obsolete>
+    Deprecated as of 08/2017. Replaced with
+    RendererScheduler.ExpectedQueueingTimeWhenQueueingTime.
+  </obsolete>
   <owner>tdresser@chromium.org</owner>
   <summary>
     Time between sending an event to the renderer main thread and when the
@@ -92400,6 +92416,9 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="ExpectedQueueingDurationThreshold" separator="_">
+  <obsolete>
+    Deprecated as of 08/2017. Replaced with ExpectedQueueingDurationThreshold.
+  </obsolete>
   <suffix name="GreaterThan.10ms"
       label="Expected Queueing Time greater than 10ms."/>
   <suffix name="GreaterThan.150ms"
@@ -97827,6 +97846,19 @@
   <affected-histogram name="ShortcutsProvider.QueryIndexTime"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="QueueingTimeThreshold" separator="_">
+  <suffix name="GreaterThan.10ms" label="Queueing Time greater than 10ms."/>
+  <suffix name="GreaterThan.150ms" label="Queueing Time greater than 150ms."/>
+  <suffix name="GreaterThan.300ms" label="Queueing Time greater than 300ms."/>
+  <suffix name="GreaterThan.450ms" label="Queueing Time greater than 450ms."/>
+  <suffix name="LessThan.10ms" label="Queueing Time less than 10ms."/>
+  <suffix name="LessThan.150ms" label="Queueing Time less than 150ms."/>
+  <suffix name="LessThan.300ms" label="Queueing Time less than 300ms."/>
+  <suffix name="LessThan.450ms" label="Queueing Time less than 450ms."/>
+  <affected-histogram
+      name="RendererScheduler.ExpectedQueueingTimeWhenQueueingTime"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="QuicConnectionType" separator="">
   <owner>rch@chromium.org</owner>
   <suffix name="ForHTTP" label="Only insecure HTTP connections are counted.">
diff --git a/ui/message_center/BUILD.gn b/ui/message_center/BUILD.gn
index dded329..96cfede 100644
--- a/ui/message_center/BUILD.gn
+++ b/ui/message_center/BUILD.gn
@@ -28,7 +28,6 @@
   deps = [
     "//base",
     "//ui/base",
-    "//ui/strings",
     "//url",
   ]
 
@@ -48,6 +47,7 @@
       "//ui/gfx/geometry",
       "//ui/native_theme",
       "//ui/resources",
+      "//ui/strings",
     ]
 
     configs += [
diff --git a/ui/message_center/fake_message_center.cc b/ui/message_center/fake_message_center.cc
index b1e67185..1900b0e6 100644
--- a/ui/message_center/fake_message_center.cc
+++ b/ui/message_center/fake_message_center.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "ui/message_center/fake_message_center.h"
-#include "base/strings/string_util.h"
 #include "ui/message_center/notification_list.h"
 
 namespace message_center {
@@ -143,13 +142,6 @@
 
 void FakeMessageCenter::PausePopupTimers() {}
 
-const base::string16& FakeMessageCenter::GetProductOSName() const {
-  return base::EmptyString16();
-}
-
-void FakeMessageCenter::SetProductOSName(
-    const base::string16& product_os_name) {}
-
 void FakeMessageCenter::DisableTimersForTest() {}
 
 void FakeMessageCenter::EnableChangeQueueForTest(bool enabled) {}
diff --git a/ui/message_center/fake_message_center.h b/ui/message_center/fake_message_center.h
index c489f67..7a8766a 100644
--- a/ui/message_center/fake_message_center.h
+++ b/ui/message_center/fake_message_center.h
@@ -68,8 +68,6 @@
   bool IsMessageCenterVisible() const override;
   void RestartPopupTimers() override;
   void PausePopupTimers() override;
-  const base::string16& GetProductOSName() const override;
-  void SetProductOSName(const base::string16& product_os_name) override;
 
  protected:
   void DisableTimersForTest() override;
diff --git a/ui/message_center/message_center.h b/ui/message_center/message_center.h
index 2eebf0ff..55697cb 100644
--- a/ui/message_center/message_center.h
+++ b/ui/message_center/message_center.h
@@ -193,11 +193,6 @@
   // example, after the mouse leaves the popup.)
   virtual void RestartPopupTimers() = 0;
 
-  // "Chromium OS" or "Chrome OS" in the current locale.
-  // Return empty string if not on these platforms.
-  virtual const base::string16& GetProductOSName() const = 0;
-  virtual void SetProductOSName(const base::string16& product_os_name) = 0;
-
  protected:
   friend class ::DownloadNotification;
   friend class ::DownloadNotificationTestBase;
diff --git a/ui/message_center/message_center_impl.cc b/ui/message_center/message_center_impl.cc
index e2369ca..c4396746 100644
--- a/ui/message_center/message_center_impl.cc
+++ b/ui/message_center/message_center_impl.cc
@@ -15,7 +15,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/observer_list.h"
 #include "base/stl_util.h"
-#include "base/strings/string_util.h"
 #include "build/build_config.h"
 #include "ui/message_center/message_center_style.h"
 #include "ui/message_center/message_center_switches.h"
@@ -944,15 +943,6 @@
     popup_timers_controller_->PauseAll();
 }
 
-const base::string16& MessageCenterImpl::GetProductOSName() const {
-  return product_os_name_;
-}
-
-void MessageCenterImpl::SetProductOSName(
-    const base::string16& product_os_name) {
-  product_os_name_ = product_os_name;
-}
-
 void MessageCenterImpl::DisableTimersForTest() {
   popup_timers_controller_.reset();
 }
diff --git a/ui/message_center/message_center_impl.h b/ui/message_center/message_center_impl.h
index ea4348a3..3dd9c7f 100644
--- a/ui/message_center/message_center_impl.h
+++ b/ui/message_center/message_center_impl.h
@@ -84,8 +84,6 @@
   void EnterQuietModeWithExpire(const base::TimeDelta& expires_in) override;
   void RestartPopupTimers() override;
   void PausePopupTimers() override;
-  const base::string16& GetProductOSName() const override;
-  void SetProductOSName(const base::string16& product_os_name) override;
   void ForceNotificationFlush(const std::string& id) override;
 
   // NotificationBlocker::Observer overrides:
@@ -139,8 +137,6 @@
   // center is visible.
   std::unique_ptr<internal::ChangeQueue> notification_queue_;
 
-  base::string16 product_os_name_;
-
   DISALLOW_COPY_AND_ASSIGN(MessageCenterImpl);
 };
 
diff --git a/ui/message_center/message_center_style.h b/ui/message_center/message_center_style.h
index 15102b9..be5acf24 100644
--- a/ui/message_center/message_center_style.h
+++ b/ui/message_center/message_center_style.h
@@ -9,7 +9,6 @@
 
 #include "build/build_config.h"
 #include "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/color_palette.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/message_center/message_center_export.h"
 
@@ -101,15 +100,6 @@
 const SkColor kControlButtonBackgroundColor =
     SkColorSetA(SK_ColorWHITE, 0.9 * 0xff);
 
-// Accent colors of system notifications.
-const SkColor kSystemNotificationColorNormal = SkColorSetRGB(0x33, 0x67, 0xd6);
-const SkColor kSystemNotificationColorWarning = SkColorSetRGB(0xea, 0x61, 0x0);
-const SkColor kSystemNotificationColorCriticalWarning =
-    SkColorSetRGB(0xc5, 0x39, 0x29);
-
-// Default accent color of notifications that are not generated by system.
-const SkColor kNotificationDefaultAccentColor = gfx::kChromeIconGrey;
-
 // Limits.
 
 // Given the size of an image, returns the size of the properly scaled-up image
diff --git a/ui/message_center/notification.cc b/ui/message_center/notification.cc
index 1d5a222..c7a7389 100644
--- a/ui/message_center/notification.cc
+++ b/ui/message_center/notification.cc
@@ -5,15 +5,8 @@
 #include "ui/message_center/notification.h"
 
 #include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/gfx/paint_vector_icon.h"
-#include "ui/gfx/vector_icon_types.h"
-#include "ui/message_center/message_center.h"
-#include "ui/message_center/message_center_style.h"
 #include "ui/message_center/notification_delegate.h"
 #include "ui/message_center/notification_types.h"
-#include "ui/strings/grit/ui_strings.h"
 
 namespace message_center {
 
@@ -61,9 +54,7 @@
       vibration_pattern(other.vibration_pattern),
       renotify(other.renotify),
       silent(other.silent),
-      accessible_name(other.accessible_name),
-      accent_color(other.accent_color) {
-}
+      accessible_name(other.accessible_name) {}
 
 RichNotificationData::~RichNotificationData() = default;
 
@@ -179,58 +170,14 @@
     const gfx::Image& icon,
     const std::string& system_component_id,
     const base::Closure& click_callback) {
-  std::unique_ptr<Notification> notification = CreateSystemNotification(
+  std::unique_ptr<Notification> notification(new Notification(
       NOTIFICATION_TYPE_SIMPLE, notification_id, title, message, icon,
       base::string16() /* display_source */, GURL(),
       NotifierId(NotifierId::SYSTEM_COMPONENT, system_component_id),
       RichNotificationData(),
-      new HandleNotificationClickedDelegate(click_callback), gfx::kNoneIcon,
-      SystemNotificationWarningLevel::CRITICAL_WARNING);
+      new HandleNotificationClickedDelegate(click_callback)));
   notification->SetSystemPriority();
   return notification;
 }
 
-// static
-std::unique_ptr<Notification> Notification::CreateSystemNotification(
-    NotificationType type,
-    const std::string& id,
-    const base::string16& title,
-    const base::string16& message,
-    const gfx::Image& icon,
-    const base::string16& display_source,
-    const GURL& origin_url,
-    const NotifierId& notifier_id,
-    const RichNotificationData& optional_fields,
-    scoped_refptr<NotificationDelegate> delegate,
-    const gfx::VectorIcon& small_image,
-    SystemNotificationWarningLevel color_type) {
-  SkColor color = message_center::kSystemNotificationColorNormal;
-  switch (color_type) {
-    case SystemNotificationWarningLevel::NORMAL:
-      color = message_center::kSystemNotificationColorNormal;
-      break;
-    case SystemNotificationWarningLevel::WARNING:
-      color = message_center::kSystemNotificationColorWarning;
-      break;
-    case SystemNotificationWarningLevel::CRITICAL_WARNING:
-      color = message_center::kSystemNotificationColorCriticalWarning;
-      break;
-  }
-  base::string16 display_source_or_default = display_source;
-  if (display_source_or_default.empty()) {
-    display_source_or_default = l10n_util::GetStringFUTF16(
-        IDS_MESSAGE_CENTER_NOTIFICATION_CHROMEOS_SYSTEM,
-        MessageCenter::Get()->GetProductOSName());
-  }
-  std::unique_ptr<Notification> notification = base::MakeUnique<Notification>(
-      type, id, title, message, icon, display_source_or_default, origin_url,
-      notifier_id, optional_fields, delegate);
-  notification->set_accent_color(color);
-  notification->set_small_image(
-      small_image.is_empty()
-          ? gfx::Image()
-          : gfx::Image(gfx::CreateVectorIcon(small_image, color)));
-  return notification;
-}
-
 }  // namespace message_center
diff --git a/ui/message_center/notification.h b/ui/message_center/notification.h
index 8101cad..d08a48a8 100644
--- a/ui/message_center/notification.h
+++ b/ui/message_center/notification.h
@@ -15,19 +15,13 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"  // nogncheck
-#include "ui/gfx/color_palette.h"
 #include "ui/gfx/image/image.h"
-#include "ui/gfx/skia_util.h"
 #include "ui/message_center/message_center_export.h"
 #include "ui/message_center/notification_delegate.h"
 #include "ui/message_center/notification_types.h"
 #include "ui/message_center/notifier_settings.h"
 #include "url/gurl.h"
 
-namespace gfx {
-struct VectorIcon;
-}  // namespace gfx
-
 namespace message_center {
 
 namespace mojom {
@@ -51,8 +45,6 @@
   TEXT
 };
 
-enum class SystemNotificationWarningLevel { NORMAL, WARNING, CRITICAL_WARNING };
-
 // Represents a button to be shown as part of a notification.
 struct MESSAGE_CENTER_EXPORT ButtonInfo {
   explicit ButtonInfo(const base::string16& title);
@@ -149,12 +141,6 @@
 
   // An accessible description of the notification's contents.
   base::string16 accessible_name;
-
-  // Unified theme color used in new style notification.
-  // Usually, it should not be set directly.
-  // For system notification, CreateSystemNotification with
-  // SystemNotificationWarningLevel should be used.
-  SkColor accent_color = SK_ColorTRANSPARENT;
 };
 
 class MESSAGE_CENTER_EXPORT Notification {
@@ -359,11 +345,6 @@
     return optional_fields_.accessible_name;
   }
 
-  SkColor accent_color() const { return optional_fields_.accent_color; }
-  void set_accent_color(SkColor accent_color) {
-    optional_fields_.accent_color = accent_color;
-  }
-
   NotificationDelegate* delegate() const { return delegate_.get(); }
 
   const RichNotificationData& rich_notification_data() const {
@@ -383,13 +364,6 @@
 
   // Helper method to create a simple system notification. |click_callback|
   // will be invoked when the notification is clicked.
-  //
-  // It should only be used for critical notification, as SetSystemPriority and
-  // CRITICAL_WARNING color are set inside, which means the notification would
-  // not go away without user interaction.
-  //
-  // TODO(tetsui): Add a function parameter |small_image| of gfx::VectorIcon, so
-  // display source of critical system notification is illustrated by icon.
   static std::unique_ptr<Notification> CreateSystemNotification(
       const std::string& notification_id,
       const base::string16& title,
@@ -398,25 +372,6 @@
       const std::string& system_component_id,
       const base::Closure& click_callback);
 
-  // Factory method to create all kinds of notifications generated by system,
-  // from normal priority ones to critical priority ones.
-  // |small_image| is a small icon show on the upper left header to illustrate
-  // |display_source| of the notification.
-  // One specified in the |optional_fields| is overridden.
-  static std::unique_ptr<Notification> CreateSystemNotification(
-      NotificationType type,
-      const std::string& id,
-      const base::string16& title,
-      const base::string16& message,
-      const gfx::Image& icon,
-      const base::string16& display_source,
-      const GURL& origin_url,
-      const NotifierId& notifier_id,
-      const RichNotificationData& optional_fields,
-      scoped_refptr<NotificationDelegate> delegate,
-      const gfx::VectorIcon& small_image,
-      SystemNotificationWarningLevel color_type);
-
  protected:
   // The type of notification we'd like displayed.
   NotificationType type_;
diff --git a/ui/message_center/views/notification_header_view.cc b/ui/message_center/views/notification_header_view.cc
index 5a26331..a43e6eaa 100644
--- a/ui/message_center/views/notification_header_view.cc
+++ b/ui/message_center/views/notification_header_view.cc
@@ -68,6 +68,8 @@
 };
 
 ExpandButton::ExpandButton() {
+  SetImage(gfx::CreateVectorIcon(kNotificationExpandMoreIcon, kExpandIconSize,
+                                 gfx::kChromeIconGrey));
   focus_painter_ = views::Painter::CreateSolidFocusPainter(
       kFocusBorderColor, gfx::Insets(1, 2, 2, 2));
   SetFocusBehavior(FocusBehavior::ALWAYS);
@@ -157,7 +159,6 @@
   app_name_view_ = new views::Label(base::string16());
   app_name_view_->SetFontList(font_list);
   app_name_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  app_name_view_->SetEnabledColor(accent_color_);
   app_info_container->AddChildView(app_name_view_);
 
   // Summary text divider
@@ -192,7 +193,6 @@
 
   // Expand button view
   expand_button_ = new ExpandButton();
-  SetExpanded(is_expanded_);
   app_info_container->AddChildView(expand_button_);
 
   // Spacer between left-aligned views and right-aligned views
@@ -224,10 +224,6 @@
   app_icon_view_->SetImage(img);
 }
 
-void NotificationHeaderView::ClearAppIcon() {
-  app_icon_view_->SetImage(gfx::CreateVectorIcon(kProductIcon, accent_color_));
-}
-
 void NotificationHeaderView::SetAppName(const base::string16& name) {
   app_name_view_->SetText(name);
 }
@@ -281,10 +277,10 @@
 }
 
 void NotificationHeaderView::SetExpanded(bool expanded) {
-  is_expanded_ = expanded;
-  expand_button_->SetImage(gfx::CreateVectorIcon(
-      expanded ? kNotificationExpandLessIcon : kNotificationExpandMoreIcon,
-      kExpandIconSize, accent_color_));
+  expand_button_->SetImage(
+      gfx::CreateVectorIcon(
+          expanded ? kNotificationExpandLessIcon : kNotificationExpandMoreIcon,
+          kExpandIconSize, gfx::kChromeIconGrey));
 }
 
 void NotificationHeaderView::SetSettingsButtonEnabled(bool enabled) {
@@ -308,12 +304,6 @@
   }
 }
 
-void NotificationHeaderView::SetAccentColor(SkColor color) {
-  accent_color_ = color;
-  app_name_view_->SetEnabledColor(accent_color_);
-  SetExpanded(is_expanded_);
-}
-
 bool NotificationHeaderView::IsExpandButtonEnabled() {
   return expand_button_->visible();
 }
diff --git a/ui/message_center/views/notification_header_view.h b/ui/message_center/views/notification_header_view.h
index d196eb5a..2471c67a 100644
--- a/ui/message_center/views/notification_header_view.h
+++ b/ui/message_center/views/notification_header_view.h
@@ -6,7 +6,6 @@
 #define UI_MESSAGE_CENTER_VIEWS_NOTIFICATION_HEADER_VIEW_H_
 
 #include "base/macros.h"
-#include "ui/message_center/message_center_style.h"
 #include "ui/message_center/views/padded_button.h"
 #include "ui/views/controls/button/custom_button.h"
 
@@ -31,10 +30,6 @@
   void SetSettingsButtonEnabled(bool enabled);
   void SetCloseButtonEnabled(bool enabled);
   void SetControlButtonsVisible(bool visible);
-  // Set the unified theme color used among the app icon, app name, and expand
-  // button.
-  void SetAccentColor(SkColor color);
-  void ClearAppIcon();
   void ClearProgress();
   void ClearOverflowIndicator();
   void ClearTimestamp();
@@ -58,8 +53,6 @@
   // Update visibility for both |summary_text_view_| and |timestamp_view_|.
   void UpdateSummaryTextVisibility();
 
-  SkColor accent_color_ = message_center::kNotificationDefaultAccentColor;
-
   views::Label* app_name_view_ = nullptr;
   views::Label* summary_text_divider_ = nullptr;
   views::Label* summary_text_view_ = nullptr;
@@ -76,7 +69,6 @@
   bool has_progress_ = false;
   bool has_overflow_indicator_ = false;
   bool has_timestamp_ = false;
-  bool is_expanded_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(NotificationHeaderView);
 };
diff --git a/ui/message_center/views/notification_view_md.cc b/ui/message_center/views/notification_view_md.cc
index 37a6d2e..3251a9ba1 100644
--- a/ui/message_center/views/notification_view_md.cc
+++ b/ui/message_center/views/notification_view_md.cc
@@ -12,6 +12,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/gfx/text_elider.h"
@@ -55,6 +56,10 @@
 constexpr gfx::Size kActionButtonMinSize(88, 32);
 constexpr gfx::Size kIconViewSize(30, 30);
 
+// Foreground of small icon image.
+constexpr SkColor kSmallImageBackgroundColor = SK_ColorWHITE;
+// Background of small icon image.
+const SkColor kSmallImageColor = SkColorSetRGB(0x43, 0x43, 0x43);
 // Background of inline actions area.
 const SkColor kActionsRowBackgroundColor = SkColorSetRGB(0xee, 0xee, 0xee);
 // Base ink drop color of action buttons.
@@ -78,6 +83,37 @@
     message_center::kNotificationWidth - kIconViewSize.width() -
     kContentRowPadding.left() - kContentRowPadding.right();
 
+const gfx::ImageSkia CreateSolidColorImage(int width,
+                                           int height,
+                                           SkColor color) {
+  SkBitmap bitmap;
+  bitmap.allocN32Pixels(width, height);
+  bitmap.eraseColor(color);
+  return gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
+}
+
+// Take the alpha channel of icon, mask it with the foreground,
+// then add the masked foreground on top of the background
+const gfx::ImageSkia GetMaskedIcon(const gfx::ImageSkia& icon) {
+  int width = icon.width();
+  int height = icon.height();
+
+  // Background color grey
+  const gfx::ImageSkia background = CreateSolidColorImage(
+      width, height, message_center::kSmallImageBackgroundColor);
+  // Foreground color white
+  const gfx::ImageSkia foreground =
+      CreateSolidColorImage(width, height, message_center::kSmallImageColor);
+  const gfx::ImageSkia masked_small_image =
+      gfx::ImageSkiaOperations::CreateMaskedImage(foreground, icon);
+  return gfx::ImageSkiaOperations::CreateSuperimposedImage(background,
+                                                           masked_small_image);
+}
+
+const gfx::ImageSkia GetProductIcon() {
+  return gfx::CreateVectorIcon(kProductIcon, kSmallImageColor);
+}
+
 // ItemView ////////////////////////////////////////////////////////////////////
 
 // ItemViews are responsible for drawing each list notification item's title and
@@ -468,10 +504,6 @@
 void NotificationViewMD::CreateOrUpdateContextTitleView(
     const Notification& notification) {
   header_row_->SetAppName(notification.display_source());
-  header_row_->SetAccentColor(
-      notification.accent_color() == SK_ColorTRANSPARENT
-          ? message_center::kNotificationDefaultAccentColor
-          : notification.accent_color());
   header_row_->SetTimestamp(notification.timestamp());
 }
 
@@ -655,10 +687,11 @@
 
 void NotificationViewMD::CreateOrUpdateSmallIconView(
     const Notification& notification) {
-  if (notification.small_image().IsEmpty())
-    header_row_->ClearAppIcon();
-  else
-    header_row_->SetAppIcon(notification.small_image().AsImageSkia());
+  gfx::ImageSkia icon =
+      notification.small_image().IsEmpty()
+          ? GetProductIcon()
+          : GetMaskedIcon(notification.small_image().AsImageSkia());
+  header_row_->SetAppIcon(icon);
 }
 
 void NotificationViewMD::CreateOrUpdateImageView(
@@ -727,12 +760,6 @@
       action_buttons_[i]->SchedulePaint();
       action_buttons_[i]->Layout();
     }
-
-    // Change action button color to the accent color.
-    action_buttons_[i]->SetEnabledTextColors(notification.accent_color() ==
-                                                     SK_ColorTRANSPARENT
-                                                 ? kActionButtonTextColor
-                                                 : notification.accent_color());
   }
 
   // Inherit mouse hover state when action button views reset.