diff --git a/DEPS b/DEPS
index 948df7b..ed050f4 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': 'f4e498601755073fb43cdbe8db02f236ce1249a3',
+  'skia_revision': 'd0e0a8ff416abf9b4736ddac8f6faeb11023ab80',
   # 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': '714590de1ae834be0e2982742a6725f353877819',
+  'v8_revision': '493d8b47ade62b5b331c13a102282102bce14489',
   # 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.
diff --git a/chrome/browser/banners/app_banner_manager.cc b/chrome/browser/banners/app_banner_manager.cc
index 79a453d..d13f2f9d 100644
--- a/chrome/browser/banners/app_banner_manager.cc
+++ b/chrome/browser/banners/app_banner_manager.cc
@@ -555,7 +555,7 @@
   UpdateState(State::COMPLETE);
 }
 
-void AppBannerManager::DisplayAppBanner() {
+void AppBannerManager::DisplayAppBanner(bool user_gesture) {
   if (is_pending_event()) {
     // Simulate a non-canceled OnBannerPromptReply to show the delayed banner.
     OnBannerPromptReply(blink::mojom::AppBannerPromptReply::NONE, referrer_);
diff --git a/chrome/browser/banners/app_banner_manager.h b/chrome/browser/banners/app_banner_manager.h
index 4c13d14..d14251b7d 100644
--- a/chrome/browser/banners/app_banner_manager.h
+++ b/chrome/browser/banners/app_banner_manager.h
@@ -268,7 +268,7 @@
   // blink::mojom::AppBannerService overrides.
   // Called when Blink has prevented a banner from being shown, and is now
   // requesting that it be shown later.
-  void DisplayAppBanner() override;
+  void DisplayAppBanner(bool user_gesture) override;
 
   // Fetches the data required to display a banner for the current page.
   InstallableManager* manager_;
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_async_file_util_unittest.cc b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_async_file_util_unittest.cc
index 4e5834d5..7342524 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_async_file_util_unittest.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_async_file_util_unittest.cc
@@ -31,6 +31,7 @@
 
 constexpr char kArcUrl[] = "content://org.chromium.foo/bar";
 constexpr char kData[] = "abcdef";
+constexpr char kMimeType[] = "application/octet-stream";
 
 class ArcContentFileSystemAsyncFileUtilTest : public testing::Test {
  public:
@@ -38,7 +39,8 @@
   ~ArcContentFileSystemAsyncFileUtilTest() override = default;
 
   void SetUp() override {
-    fake_file_system_.AddFile(File(kArcUrl, kData, File::Seekable::NO));
+    fake_file_system_.AddFile(
+        File(kArcUrl, kData, kMimeType, File::Seekable::NO));
 
     arc_service_manager_ = base::MakeUnique<ArcServiceManager>(nullptr);
     arc_service_manager_->AddService(
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_file_stream_reader_unittest.cc b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_file_stream_reader_unittest.cc
index 14d99657..52d1c70 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_file_stream_reader_unittest.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_file_stream_reader_unittest.cc
@@ -36,6 +36,8 @@
 
 constexpr char kData[] = "abcdefghijklmnopqrstuvwxyz";
 
+constexpr char kMimeType[] = "application/octet-stream";
+
 // Reads data from the reader to fill the buffer.
 bool ReadData(ArcContentFileSystemFileStreamReader* reader,
               net::IOBufferWithSize* buffer) {
@@ -62,8 +64,10 @@
   ~ArcContentFileSystemFileStreamReaderTest() override = default;
 
   void SetUp() override {
-    fake_file_system_.AddFile(File(kArcUrlFile, kData, File::Seekable::YES));
-    fake_file_system_.AddFile(File(kArcUrlPipe, kData, File::Seekable::NO));
+    fake_file_system_.AddFile(
+        File(kArcUrlFile, kData, kMimeType, File::Seekable::YES));
+    fake_file_system_.AddFile(
+        File(kArcUrlPipe, kData, kMimeType, File::Seekable::NO));
 
     arc_service_manager_ = base::MakeUnique<ArcServiceManager>(nullptr);
     arc_service_manager_->AddService(
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util.cc b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util.cc
index 4603258..c0a9a8c 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util.cc
@@ -47,9 +47,13 @@
 }
 
 GURL FileSystemUrlToArcUrl(const storage::FileSystemURL& url) {
+  return PathToArcUrl(url.path());
+}
+
+GURL PathToArcUrl(const base::FilePath& path) {
   base::FilePath path_after_mount_point;
   if (!base::FilePath(kContentFileSystemMountPointPath)
-           .AppendRelativePath(url.path(), &path_after_mount_point)) {
+           .AppendRelativePath(path, &path_after_mount_point)) {
     return GURL();
   }
   return UnescapeArcUrl(path_after_mount_point.AsUTF8Unsafe());
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util.h b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util.h
index 1a49c78..a6daddc 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util.h
+++ b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util.h
@@ -40,6 +40,12 @@
 // Converts a FileSystemURL to a URL which can be used within the ARC container.
 GURL FileSystemUrlToArcUrl(const storage::FileSystemURL& url);
 
+// Converts a path which was returned by FileSystemURL::path() (not to be
+// confused with virtual_path()) to a URL which can be used within the ARC
+// container. If the given path is not under the ARC content file system mount
+// point, returns an empty GURL.
+GURL PathToArcUrl(const base::FilePath& path);
+
 }  // namespace arc
 
 #endif  // CHROME_BROWSER_CHROMEOS_ARC_FILEAPI_ARC_CONTENT_FILE_SYSTEM_URL_UTIL_H_
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util_unittest.cc b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util_unittest.cc
index e77f3cf..e58c0f5 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util_unittest.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util_unittest.cc
@@ -78,4 +78,13 @@
   EXPECT_EQ(arc_url, FileSystemUrlToArcUrl(file_system_url));
 }
 
+TEST(ArcContentFileSystemUrlUtilTest, PathToArcUrl) {
+  GURL arc_url("content://org.chromium.foo/bar/baz");
+
+  base::FilePath path =
+      base::FilePath(kContentFileSystemMountPointPath)
+          .Append(base::FilePath::FromUTF8Unsafe(EscapeArcUrl(arc_url)));
+  EXPECT_EQ(arc_url, PathToArcUrl(path));
+}
+
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.cc b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.cc
index 77bcfeb..b1de0ee 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.cc
@@ -104,6 +104,26 @@
   file_system_instance->GetFileSize(url.spec(), callback);
 }
 
+void ArcFileSystemOperationRunner::GetMimeType(
+    const GURL& url,
+    const GetMimeTypeCallback& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (should_defer_) {
+    deferred_operations_.emplace_back(
+        base::Bind(&ArcFileSystemOperationRunner::GetMimeType,
+                   weak_ptr_factory_.GetWeakPtr(), url, callback));
+    return;
+  }
+  auto* file_system_instance = ARC_GET_INSTANCE_FOR_METHOD(
+      arc_bridge_service()->file_system(), GetMimeType);
+  if (!file_system_instance) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(callback, base::nullopt));
+    return;
+  }
+  file_system_instance->GetMimeType(url.spec(), callback);
+}
+
 void ArcFileSystemOperationRunner::OpenFileToRead(
     const GURL& url,
     const OpenFileToReadCallback& callback) {
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.h b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.h
index e378270b..6f40f97 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.h
+++ b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.h
@@ -55,6 +55,7 @@
       public InstanceHolder<mojom::FileSystemInstance>::Observer {
  public:
   using GetFileSizeCallback = mojom::FileSystemInstance::GetFileSizeCallback;
+  using GetMimeTypeCallback = mojom::FileSystemInstance::GetMimeTypeCallback;
   using OpenFileToReadCallback =
       mojom::FileSystemInstance::OpenFileToReadCallback;
   using GetDocumentCallback = mojom::FileSystemInstance::GetDocumentCallback;
@@ -99,6 +100,7 @@
 
   // Runs file system operations. See file_system.mojom for documentation.
   void GetFileSize(const GURL& url, const GetFileSizeCallback& callback);
+  void GetMimeType(const GURL& url, const GetMimeTypeCallback& callback);
   void OpenFileToRead(const GURL& url, const OpenFileToReadCallback& callback);
   void GetDocument(const std::string& authority,
                    const std::string& document_id,
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_unittest.cc b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_unittest.cc
index 0d04ab3..d334a08e 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_unittest.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_unittest.cc
@@ -79,6 +79,13 @@
     runner_->GetFileSize(
         GURL(kUrl),
         base::Bind([](int* counter, int64_t size) { ++*counter; }, counter));
+    runner_->GetMimeType(
+        GURL(kUrl),
+        base::Bind(
+            [](int* counter, const base::Optional<std::string>& mime_type) {
+              ++*counter;
+            },
+            counter));
     runner_->OpenFileToRead(
         GURL(kUrl),
         base::Bind([](int* counter, mojo::ScopedHandle handle) { ++*counter; },
@@ -105,7 +112,7 @@
   CallSetShouldDefer(false);
   CallAllFunctions(&counter);
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(6, counter);
+  EXPECT_EQ(7, counter);
 }
 
 TEST_F(ArcFileSystemOperationRunnerTest, DeferAndRun) {
@@ -117,7 +124,7 @@
 
   CallSetShouldDefer(false);
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(6, counter);
+  EXPECT_EQ(7, counter);
 }
 
 TEST_F(ArcFileSystemOperationRunnerTest, DeferAndDiscard) {
@@ -140,7 +147,7 @@
   CallSetShouldDefer(false);
   CallAllFunctions(&counter);
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(6, counter);
+  EXPECT_EQ(7, counter);
 }
 
 }  // namespace arc
diff --git a/chrome/browser/chromeos/file_manager/filesystem_api_util.cc b/chrome/browser/chromeos/file_manager/filesystem_api_util.cc
index ccf4a84d..8a435cb 100644
--- a/chrome/browser/chromeos/file_manager/filesystem_api_util.cc
+++ b/chrome/browser/chromeos/file_manager/filesystem_api_util.cc
@@ -9,6 +9,9 @@
 #include "base/callback.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
+#include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util.h"
+#include "chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.h"
 #include "chrome/browser/chromeos/drive/file_system_util.h"
 #include "chrome/browser/chromeos/file_manager/app_id.h"
 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
@@ -16,6 +19,7 @@
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/profiles/profile.h"
+#include "components/arc/arc_service_manager.h"
 #include "components/drive/chromeos/file_system_interface.h"
 #include "components/drive/file_errors.h"
 #include "components/drive/file_system_core_util.h"
@@ -59,6 +63,19 @@
   callback.Run(true, *metadata->mime_type);
 }
 
+// Helper function used to implement GetNonNativeLocalPathMimeType. It passes
+// the returned mime type to the callback.
+void GetMimeTypeAfterGetMimeTypeForArcContentFileSystem(
+    const base::Callback<void(bool, const std::string&)>& callback,
+    const base::Optional<std::string>& mime_type) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (mime_type.has_value()) {
+    callback.Run(true, mime_type.value());
+  } else {
+    callback.Run(false, std::string());
+  }
+}
+
 // Helper function to converts a callback that takes boolean value to that takes
 // File::Error, by regarding FILE_OK as the only successful value.
 void BoolCallbackAsFileErrorCallback(
@@ -181,6 +198,23 @@
     return;
   }
 
+  if (arc::IsArcAllowedForProfile(profile) &&
+      base::FilePath(arc::kContentFileSystemMountPointPath).IsParent(path)) {
+    GURL arc_url = arc::PathToArcUrl(path);
+    auto* runner = arc::ArcServiceManager::GetGlobalService<
+        arc::ArcFileSystemOperationRunner>();
+    if (!runner) {
+      content::BrowserThread::PostTask(
+          content::BrowserThread::UI, FROM_HERE,
+          base::BindOnce(callback, false, std::string()));
+      return;
+    }
+    runner->GetMimeType(
+        arc_url, base::Bind(&GetMimeTypeAfterGetMimeTypeForArcContentFileSystem,
+                            callback));
+    return;
+  }
+
   // We don't have a way to obtain metadata other than drive and FSP. Returns an
   // error with empty MIME type, that leads fallback guessing mime type from
   // file extensions.
diff --git a/chrome/browser/permissions/mock_permission_request.cc b/chrome/browser/permissions/mock_permission_request.cc
index ae21796..f436de82 100644
--- a/chrome/browser/permissions/mock_permission_request.cc
+++ b/chrome/browser/permissions/mock_permission_request.cc
@@ -36,14 +36,14 @@
                              request_type,
                              gesture_type) {}
 
-MockPermissionRequest::MockPermissionRequest(
-    const std::string& text,
-    const GURL& url)
+MockPermissionRequest::MockPermissionRequest(const std::string& text,
+                                             PermissionRequestType request_type,
+                                             const GURL& url)
     : MockPermissionRequest(text,
                             "button",
                             "button",
                             url,
-                            PermissionRequestType::UNKNOWN,
+                            request_type,
                             PermissionRequestGestureType::UNKNOWN) {}
 
 MockPermissionRequest::MockPermissionRequest(
diff --git a/chrome/browser/permissions/mock_permission_request.h b/chrome/browser/permissions/mock_permission_request.h
index 8bcfe1b..1310290 100644
--- a/chrome/browser/permissions/mock_permission_request.h
+++ b/chrome/browser/permissions/mock_permission_request.h
@@ -16,7 +16,9 @@
   MockPermissionRequest(const std::string& text,
                         PermissionRequestType request_type,
                         PermissionRequestGestureType gesture_type);
-  MockPermissionRequest(const std::string& text, const GURL& url);
+  MockPermissionRequest(const std::string& text,
+                        PermissionRequestType request_type,
+                        const GURL& url);
   MockPermissionRequest(const std::string& text,
                         const std::string& accept_label,
                         const std::string& deny_label);
diff --git a/chrome/browser/permissions/permission_request_manager.cc b/chrome/browser/permissions/permission_request_manager.cc
index 61346ec..982e4ff 100644
--- a/chrome/browser/permissions/permission_request_manager.cc
+++ b/chrome/browser/permissions/permission_request_manager.cc
@@ -70,6 +70,27 @@
   return false;
 }
 
+// We only group together media requests. We don't display grouped requests for
+// any other permissions at present.
+bool ShouldGroupRequests(PermissionRequest* a, PermissionRequest* b) {
+  if (a->GetOrigin() != b->GetOrigin())
+    return false;
+
+  if (a->GetPermissionRequestType() ==
+      PermissionRequestType::PERMISSION_MEDIASTREAM_MIC) {
+    return b->GetPermissionRequestType() ==
+           PermissionRequestType::PERMISSION_MEDIASTREAM_CAMERA;
+  }
+
+  if (a->GetPermissionRequestType() ==
+      PermissionRequestType::PERMISSION_MEDIASTREAM_CAMERA) {
+    return b->GetPermissionRequestType() ==
+           PermissionRequestType::PERMISSION_MEDIASTREAM_MIC;
+  }
+
+  return false;
+}
+
 }  // namespace
 
 // PermissionRequestManager::Observer ------------------------------------------
@@ -107,8 +128,6 @@
     request->RequestFinished();
   for (PermissionRequest* request : queued_requests_)
     request->RequestFinished();
-  for (PermissionRequest* request : queued_frame_requests_)
-    request->RequestFinished();
   for (const auto& entry : duplicate_requests_)
     entry.second->RequestFinished();
 }
@@ -148,12 +167,11 @@
       base::RecordAction(
           base::UserMetricsAction("PermissionBubbleRequestQueued"));
     }
-    queued_requests_.push_back(request);
   } else {
     base::RecordAction(
         base::UserMetricsAction("PermissionBubbleIFrameRequestQueued"));
-    queued_frame_requests_.push_back(request);
   }
+  queued_requests_.push_back(request);
 
   if (!IsBubbleVisible())
     ScheduleShowBubble();
@@ -162,25 +180,17 @@
 void PermissionRequestManager::CancelRequest(PermissionRequest* request) {
   // First look in the queued requests, where we can simply finish the request
   // and go on.
-  std::vector<PermissionRequest*>::iterator requests_iter;
-  for (requests_iter = queued_requests_.begin();
-       requests_iter != queued_requests_.end();
-       requests_iter++) {
-    if (*requests_iter == request) {
-      RequestFinishedIncludingDuplicates(*requests_iter);
-      queued_requests_.erase(requests_iter);
-      return;
-    }
-  }
-  for (requests_iter = queued_frame_requests_.begin();
-       requests_iter != queued_frame_requests_.end(); requests_iter++) {
-    if (*requests_iter == request) {
-      RequestFinishedIncludingDuplicates(*requests_iter);
-      queued_frame_requests_.erase(requests_iter);
+  std::deque<PermissionRequest*>::iterator queued_requests_iter;
+  for (queued_requests_iter = queued_requests_.begin();
+       queued_requests_iter != queued_requests_.end(); queued_requests_iter++) {
+    if (*queued_requests_iter == request) {
+      RequestFinishedIncludingDuplicates(*queued_requests_iter);
+      queued_requests_.erase(queued_requests_iter);
       return;
     }
   }
 
+  std::vector<PermissionRequest*>::iterator requests_iter;
   std::vector<bool>::iterator accepts_iter = accept_states_.begin();
   for (requests_iter = requests_.begin(), accepts_iter = accept_states_.begin();
        requests_iter != requests_.end();
@@ -212,7 +222,7 @@
     return;
   }
 
-  // Since |request| wasn't found in queued_requests_, queued_frame_requests_ or
+  // Since |request| wasn't found in queued_requests_ or
   // requests_ it must have been marked as a duplicate. We can't search
   // duplicate_requests_ by value, so instead use GetExistingRequest to find the
   // key (request it was duped against), and iterate through duplicates of that.
@@ -399,13 +409,17 @@
     return;
   if (!main_frame_has_fully_loaded_)
     return;
-  if (queued_requests_.empty() && queued_frame_requests_.empty())
+  if (queued_requests_.empty())
     return;
 
-  if (queued_requests_.size())
-    requests_.swap(queued_requests_);
-  else
-    requests_.swap(queued_frame_requests_);
+  requests_.push_back(queued_requests_.front());
+  queued_requests_.pop_front();
+
+  if (!queued_requests_.empty() &&
+      ShouldGroupRequests(requests_.front(), queued_requests_.front())) {
+    requests_.push_back(queued_requests_.front());
+    queued_requests_.pop_front();
+  }
 
   // Sets the default value for each request to be 'accept'.
   accept_states_.resize(requests_.size(), true);
@@ -439,24 +453,18 @@
   }
   requests_.clear();
   accept_states_.clear();
-  if (queued_requests_.size() || queued_frame_requests_.size())
+  if (queued_requests_.size())
     DequeueRequestsAndShowBubble();
 }
 
 void PermissionRequestManager::CancelPendingQueues() {
-  std::vector<PermissionRequest*>::iterator requests_iter;
+  std::deque<PermissionRequest*>::iterator requests_iter;
   for (requests_iter = queued_requests_.begin();
        requests_iter != queued_requests_.end();
        requests_iter++) {
     RequestFinishedIncludingDuplicates(*requests_iter);
   }
-  for (requests_iter = queued_frame_requests_.begin();
-       requests_iter != queued_frame_requests_.end();
-       requests_iter++) {
-    RequestFinishedIncludingDuplicates(*requests_iter);
-  }
   queued_requests_.clear();
-  queued_frame_requests_.clear();
 }
 
 PermissionRequest* PermissionRequestManager::GetExistingRequest(
@@ -467,9 +475,6 @@
   for (PermissionRequest* existing_request : queued_requests_)
     if (IsMessageTextEqual(existing_request, request))
       return existing_request;
-  for (PermissionRequest* existing_request : queued_frame_requests_)
-    if (IsMessageTextEqual(existing_request, request))
-      return existing_request;
   return nullptr;
 }
 
@@ -488,7 +493,7 @@
 void PermissionRequestManager::PermissionDeniedIncludingDuplicates(
     PermissionRequest* request) {
   DCHECK_EQ(request, GetExistingRequest(request))
-      << "Only requests in [queued_[frame_]]requests_ can have duplicates";
+      << "Only requests in [queued_]requests_ can have duplicates";
   request->set_persist(persist_);
   request->PermissionDenied();
   auto range = duplicate_requests_.equal_range(request);
@@ -500,7 +505,7 @@
 void PermissionRequestManager::CancelledIncludingDuplicates(
     PermissionRequest* request) {
   DCHECK_EQ(request, GetExistingRequest(request))
-      << "Only requests in [queued_[frame_]]requests_ can have duplicates";
+      << "Only requests in [queued_]requests_ can have duplicates";
   request->Cancelled();
   auto range = duplicate_requests_.equal_range(request);
   for (auto it = range.first; it != range.second; ++it)
@@ -509,13 +514,11 @@
 void PermissionRequestManager::RequestFinishedIncludingDuplicates(
     PermissionRequest* request) {
   // We can't call GetExistingRequest here, because other entries in requests_,
-  // queued_requests_ or queued_frame_requests_ might already have been deleted.
+  // queued_requests_ might already have been deleted.
   DCHECK_EQ(1, std::count(requests_.begin(), requests_.end(), request) +
-               std::count(queued_requests_.begin(), queued_requests_.end(),
-                          request) +
-               std::count(queued_frame_requests_.begin(),
-                          queued_frame_requests_.end(), request))
-      << "Only requests in [queued_[frame_]]requests_ can have duplicates";
+                   std::count(queued_requests_.begin(), queued_requests_.end(),
+                              request))
+      << "Only requests in [queued_]requests_ can have duplicates";
   request->RequestFinished();
   // Beyond this point, |request| has probably been deleted.
   auto range = duplicate_requests_.equal_range(request);
diff --git a/chrome/browser/permissions/permission_request_manager.h b/chrome/browser/permissions/permission_request_manager.h
index ffc4af1..275f968 100644
--- a/chrome/browser/permissions/permission_request_manager.h
+++ b/chrome/browser/permissions/permission_request_manager.h
@@ -5,8 +5,8 @@
 #ifndef CHROME_BROWSER_PERMISSIONS_PERMISSION_REQUEST_MANAGER_H_
 #define CHROME_BROWSER_PERMISSIONS_PERMISSION_REQUEST_MANAGER_H_
 
+#include <deque>
 #include <unordered_map>
-#include <vector>
 
 #include "base/gtest_prod_util.h"
 #include "base/memory/weak_ptr.h"
@@ -188,8 +188,7 @@
   std::unique_ptr<PermissionPrompt> view_;
 
   std::vector<PermissionRequest*> requests_;
-  std::vector<PermissionRequest*> queued_requests_;
-  std::vector<PermissionRequest*> queued_frame_requests_;
+  std::deque<PermissionRequest*> queued_requests_;
   // Maps from the first request of a kind to subsequent requests that were
   // duped against it.
   std::unordered_multimap<PermissionRequest*, PermissionRequest*>
diff --git a/chrome/browser/permissions/permission_request_manager_browsertest.cc b/chrome/browser/permissions/permission_request_manager_browsertest.cc
index 4b2b62c..a6de20a 100644
--- a/chrome/browser/permissions/permission_request_manager_browsertest.cc
+++ b/chrome/browser/permissions/permission_request_manager_browsertest.cc
@@ -211,16 +211,6 @@
 
 void PermissionDialogTest::ShowDialog(const std::string& name) {
   constexpr const char* kMultipleName = "multiple";
-  // Permissions to request for a "multiple" request. Only types handled in
-  // PermissionRequestImpl::GetMessageTextFragment() are valid.
-  constexpr ContentSettingsType kMultipleRequests[] = {
-      CONTENT_SETTINGS_TYPE_GEOLOCATION, CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
-      CONTENT_SETTINGS_TYPE_MIDI_SYSEX,
-  };
-  constexpr ContentSettingsType kMultipleRequestsWithMedia[] = {
-      CONTENT_SETTINGS_TYPE_GEOLOCATION, CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
-      CONTENT_SETTINGS_TYPE_MIDI_SYSEX, CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
-      CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA};
   constexpr struct {
     const char* name;
     ContentSettingsType type;
@@ -274,15 +264,13 @@
       manager->AddRequest(MakePermissionRequest(it->type));
       break;
     case CONTENT_SETTINGS_TYPE_DEFAULT:
+      // Permissions to request for a "multiple" request. Only mic/camera
+      // requests are grouped together.
       EXPECT_EQ(kMultipleName, name);
-      if (base::FeatureList::IsEnabled(
-              features::kUsePermissionManagerForMediaRequests)) {
-        for (auto request : kMultipleRequestsWithMedia)
-          manager->AddRequest(MakePermissionRequest(request));
-      } else {
-        for (auto request : kMultipleRequests)
-          manager->AddRequest(MakePermissionRequest(request));
-      }
+      manager->AddRequest(
+          MakePermissionRequest(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC));
+      manager->AddRequest(
+          MakePermissionRequest(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA));
 
       break;
     default:
@@ -348,7 +336,7 @@
   bubble_factory()->WaitForPermissionBubble();
 
   EXPECT_EQ(2, bubble_factory()->show_count());
-  EXPECT_EQ(4, bubble_factory()->TotalRequestCount());
+  EXPECT_EQ(2, bubble_factory()->TotalRequestCount());
 }
 
 // Navigating twice to the same URL with a hash should be navigation within the
@@ -377,7 +365,7 @@
   bubble_factory()->WaitForPermissionBubble();
 
   EXPECT_EQ(1, bubble_factory()->show_count());
-  EXPECT_EQ(2, bubble_factory()->TotalRequestCount());
+  EXPECT_EQ(1, bubble_factory()->TotalRequestCount());
 }
 
 // Bubble requests should be shown after in-page navigation.
@@ -522,13 +510,6 @@
 // Shows a permissions bubble with multiple requests.
 IN_PROC_BROWSER_TEST_F(PermissionDialogTest, InvokeDialog_multiple) {
   RunDialog();
-
-  {
-    base::test::ScopedFeatureList scoped_feature_list;
-    scoped_feature_list.InitAndEnableFeature(
-        features::kUsePermissionManagerForMediaRequests);
-    RunDialog();
-  }
 }
 
 // CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER is ChromeOS only.
diff --git a/chrome/browser/permissions/permission_request_manager_unittest.cc b/chrome/browser/permissions/permission_request_manager_unittest.cc
index a9d6cc1..54fd984 100644
--- a/chrome/browser/permissions/permission_request_manager_unittest.cc
+++ b/chrome/browser/permissions/permission_request_manager_unittest.cc
@@ -28,10 +28,22 @@
         request2_("test2",
                   PermissionRequestType::DOWNLOAD,
                   PermissionRequestGestureType::NO_GESTURE),
+        request_mic_("mic",
+                     PermissionRequestType::PERMISSION_MEDIASTREAM_MIC,
+                     PermissionRequestGestureType::NO_GESTURE),
+        request_camera_("cam",
+                        PermissionRequestType::PERMISSION_MEDIASTREAM_CAMERA,
+                        PermissionRequestGestureType::NO_GESTURE),
         iframe_request_same_domain_("iframe",
+                                    PermissionRequestType::UNKNOWN,
                                     GURL("http://www.google.com/some/url")),
-        iframe_request_other_domain_("iframe", GURL("http://www.youtube.com")) {
-  }
+        iframe_request_other_domain_("iframe",
+                                     PermissionRequestType::UNKNOWN,
+                                     GURL("http://www.youtube.com")),
+        iframe_request_mic_other_domain_(
+            "iframe",
+            PermissionRequestType::PERMISSION_MEDIASTREAM_MIC,
+            GURL("http://www.youtube.com")) {}
   ~PermissionRequestManagerTest() override {}
 
   void SetUp() override {
@@ -71,7 +83,7 @@
     base::RunLoop().RunUntilIdle();
   }
 
-  void WaitForCoalescing() {
+  void WaitForBubbleToBeShown() {
     manager_->DocumentOnLoadCompletedInMainFrame();
     base::RunLoop().RunUntilIdle();
   }
@@ -88,8 +100,11 @@
  protected:
   MockPermissionRequest request1_;
   MockPermissionRequest request2_;
+  MockPermissionRequest request_mic_;
+  MockPermissionRequest request_camera_;
   MockPermissionRequest iframe_request_same_domain_;
   MockPermissionRequest iframe_request_other_domain_;
+  MockPermissionRequest iframe_request_mic_other_domain_;
   std::unique_ptr<PermissionRequestManager> manager_;
   std::unique_ptr<MockPermissionPromptFactory> prompt_factory_;
 };
@@ -97,7 +112,7 @@
 TEST_F(PermissionRequestManagerTest, SingleRequest) {
   manager_->AddRequest(&request1_);
   manager_->DisplayPendingRequests();
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
 
   EXPECT_TRUE(prompt_factory_->is_visible());
   ASSERT_EQ(prompt_factory_->request_count(), 1);
@@ -110,7 +125,7 @@
 TEST_F(PermissionRequestManagerTest, SingleRequestViewFirst) {
   manager_->DisplayPendingRequests();
   manager_->AddRequest(&request1_);
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
 
   EXPECT_TRUE(prompt_factory_->is_visible());
   ASSERT_EQ(prompt_factory_->request_count(), 1);
@@ -120,27 +135,55 @@
   EXPECT_TRUE(request1_.granted());
 }
 
-TEST_F(PermissionRequestManagerTest, TwoRequests) {
+// Most requests should never be grouped.
+TEST_F(PermissionRequestManagerTest, TwoRequestsUngrouped) {
   manager_->AddRequest(&request1_);
   manager_->AddRequest(&request2_);
   manager_->DisplayPendingRequests();
-  WaitForCoalescing();
+
+  WaitForBubbleToBeShown();
+  EXPECT_TRUE(prompt_factory_->is_visible());
+  ASSERT_EQ(prompt_factory_->request_count(), 1);
+  Accept();
+  EXPECT_TRUE(request1_.granted());
+
+  WaitForBubbleToBeShown();
+  EXPECT_TRUE(prompt_factory_->is_visible());
+  ASSERT_EQ(prompt_factory_->request_count(), 1);
+  Accept();
+  EXPECT_TRUE(request2_.granted());
+}
+
+// Only mic/camera requests from the same origin should be grouped.
+TEST_F(PermissionRequestManagerTest, MicCameraGrouped) {
+  manager_->AddRequest(&request_mic_);
+  manager_->AddRequest(&request_camera_);
+  manager_->DisplayPendingRequests();
+  WaitForBubbleToBeShown();
 
   EXPECT_TRUE(prompt_factory_->is_visible());
   ASSERT_EQ(prompt_factory_->request_count(), 2);
 
   ToggleAccept(0, true);
-  ToggleAccept(1, false);
+  ToggleAccept(1, true);
   Accept();
-  EXPECT_TRUE(request1_.granted());
-  EXPECT_FALSE(request2_.granted());
+  EXPECT_TRUE(request_mic_.granted());
+  EXPECT_TRUE(request_camera_.granted());
+
+  // If the requests come from different origins, they should not be grouped.
+  manager_->AddRequest(&iframe_request_mic_other_domain_);
+  manager_->AddRequest(&request_camera_);
+  WaitForBubbleToBeShown();
+
+  EXPECT_TRUE(prompt_factory_->is_visible());
+  ASSERT_EQ(prompt_factory_->request_count(), 1);
 }
 
 TEST_F(PermissionRequestManagerTest, TwoRequestsTabSwitch) {
-  manager_->AddRequest(&request1_);
-  manager_->AddRequest(&request2_);
+  manager_->AddRequest(&request_mic_);
+  manager_->AddRequest(&request_camera_);
   manager_->DisplayPendingRequests();
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
 
   EXPECT_TRUE(prompt_factory_->is_visible());
   ASSERT_EQ(prompt_factory_->request_count(), 2);
@@ -152,43 +195,32 @@
   EXPECT_FALSE(prompt_factory_->is_visible());
 
   MockTabSwitchBack();
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   EXPECT_TRUE(prompt_factory_->is_visible());
   ASSERT_EQ(prompt_factory_->request_count(), 2);
 
   Accept();
-  EXPECT_TRUE(request1_.granted());
-  EXPECT_FALSE(request2_.granted());
+  EXPECT_TRUE(request_mic_.granted());
+  EXPECT_FALSE(request_camera_.granted());
 }
 
 TEST_F(PermissionRequestManagerTest, NoRequests) {
   manager_->DisplayPendingRequests();
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   EXPECT_FALSE(prompt_factory_->is_visible());
 }
 
 TEST_F(PermissionRequestManagerTest, NoView) {
   manager_->AddRequest(&request1_);
   // Don't display the pending requests.
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   EXPECT_FALSE(prompt_factory_->is_visible());
 }
 
-TEST_F(PermissionRequestManagerTest, TwoRequestsCoalesce) {
-  manager_->DisplayPendingRequests();
-  manager_->AddRequest(&request1_);
-  manager_->AddRequest(&request2_);
-  EXPECT_FALSE(prompt_factory_->is_visible());
-  WaitForCoalescing();
-
-  EXPECT_TRUE(prompt_factory_->is_visible());
-  ASSERT_EQ(prompt_factory_->request_count(), 2);
-}
-
 TEST_F(PermissionRequestManagerTest, TwoRequestsDoNotCoalesce) {
   manager_->DisplayPendingRequests();
   manager_->AddRequest(&request1_);
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   manager_->AddRequest(&request2_);
 
   EXPECT_TRUE(prompt_factory_->is_visible());
@@ -198,14 +230,14 @@
 TEST_F(PermissionRequestManagerTest, TwoRequestsShownInTwoBubbles) {
   manager_->DisplayPendingRequests();
   manager_->AddRequest(&request1_);
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   manager_->AddRequest(&request2_);
 
   EXPECT_TRUE(prompt_factory_->is_visible());
   ASSERT_EQ(prompt_factory_->request_count(), 1);
 
   Accept();
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
 
   EXPECT_TRUE(prompt_factory_->is_visible());
   ASSERT_EQ(prompt_factory_->request_count(), 1);
@@ -215,18 +247,17 @@
 TEST_F(PermissionRequestManagerTest, TestAddDuplicateRequest) {
   manager_->DisplayPendingRequests();
   manager_->AddRequest(&request1_);
-  manager_->AddRequest(&request2_);
   manager_->AddRequest(&request1_);
 
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   EXPECT_TRUE(prompt_factory_->is_visible());
-  ASSERT_EQ(prompt_factory_->request_count(), 2);
+  ASSERT_EQ(prompt_factory_->request_count(), 1);
 }
 
 TEST_F(PermissionRequestManagerTest, SequentialRequests) {
   manager_->DisplayPendingRequests();
   manager_->AddRequest(&request1_);
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   EXPECT_TRUE(prompt_factory_->is_visible());
 
   Accept();
@@ -235,7 +266,7 @@
   EXPECT_FALSE(prompt_factory_->is_visible());
 
   manager_->AddRequest(&request2_);
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   EXPECT_TRUE(prompt_factory_->is_visible());
   Accept();
   EXPECT_FALSE(prompt_factory_->is_visible());
@@ -248,7 +279,7 @@
   manager_->AddRequest(&request1_);
   EXPECT_FALSE(request1_.finished());
 
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   EXPECT_TRUE(prompt_factory_->is_visible());
   ASSERT_EQ(prompt_factory_->request_count(), 1);
 }
@@ -268,7 +299,7 @@
 TEST_F(PermissionRequestManagerTest, DuplicateQueuedRequest) {
   manager_->DisplayPendingRequests();
   manager_->AddRequest(&request1_);
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   manager_->AddRequest(&request2_);
 
   MockPermissionRequest dupe_request("test1");
@@ -293,7 +324,7 @@
 TEST_F(PermissionRequestManagerTest, ForgetRequestsOnPageNavigation) {
   manager_->DisplayPendingRequests();
   manager_->AddRequest(&request1_);
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   manager_->AddRequest(&request2_);
   manager_->AddRequest(&iframe_request_other_domain_);
 
@@ -301,7 +332,7 @@
   ASSERT_EQ(prompt_factory_->request_count(), 1);
 
   NavigateAndCommit(GURL("http://www2.google.com/"));
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
 
   EXPECT_FALSE(prompt_factory_->is_visible());
   EXPECT_TRUE(request1_.finished());
@@ -320,7 +351,7 @@
   EXPECT_FALSE(prompt_factory_->is_visible());
 
   manager_->AddRequest(&request2_);
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   EXPECT_TRUE(prompt_factory_->is_visible());
   ASSERT_EQ(prompt_factory_->request_count(), 1);
 }
@@ -328,13 +359,13 @@
 TEST_F(PermissionRequestManagerTest, TestCancelWhileDialogShown) {
   manager_->DisplayPendingRequests();
   manager_->AddRequest(&request1_);
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
 
   prompt_factory_->SetCanUpdateUi(true);
   EXPECT_TRUE(prompt_factory_->is_visible());
   EXPECT_FALSE(request1_.finished());
   manager_->CancelRequest(&request1_);
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   EXPECT_TRUE(request1_.finished());
   EXPECT_FALSE(prompt_factory_->is_visible());
 }
@@ -343,7 +374,7 @@
   manager_->DisplayPendingRequests();
   prompt_factory_->SetCanUpdateUi(false);
   manager_->AddRequest(&request1_);
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   prompt_factory_->SetCanUpdateUi(false);
 
   EXPECT_TRUE(prompt_factory_->is_visible());
@@ -357,7 +388,7 @@
 TEST_F(PermissionRequestManagerTest, TestCancelPendingRequest) {
   manager_->DisplayPendingRequests();
   manager_->AddRequest(&request1_);
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   manager_->AddRequest(&request2_);
 
   EXPECT_TRUE(prompt_factory_->is_visible());
@@ -372,7 +403,7 @@
 TEST_F(PermissionRequestManagerTest, MainFrameNoRequestIFrameRequest) {
   manager_->DisplayPendingRequests();
   manager_->AddRequest(&iframe_request_same_domain_);
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   WaitForFrameLoad();
 
   EXPECT_TRUE(prompt_factory_->is_visible());
@@ -385,14 +416,19 @@
   manager_->AddRequest(&request1_);
   manager_->AddRequest(&iframe_request_same_domain_);
   WaitForFrameLoad();
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
 
   EXPECT_TRUE(prompt_factory_->is_visible());
-  ASSERT_EQ(prompt_factory_->request_count(), 2);
+  ASSERT_EQ(1, prompt_factory_->request_count());
   Closing();
   EXPECT_TRUE(request1_.finished());
-  EXPECT_TRUE(iframe_request_same_domain_.finished());
+  EXPECT_FALSE(iframe_request_same_domain_.finished());
+  WaitForBubbleToBeShown();
+  EXPECT_TRUE(prompt_factory_->is_visible());
+  ASSERT_EQ(1, prompt_factory_->request_count());
+  Closing();
   EXPECT_FALSE(prompt_factory_->is_visible());
+  EXPECT_TRUE(iframe_request_same_domain_.finished());
 }
 
 TEST_F(PermissionRequestManagerTest, MainFrameAndIFrameRequestOtherDomain) {
@@ -400,7 +436,7 @@
   manager_->AddRequest(&request1_);
   manager_->AddRequest(&iframe_request_other_domain_);
   WaitForFrameLoad();
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
 
   EXPECT_TRUE(prompt_factory_->is_visible());
   Closing();
@@ -414,7 +450,7 @@
 TEST_F(PermissionRequestManagerTest, IFrameRequestWhenMainRequestVisible) {
   manager_->DisplayPendingRequests();
   manager_->AddRequest(&request1_);
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   EXPECT_TRUE(prompt_factory_->is_visible());
 
   manager_->AddRequest(&iframe_request_same_domain_);
@@ -433,7 +469,7 @@
        IFrameRequestOtherDomainWhenMainRequestVisible) {
   manager_->DisplayPendingRequests();
   manager_->AddRequest(&request1_);
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   EXPECT_TRUE(prompt_factory_->is_visible());
 
   manager_->AddRequest(&iframe_request_other_domain_);
@@ -449,7 +485,7 @@
 TEST_F(PermissionRequestManagerTest, RequestsDontNeedUserGesture) {
   manager_->DisplayPendingRequests();
   WaitForFrameLoad();
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   manager_->AddRequest(&request1_);
   manager_->AddRequest(&iframe_request_other_domain_);
   manager_->AddRequest(&request2_);
@@ -463,7 +499,7 @@
 
   manager_->AddRequest(&request1_);
   manager_->DisplayPendingRequests();
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   histograms.ExpectUniqueSample(
       PermissionUmaUtil::kPermissionsPromptShown,
       static_cast<base::HistogramBase::Sample>(PermissionRequestType::QUOTA),
@@ -499,7 +535,7 @@
 
   manager_->AddRequest(&request2_);
   manager_->DisplayPendingRequests();
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
 
   histograms.ExpectTotalCount(
       PermissionUmaUtil::kPermissionsPromptShownGesture, 0);
@@ -534,7 +570,7 @@
 
   manager_->AddRequest(&request1_);
   manager_->DisplayPendingRequests();
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   // No need to test UMA for showing prompts again, they were tested in
   // UMAForSimpleAcceptedBubble.
 
@@ -549,10 +585,10 @@
 TEST_F(PermissionRequestManagerTest, UMAForMergedAcceptedBubble) {
   base::HistogramTester histograms;
 
-  manager_->AddRequest(&request1_);
-  manager_->AddRequest(&request2_);
+  manager_->AddRequest(&request_mic_);
+  manager_->AddRequest(&request_camera_);
   manager_->DisplayPendingRequests();
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
 
   histograms.ExpectUniqueSample(
       PermissionUmaUtil::kPermissionsPromptShown,
@@ -560,11 +596,13 @@
       1);
   histograms.ExpectBucketCount(
       PermissionUmaUtil::kPermissionsPromptMergedBubbleTypes,
-      static_cast<base::HistogramBase::Sample>(PermissionRequestType::QUOTA),
+      static_cast<base::HistogramBase::Sample>(
+          PermissionRequestType::PERMISSION_MEDIASTREAM_MIC),
       1);
   histograms.ExpectBucketCount(
       PermissionUmaUtil::kPermissionsPromptMergedBubbleTypes,
-      static_cast<base::HistogramBase::Sample>(PermissionRequestType::DOWNLOAD),
+      static_cast<base::HistogramBase::Sample>(
+          PermissionRequestType::PERMISSION_MEDIASTREAM_CAMERA),
       1);
   histograms.ExpectUniqueSample(
       PermissionUmaUtil::kPermissionsPromptRequestsPerPrompt, 2, 1);
@@ -583,21 +621,23 @@
       1);
   histograms.ExpectBucketCount(
       PermissionUmaUtil::kPermissionsPromptMergedBubbleAccepted,
-      static_cast<base::HistogramBase::Sample>(PermissionRequestType::QUOTA),
+      static_cast<base::HistogramBase::Sample>(
+          PermissionRequestType::PERMISSION_MEDIASTREAM_MIC),
       1);
   histograms.ExpectBucketCount(
       PermissionUmaUtil::kPermissionsPromptMergedBubbleAccepted,
-      static_cast<base::HistogramBase::Sample>(PermissionRequestType::DOWNLOAD),
+      static_cast<base::HistogramBase::Sample>(
+          PermissionRequestType::PERMISSION_MEDIASTREAM_CAMERA),
       1);
 }
 
 TEST_F(PermissionRequestManagerTest, UMAForMergedMixedBubble) {
   base::HistogramTester histograms;
 
-  manager_->AddRequest(&request1_);
-  manager_->AddRequest(&request2_);
+  manager_->AddRequest(&request_mic_);
+  manager_->AddRequest(&request_camera_);
   manager_->DisplayPendingRequests();
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   // No need to test UMA for showing prompts again, they were tested in
   // UMAForMergedAcceptedBubble.
 
@@ -611,21 +651,23 @@
       1);
   histograms.ExpectBucketCount(
       PermissionUmaUtil::kPermissionsPromptMergedBubbleAccepted,
-      static_cast<base::HistogramBase::Sample>(PermissionRequestType::QUOTA),
+      static_cast<base::HistogramBase::Sample>(
+          PermissionRequestType::PERMISSION_MEDIASTREAM_MIC),
       1);
   histograms.ExpectBucketCount(
       PermissionUmaUtil::kPermissionsPromptMergedBubbleDenied,
-      static_cast<base::HistogramBase::Sample>(PermissionRequestType::DOWNLOAD),
+      static_cast<base::HistogramBase::Sample>(
+          PermissionRequestType::PERMISSION_MEDIASTREAM_CAMERA),
       1);
 }
 
 TEST_F(PermissionRequestManagerTest, UMAForMergedDeniedBubble) {
   base::HistogramTester histograms;
 
-  manager_->AddRequest(&request1_);
-  manager_->AddRequest(&request2_);
+  manager_->AddRequest(&request_mic_);
+  manager_->AddRequest(&request_camera_);
   manager_->DisplayPendingRequests();
-  WaitForCoalescing();
+  WaitForBubbleToBeShown();
   // No need to test UMA for showing prompts again, they were tested in
   // UMAForMergedAcceptedBubble.
 
@@ -639,10 +681,12 @@
       1);
   histograms.ExpectBucketCount(
       PermissionUmaUtil::kPermissionsPromptMergedBubbleDenied,
-      static_cast<base::HistogramBase::Sample>(PermissionRequestType::QUOTA),
+      static_cast<base::HistogramBase::Sample>(
+          PermissionRequestType::PERMISSION_MEDIASTREAM_MIC),
       1);
   histograms.ExpectBucketCount(
       PermissionUmaUtil::kPermissionsPromptMergedBubbleDenied,
-      static_cast<base::HistogramBase::Sample>(PermissionRequestType::DOWNLOAD),
+      static_cast<base::HistogramBase::Sample>(
+          PermissionRequestType::PERMISSION_MEDIASTREAM_CAMERA),
       1);
 }
diff --git a/chrome/browser/resources/md_bookmarks/actions.js b/chrome/browser/resources/md_bookmarks/actions.js
index f13c451..744b365f 100644
--- a/chrome/browser/resources/md_bookmarks/actions.js
+++ b/chrome/browser/resources/md_bookmarks/actions.js
@@ -241,6 +241,18 @@
     };
   }
 
+  /**
+   * @param {IncognitoAvailability} availability
+   * @return {!Action}
+   */
+  function setIncognitoAvailability(availability) {
+    assert(availability != IncognitoAvailability.FORCED);
+    return {
+      name: 'set-incognito-availability',
+      value: availability,
+    };
+  }
+
   return {
     changeFolderOpen: changeFolderOpen,
     clearSearch: clearSearch,
@@ -254,6 +266,7 @@
     selectAll: selectAll,
     selectFolder: selectFolder,
     selectItem: selectItem,
+    setIncognitoAvailability: setIncognitoAvailability,
     setSearchResults: setSearchResults,
     setSearchTerm: setSearchTerm,
     updateAnchor: updateAnchor,
diff --git a/chrome/browser/resources/md_bookmarks/api_listener.js b/chrome/browser/resources/md_bookmarks/api_listener.js
index 95634523..0e54e07 100644
--- a/chrome/browser/resources/md_bookmarks/api_listener.js
+++ b/chrome/browser/resources/md_bookmarks/api_listener.js
@@ -78,6 +78,13 @@
     chrome.bookmarks.onCreated.addListener(onBookmarkCreated);
   }
 
+  /**
+   * @param {IncognitoAvailability} availability
+   */
+  function onIncognitoAvailabilityChanged(availability) {
+    dispatch(bookmarks.actions.setIncognitoAvailability(availability));
+  }
+
   function init() {
     chrome.bookmarks.onChanged.addListener(onBookmarkChanged);
     chrome.bookmarks.onChildrenReordered.addListener(onChildrenReordered);
@@ -86,6 +93,11 @@
     chrome.bookmarks.onRemoved.addListener(onBookmarkRemoved);
     chrome.bookmarks.onImportBegan.addListener(onImportBegan);
     chrome.bookmarks.onImportEnded.addListener(onImportEnded);
+
+    cr.sendWithPromise('getIncognitoAvailability')
+        .then(onIncognitoAvailabilityChanged);
+    cr.addWebUIListener(
+        'incognito-availability-changed', onIncognitoAvailabilityChanged);
   }
 
   return {
diff --git a/chrome/browser/resources/md_bookmarks/command_manager.js b/chrome/browser/resources/md_bookmarks/command_manager.js
index aef8c5a..e2f84cd 100644
--- a/chrome/browser/resources/md_bookmarks/command_manager.js
+++ b/chrome/browser/resources/md_bookmarks/command_manager.js
@@ -92,6 +92,7 @@
     },
 
     closeCommandMenu: function() {
+      this.menuIds_ = new Set();
       /** @type {!CrActionMenuElement} */ (this.$.dropdown).close();
     },
 
@@ -154,8 +155,11 @@
       switch (command) {
         case Command.OPEN_NEW_TAB:
         case Command.OPEN_NEW_WINDOW:
-        case Command.OPEN_INCOGNITO:
           return this.expandUrls_(itemIds).length > 0;
+        case Command.OPEN_INCOGNITO:
+          return this.expandUrls_(itemIds).length > 0 &&
+              this.getState().prefs.incognitoAvailability !=
+              IncognitoAvailability.DISABLED;
         default:
           return true;
       }
@@ -326,8 +330,8 @@
      * @private
      */
     onCommandClick_: function(e) {
-      this.closeCommandMenu();
       this.handle(e.target.getAttribute('command'), assert(this.menuIds_));
+      this.closeCommandMenu();
     },
 
     /**
@@ -363,7 +367,7 @@
       if (e.path[0] != this.$.dropdown)
         return;
 
-      this.$.dropdown.close();
+      this.closeCommandMenu();
     },
 
     /**
@@ -379,7 +383,7 @@
       var label;
       switch (command) {
         case Command.EDIT:
-          if (this.menuIds_.size > 1)
+          if (this.menuIds_.size != 1)
             return '';
 
           var id = Array.from(this.menuIds_)[0];
diff --git a/chrome/browser/resources/md_bookmarks/constants.js b/chrome/browser/resources/md_bookmarks/constants.js
index e93f0a43..b004992 100644
--- a/chrome/browser/resources/md_bookmarks/constants.js
+++ b/chrome/browser/resources/md_bookmarks/constants.js
@@ -32,6 +32,17 @@
   OPEN: 'open',
 };
 
+/**
+ * Mirrors the C++ enum from IncognitoModePrefs.
+ * @enum {number}
+ * @const
+ */
+var IncognitoAvailability = {
+  ENABLED: 0,
+  DISABLED: 1,
+  FORCED: 2,
+};
+
 /** @const */
 var LOCAL_STORAGE_CLOSED_FOLDERS_KEY = 'closedState';
 
diff --git a/chrome/browser/resources/md_bookmarks/reducers.js b/chrome/browser/resources/md_bookmarks/reducers.js
index 9b84689..7665d35 100644
--- a/chrome/browser/resources/md_bookmarks/reducers.js
+++ b/chrome/browser/resources/md_bookmarks/reducers.js
@@ -395,6 +395,24 @@
     return newClosedFolders;
   };
 
+  var PreferencesState = {};
+
+  /**
+   * @param {PreferencesState} prefs
+   * @param {Action} action
+   * @return {PreferencesState}
+   */
+  PreferencesState.updatePrefs = function(prefs, action) {
+    switch (action.name) {
+      case 'set-incognito-availability':
+        return /** @type {PreferencesState} */ (Object.assign({}, prefs, {
+          incognitoAvailability: action.value,
+        }));
+      default:
+        return prefs;
+    }
+  };
+
   /**
    * @param {ClosedFolderState} closedFolders
    * @param {Action} action
@@ -437,6 +455,7 @@
           state.selectedFolder, action, state.nodes),
       closedFolders: ClosedFolderState.updateClosedFolders(
           state.closedFolders, action, state.nodes),
+      prefs: PreferencesState.updatePrefs(state.prefs, action),
       search: SearchState.updateSearch(state.search, action),
       selection: SelectionState.updateSelection(state.selection, action),
     };
@@ -446,6 +465,7 @@
     reduceAction: reduceAction,
     ClosedFolderState: ClosedFolderState,
     NodeState: NodeState,
+    PreferencesState: PreferencesState,
     SearchState: SearchState,
     SelectedFolderState: SelectedFolderState,
     SelectionState: SelectionState,
diff --git a/chrome/browser/resources/md_bookmarks/types.js b/chrome/browser/resources/md_bookmarks/types.js
index d766e35..9b08e8c 100644
--- a/chrome/browser/resources/md_bookmarks/types.js
+++ b/chrome/browser/resources/md_bookmarks/types.js
@@ -50,9 +50,17 @@
 
 /**
  * @typedef {{
+ *   incognitoAvailability: IncognitoAvailability,
+ * }}
+ */
+var PreferencesState;
+
+/**
+ * @typedef {{
  *   nodes: NodeMap,
  *   selectedFolder: string,
  *   closedFolders: ClosedFolderState,
+ *   prefs: PreferencesState,
  *   search: SearchState,
  *   selection: SelectionState,
  * }}
diff --git a/chrome/browser/resources/md_bookmarks/util.js b/chrome/browser/resources/md_bookmarks/util.js
index a660add7..2a9de6a 100644
--- a/chrome/browser/resources/md_bookmarks/util.js
+++ b/chrome/browser/resources/md_bookmarks/util.js
@@ -70,6 +70,9 @@
       nodes: {},
       selectedFolder: '0',
       closedFolders: new Set(),
+      prefs: {
+        incognitoAvailability: IncognitoAvailability.ENABLED,
+      },
       search: {
         term: '',
         inProgress: false,
diff --git a/chrome/browser/resources/settings/route.js b/chrome/browser/resources/settings/route.js
index b675fc89..76adf7c6 100644
--- a/chrome/browser/resources/settings/route.js
+++ b/chrome/browser/resources/settings/route.js
@@ -168,6 +168,12 @@
     r.SITE_SETTINGS_ALL = r.SITE_SETTINGS.createChild('all');
     r.SITE_SETTINGS_SITE_DETAILS =
         r.SITE_SETTINGS_ALL.createChild('/content/siteDetails');
+  } else if (loadTimeData.getBoolean('enableSiteDetails')) {
+    // When there is no "All Sites", pressing 'back' from "Site Details" should
+    // return to "Content Settings". This should only occur when |kSiteSettings|
+    // is off and |kSiteDetails| is on.
+    r.SITE_SETTINGS_SITE_DETAILS =
+        r.SITE_SETTINGS.createChild('/content/siteDetails');
   }
 
   r.SITE_SETTINGS_HANDLERS = r.SITE_SETTINGS.createChild('/handlers');
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 710e768..24e5e840 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -948,6 +948,8 @@
       "webui/identity_internals_ui.h",
       "webui/inspect_ui.cc",
       "webui/inspect_ui.h",
+      "webui/md_bookmarks/bookmarks_message_handler.cc",
+      "webui/md_bookmarks/bookmarks_message_handler.h",
       "webui/md_bookmarks/md_bookmarks_ui.cc",
       "webui/md_bookmarks/md_bookmarks_ui.h",
       "webui/md_downloads/downloads_list_tracker.cc",
diff --git a/chrome/browser/ui/cocoa/page_info/page_info_bubble_controller.mm b/chrome/browser/ui/cocoa/page_info/page_info_bubble_controller.mm
index e237b99..0dfd1bc 100644
--- a/chrome/browser/ui/cocoa/page_info/page_info_bubble_controller.mm
+++ b/chrome/browser/ui/cocoa/page_info/page_info_bubble_controller.mm
@@ -541,11 +541,7 @@
 - (void)showSiteSettingsData:(id)sender {
   DCHECK(webContents_);
   DCHECK(presenter_);
-  presenter_->RecordPageInfoAction(PageInfo::PAGE_INFO_SITE_SETTINGS_OPENED);
-  webContents_->OpenURL(content::OpenURLParams(
-      GURL(chrome::kChromeUIContentSettingsURL), content::Referrer(),
-      WindowOpenDisposition::NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_LINK,
-      false));
+  presenter_->OpenSiteSettingsView();
 }
 
 // TODO(lgarron): Move some of this to the presenter for separation of concerns
diff --git a/chrome/browser/ui/page_info/page_info.cc b/chrome/browser/ui/page_info/page_info.cc
index b5a07be5..0d815e74 100644
--- a/chrome/browser/ui/page_info/page_info.cc
+++ b/chrome/browser/ui/page_info/page_info.cc
@@ -43,6 +43,7 @@
 #include "chrome/browser/ui/page_info/page_info_ui.h"
 #include "chrome/browser/usb/usb_chooser_context.h"
 #include "chrome/browser/usb/usb_chooser_context_factory.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/theme_resources.h"
@@ -66,6 +67,7 @@
 #include "net/ssl/ssl_connection_status_flags.h"
 #include "third_party/boringssl/src/include/openssl/ssl.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "url/origin.h"
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/policy/policy_cert_service.h"
@@ -411,6 +413,31 @@
   did_revoke_user_ssl_decisions_ = true;
 }
 
+void PageInfo::OpenSiteSettingsView() {
+  // By default, this opens the general Content Settings pane. If the
+  // |kSiteSettings| and/or |kSiteDetails| flags are enabled this opens a
+  // settings page specific to the current origin of the page. crbug.com/655876
+  url::Origin site_origin = url::Origin(site_url());
+  std::string link_destination(chrome::kChromeUIContentSettingsURL);
+  if ((base::CommandLine::ForCurrentProcess()->HasSwitch(
+           switches::kEnableSiteSettings) ||
+       base::FeatureList::IsEnabled(features::kSiteDetails)) &&
+      !site_origin.unique()) {
+    std::string origin_string = site_origin.Serialize();
+    url::RawCanonOutputT<char> percent_encoded_origin;
+    url::EncodeURIComponent(origin_string.c_str(), origin_string.length(),
+                            &percent_encoded_origin);
+    link_destination = chrome::kChromeUISiteDetailsPrefixURL +
+                       std::string(percent_encoded_origin.data(),
+                                   percent_encoded_origin.length());
+  }
+  web_contents()->OpenURL(
+      content::OpenURLParams(GURL(link_destination), content::Referrer(),
+                             WindowOpenDisposition::NEW_FOREGROUND_TAB,
+                             ui::PAGE_TRANSITION_LINK, false));
+  RecordPageInfoAction(PageInfo::PAGE_INFO_SITE_SETTINGS_OPENED);
+}
+
 void PageInfo::Init(const GURL& url,
                     const security_state::SecurityInfo& security_info) {
 #if !defined(OS_ANDROID) && !defined(OS_IOS)
diff --git a/chrome/browser/ui/page_info/page_info.h b/chrome/browser/ui/page_info/page_info.h
index 49eaff4..f7f981e 100644
--- a/chrome/browser/ui/page_info/page_info.h
+++ b/chrome/browser/ui/page_info/page_info.h
@@ -142,6 +142,9 @@
   // This method is called when the revoke SSL error bypass button is pressed.
   void OnRevokeSSLErrorBypassButtonPressed();
 
+  // Handles opening the link to show more site settings and records the event.
+  void OpenSiteSettingsView();
+
   // Accessors.
   SiteConnectionStatus site_connection_status() const {
     return site_connection_status_;
diff --git a/chrome/browser/ui/view_ids.h b/chrome/browser/ui/view_ids.h
index 78a02ed..aa0204a 100644
--- a/chrome/browser/ui/view_ids.h
+++ b/chrome/browser/ui/view_ids.h
@@ -95,6 +95,14 @@
 
   // Plus button on location bar.
   VIEW_ID_ACTION_BOX_BUTTON,
+
+  // Page Info bubble.
+  VIEW_ID_PAGE_INFO_BUTTON_CLOSE,
+  VIEW_ID_PAGE_INFO_LABEL_SECURITY_DETAILS,
+  VIEW_ID_PAGE_INFO_LABEL_RESET_CERTIFICATE_DECISIONS,
+  VIEW_ID_PAGE_INFO_LINK_COOKIE_DIALOG,
+  VIEW_ID_PAGE_INFO_LINK_SITE_SETTINGS,
+  VIEW_ID_PAGE_INFO_LINK_CERTIFICATE_VIEWER,
 };
 
 #endif  // CHROME_BROWSER_UI_VIEW_IDS_H_
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
index 71e22e32..abdd29e7 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/page_info/page_info.h"
+#include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/collected_cookies_views.h"
 #include "chrome/browser/ui/views/harmony/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/harmony/chrome_typography.h"
@@ -60,14 +61,14 @@
 
 namespace {
 
-// NOTE(jdonnelly): This use of this process-wide variable assumes that there's
-// never more than one page info bubble shown and that it's associated
-// with the current window. If this assumption fails in the future, we'll need
-// to return a weak pointer from ShowBubble so callers can associate it with the
-// current window (or other context) and check if the bubble they care about is
-// showing.
+// NOTE(jdonnelly): The following two process-wide variables assume that there's
+// never more than one page info bubble shown and that it's associated with the
+// current window. If this assumption fails in the future, we'll need to return
+// a weak pointer from ShowBubble so callers can associate it with the current
+// window (or other context) and check if the bubble they care about is showing.
 PageInfoBubbleView::BubbleType g_shown_bubble_type =
     PageInfoBubbleView::BUBBLE_NONE;
+views::BubbleDialogDelegateView* g_page_info_bubble = nullptr;
 
 // General constants -----------------------------------------------------------
 
@@ -99,14 +100,6 @@
 // Spacing between the label and the menu.
 const int kPermissionMenuSpacing = 16;
 
-// Button/styled label/link IDs ------------------------------------------------
-const int BUTTON_CLOSE = 1337;
-const int STYLED_LABEL_SECURITY_DETAILS = 1338;
-const int STYLED_LABEL_RESET_CERTIFICATE_DECISIONS = 1339;
-const int LINK_COOKIE_DIALOG = 1340;
-const int LINK_SITE_SETTINGS = 1341;
-const int LINK_CERTIFICATE_VIEWER = 1342;
-
 // The default, ui::kTitleFontSizeDelta, is too large for the page info
 // bubble (e.g. +3). Use +1 to obtain a smaller font.
 constexpr int kSummaryFontSizeDelta = 1;
@@ -189,13 +182,13 @@
   // The label that displays the status of the identity check for this site.
   // Includes a link to open the Chrome Help Center article about connection
   // security.
-  views::StyledLabel* details_label_;
+  views::StyledLabel* security_details_label_;
 
   // A container for the styled label with a link for resetting cert decisions.
   // This is only shown sometimes, so we use a container to keep track of
   // where to place it (if needed).
   views::View* reset_decisions_label_container_;
-  views::StyledLabel* reset_decisions_label_;
+  views::StyledLabel* reset_cert_decisions_label_;
 
   DISALLOW_COPY_AND_ASSIGN(BubbleHeaderView);
 };
@@ -234,9 +227,9 @@
     views::StyledLabelListener* styled_label_listener,
     int side_margin)
     : styled_label_listener_(styled_label_listener),
-      details_label_(nullptr),
+      security_details_label_(nullptr),
       reset_decisions_label_container_(nullptr),
-      reset_decisions_label_(nullptr) {
+      reset_cert_decisions_label_(nullptr) {
   views::GridLayout* layout = new views::GridLayout(this);
   SetLayoutManager(layout);
 
@@ -245,10 +238,10 @@
   layout->AddPaddingRow(0, kHeaderLabelSpacing);
 
   layout->StartRow(0, label_column_status);
-  details_label_ =
+  security_details_label_ =
       new views::StyledLabel(base::string16(), styled_label_listener);
-  details_label_->set_id(STYLED_LABEL_SECURITY_DETAILS);
-  layout->AddView(details_label_, 1, 1, views::GridLayout::FILL,
+  security_details_label_->set_id(VIEW_ID_PAGE_INFO_LABEL_SECURITY_DETAILS);
+  layout->AddView(security_details_label_, 1, 1, views::GridLayout::FILL,
                   views::GridLayout::LEADING);
 
   layout->StartRow(0, label_column_status);
@@ -272,7 +265,7 @@
 
   base::string16 text = base::ReplaceStringPlaceholders(
       base::ASCIIToUTF16("$1 $2"), subst, &offsets);
-  details_label_->SetText(text);
+  security_details_label_->SetText(text);
   gfx::Range details_range(offsets[1], text.length());
 
   views::StyledLabel::RangeStyleInfo link_style =
@@ -281,7 +274,7 @@
     link_style.font_style |= gfx::Font::FontStyle::UNDERLINE;
   link_style.disable_line_wrapping = false;
 
-  details_label_->AddStyleRange(details_range, link_style);
+  security_details_label_->AddStyleRange(details_range, link_style);
 }
 
 void BubbleHeaderView::AddResetDecisionsLabel() {
@@ -295,8 +288,10 @@
 
   base::string16 text = base::ReplaceStringPlaceholders(
       base::ASCIIToUTF16("$1 $2"), subst, &offsets);
-  reset_decisions_label_ = new views::StyledLabel(text, styled_label_listener_);
-  reset_decisions_label_->set_id(STYLED_LABEL_RESET_CERTIFICATE_DECISIONS);
+  reset_cert_decisions_label_ =
+      new views::StyledLabel(text, styled_label_listener_);
+  reset_cert_decisions_label_->set_id(
+      VIEW_ID_PAGE_INFO_LABEL_RESET_CERTIFICATE_DECISIONS);
   gfx::Range link_range(offsets[1], text.length());
 
   views::StyledLabel::RangeStyleInfo link_style =
@@ -305,10 +300,10 @@
     link_style.font_style |= gfx::Font::FontStyle::UNDERLINE;
   link_style.disable_line_wrapping = false;
 
-  reset_decisions_label_->AddStyleRange(link_range, link_style);
+  reset_cert_decisions_label_->AddStyleRange(link_range, link_style);
   // Fit the styled label to occupy available width.
-  reset_decisions_label_->SizeToFit(0);
-  reset_decisions_label_container_->AddChildView(reset_decisions_label_);
+  reset_cert_decisions_label_->SizeToFit(0);
+  reset_decisions_label_container_->AddChildView(reset_cert_decisions_label_);
 
   // Now that it contains a label, the container needs padding at the top.
   reset_decisions_label_container_->SetBorder(
@@ -327,6 +322,7 @@
     const GURL& url)
     : BubbleDialogDelegateView(anchor_view, views::BubbleBorder::TOP_LEFT) {
   g_shown_bubble_type = PageInfoBubbleView::BUBBLE_INTERNAL_PAGE;
+  g_page_info_bubble = this;
   set_parent_window(parent_window);
 
   int text = IDS_PAGE_INFO_INTERNAL_PAGE;
@@ -370,6 +366,7 @@
 
 void InternalPageInfoBubbleView::OnWidgetDestroying(views::Widget* widget) {
   g_shown_bubble_type = PageInfoBubbleView::BUBBLE_NONE;
+  g_page_info_bubble = nullptr;
 }
 
 int InternalPageInfoBubbleView::GetDialogButtons() const {
@@ -422,6 +419,11 @@
   return g_shown_bubble_type;
 }
 
+// static
+views::BubbleDialogDelegateView* PageInfoBubbleView::GetPageInfoBubble() {
+  return g_page_info_bubble;
+}
+
 PageInfoBubbleView::PageInfoBubbleView(
     views::View* anchor_view,
     gfx::NativeView parent_window,
@@ -439,6 +441,7 @@
       permissions_view_(nullptr),
       weak_factory_(this) {
   g_shown_bubble_type = BUBBLE_PAGE_INFO;
+  g_page_info_bubble = this;
   set_parent_window(parent_window);
 
   // Compensate for built-in vertical padding in the anchor view's image.
@@ -536,6 +539,7 @@
 
 void PageInfoBubbleView::OnWidgetDestroying(views::Widget* widget) {
   g_shown_bubble_type = BUBBLE_NONE;
+  g_page_info_bubble = nullptr;
   presenter_->OnUIClosing();
 }
 
@@ -550,7 +554,7 @@
 
 void PageInfoBubbleView::ButtonPressed(views::Button* button,
                                        const ui::Event& event) {
-  DCHECK_EQ(BUTTON_CLOSE, button->id());
+  DCHECK_EQ(VIEW_ID_PAGE_INFO_BUTTON_CLOSE, button->id());
   GetWidget()->Close();
 }
 
@@ -660,7 +664,7 @@
   // Add site settings link.
   views::Link* site_settings_link = new views::Link(
       l10n_util::GetStringUTF16(IDS_PAGE_INFO_SITE_SETTINGS_LINK));
-  site_settings_link->set_id(LINK_SITE_SETTINGS);
+  site_settings_link->set_id(VIEW_ID_PAGE_INFO_LINK_SITE_SETTINGS);
   site_settings_link->set_listener(this);
   views::View* link_section = new views::View();
   const int kLinkMarginTop = 4;
@@ -695,11 +699,12 @@
                          : IDS_PAGE_INFO_CERTIFICATE_INVALID_LINK);
 
       // Create the link to add to the Certificate Section.
-      views::Link* inspect_link = new views::Link(link_title);
-      inspect_link->set_id(LINK_CERTIFICATE_VIEWER);
-      inspect_link->set_listener(this);
+      views::Link* certificate_viewer_link = new views::Link(link_title);
+      certificate_viewer_link->set_id(
+          VIEW_ID_PAGE_INFO_LINK_CERTIFICATE_VIEWER);
+      certificate_viewer_link->set_listener(this);
       if (valid_identity) {
-        inspect_link->SetTooltipText(l10n_util::GetStringFUTF16(
+        certificate_viewer_link->SetTooltipText(l10n_util::GetStringFUTF16(
             IDS_PAGE_INFO_CERTIFICATE_VALID_LINK_TOOLTIP,
             base::UTF8ToUTF16(certificate_->issuer().GetDisplayName())));
       }
@@ -707,7 +712,8 @@
       // Add the Certificate Section.
       site_settings_view_->AddChildViewAt(
           CreateInspectLinkSection(PageInfoUI::GetCertificateIcon(),
-                                   IDS_PAGE_INFO_CERTIFICATE, inspect_link),
+                                   IDS_PAGE_INFO_CERTIFICATE,
+                                   certificate_viewer_link),
           0);
     }
   }
@@ -729,7 +735,7 @@
   // Create the link and icon for the Certificate section.
   cookie_dialog_link_ = new views::Link(
       l10n_util::GetPluralStringFUTF16(IDS_PAGE_INFO_NUM_COOKIES, 0));
-  cookie_dialog_link_->set_id(LINK_COOKIE_DIALOG);
+  cookie_dialog_link_->set_id(VIEW_ID_PAGE_INFO_LINK_COOKIE_DIALOG);
   cookie_dialog_link_->set_listener(this);
 
   PageInfoUI::PermissionInfo info;
@@ -752,25 +758,16 @@
   if (web_contents() == nullptr || web_contents()->IsBeingDestroyed())
     return;
   switch (source->id()) {
-    case LINK_SITE_SETTINGS:
-      // TODO(crbug.com/655876): This opens the general Content Settings pane,
-      // which is OK for now. But on Android, it opens a page specific to a
-      // given origin that shows all of the settings for that origin. If/when
-      // that's available on desktop we should link to that here, too.
-      web_contents()->OpenURL(content::OpenURLParams(
-          GURL(chrome::kChromeUIContentSettingsURL), content::Referrer(),
-          WindowOpenDisposition::NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_LINK,
-          false));
-      presenter_->RecordPageInfoAction(
-          PageInfo::PAGE_INFO_SITE_SETTINGS_OPENED);
+    case VIEW_ID_PAGE_INFO_LINK_SITE_SETTINGS:
+      presenter_->OpenSiteSettingsView();
       break;
-    case LINK_COOKIE_DIALOG:
+    case VIEW_ID_PAGE_INFO_LINK_COOKIE_DIALOG:
       // Count how often the Collected Cookies dialog is opened.
       presenter_->RecordPageInfoAction(
           PageInfo::PAGE_INFO_COOKIES_DIALOG_OPENED);
       new CollectedCookiesViews(web_contents());
       break;
-    case LINK_CERTIFICATE_VIEWER: {
+    case VIEW_ID_PAGE_INFO_LINK_CERTIFICATE_VIEWER: {
       gfx::NativeWindow top_window = web_contents()->GetTopLevelNativeWindow();
       if (certificate_ && top_window) {
         presenter_->RecordPageInfoAction(
@@ -788,7 +785,7 @@
                                                 const gfx::Range& range,
                                                 int event_flags) {
   switch (label->id()) {
-    case STYLED_LABEL_SECURITY_DETAILS:
+    case VIEW_ID_PAGE_INFO_LABEL_SECURITY_DETAILS:
       web_contents()->OpenURL(content::OpenURLParams(
           GURL(chrome::kPageInfoHelpCenterURL), content::Referrer(),
           WindowOpenDisposition::NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_LINK,
@@ -796,7 +793,7 @@
       presenter_->RecordPageInfoAction(
           PageInfo::PAGE_INFO_CONNECTION_HELP_OPENED);
       break;
-    case STYLED_LABEL_RESET_CERTIFICATE_DECISIONS:
+    case VIEW_ID_PAGE_INFO_LABEL_RESET_CERTIFICATE_DECISIONS:
       presenter_->OnRevokeSSLErrorBypassButtonPressed();
       GetWidget()->Close();
       break;
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view.h b/chrome/browser/ui/views/page_info/page_info_bubble_view.h
index d8cfbfd9..41002b1f 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view.h
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.h
@@ -88,6 +88,9 @@
   // Returns the type of the bubble being shown.
   static BubbleType GetShownBubbleType();
 
+  // Returns a weak reference to the page info bubble being shown.
+  static views::BubbleDialogDelegateView* GetPageInfoBubble();
+
  private:
   friend class test::PageInfoBubbleViewTestApi;
 
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc
new file mode 100644
index 0000000..29d96d4
--- /dev/null
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc
@@ -0,0 +1,174 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/page_info/page_info_bubble_view.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/view_ids.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
+#include "chrome/browser/ui/views/location_bar/location_icon_view.h"
+#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/test/browser_test_utils.h"
+#include "ui/accessibility/ax_action_data.h"
+
+namespace {
+
+typedef InProcessBrowserTest PageInfoBubbleViewBrowserTest;
+
+void PerformMouseClickOnView(views::View* view) {
+  ui::AXActionData data;
+  data.action = ui::AX_ACTION_DO_DEFAULT;
+  view->HandleAccessibleAction(data);
+}
+
+// Clicks the location icon to open the page info bubble.
+void OpenPageInfoBubble(Browser* browser) {
+  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
+  views::View* location_icon_view =
+      browser_view->toolbar()->location_bar()->location_icon_view();
+  ASSERT_TRUE(location_icon_view);
+  PerformMouseClickOnView(location_icon_view);
+  views::BubbleDialogDelegateView* page_info =
+      PageInfoBubbleView::GetPageInfoBubble();
+  EXPECT_NE(nullptr, page_info);
+  page_info->set_close_on_deactivate(false);
+}
+
+// Opens the Page Info bubble and retrieves the "Site settings" button.
+views::View* GetSiteSettingsButton(Browser* browser) {
+  OpenPageInfoBubble(browser);
+  views::Widget* page_info_bubble =
+      PageInfoBubbleView::GetPageInfoBubble()->GetWidget();
+  EXPECT_TRUE(page_info_bubble);
+
+  // Retrieve the "Site settings" button.
+  views::View* site_settings_button =
+      page_info_bubble->GetRootView()->GetViewByID(
+          VIEW_ID_PAGE_INFO_LINK_SITE_SETTINGS);
+  EXPECT_TRUE(site_settings_button);
+  return site_settings_button;
+}
+
+// Clicks the "Site settings" button from Page Info and waits for a "Settings"
+// tab to open.
+void ClickAndWaitForSettingsPageToOpen(views::View* site_settings_button) {
+  content::WebContentsAddedObserver new_tab_observer;
+  PerformMouseClickOnView(site_settings_button);
+
+  base::string16 expected_title(base::ASCIIToUTF16("Settings"));
+  content::TitleWatcher title_watcher(new_tab_observer.GetWebContents(),
+                                      expected_title);
+  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
+}
+
+// Returns the URL of the new tab that's opened on clicking the "Site settings"
+// button from Page Info.
+const GURL OpenSiteSettingsForUrl(Browser* browser,
+                                  const GURL& url,
+                                  bool enable_site_details) {
+  ui_test_utils::NavigateToURL(browser, url);
+  views::View* site_settings_button = GetSiteSettingsButton(browser);
+  base::test::ScopedFeatureList feature_list;
+  if (enable_site_details)
+    feature_list.InitAndEnableFeature(features::kSiteDetails);
+  else
+    feature_list.InitAndDisableFeature(features::kSiteDetails);
+  ClickAndWaitForSettingsPageToOpen(site_settings_button);
+
+  return browser->tab_strip_model()
+      ->GetActiveWebContents()
+      ->GetLastCommittedURL();
+}
+
+IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, ShowBubble) {
+  OpenPageInfoBubble(browser());
+  EXPECT_EQ(PageInfoBubbleView::BUBBLE_PAGE_INFO,
+            PageInfoBubbleView::GetShownBubbleType());
+}
+
+IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, ChromeURL) {
+  ui_test_utils::NavigateToURL(browser(), GURL("chrome://settings"));
+  OpenPageInfoBubble(browser());
+  EXPECT_EQ(PageInfoBubbleView::BUBBLE_INTERNAL_PAGE,
+            PageInfoBubbleView::GetShownBubbleType());
+}
+
+IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, ChromeExtensionURL) {
+  ui_test_utils::NavigateToURL(
+      browser(), GURL("chrome-extension://extension-id/options.html"));
+  OpenPageInfoBubble(browser());
+  EXPECT_EQ(PageInfoBubbleView::BUBBLE_INTERNAL_PAGE,
+            PageInfoBubbleView::GetShownBubbleType());
+}
+
+IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, ChromeDevtoolsURL) {
+  ui_test_utils::NavigateToURL(
+      browser(), GURL("chrome-devtools://devtools/bundled/inspector.html"));
+  OpenPageInfoBubble(browser());
+  EXPECT_EQ(PageInfoBubbleView::BUBBLE_INTERNAL_PAGE,
+            PageInfoBubbleView::GetShownBubbleType());
+}
+
+IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, ViewSourceURL) {
+  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+  chrome::ViewSelectedSource(browser());
+  OpenPageInfoBubble(browser());
+  EXPECT_EQ(PageInfoBubbleView::BUBBLE_INTERNAL_PAGE,
+            PageInfoBubbleView::GetShownBubbleType());
+}
+
+// Test opening "Content Settings" via Page Info from an ASCII origin works.
+IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, SiteSettingsLink) {
+  GURL url = GURL("https://www.google.com/");
+  EXPECT_EQ(GURL(chrome::kChromeUIContentSettingsURL),
+            OpenSiteSettingsForUrl(browser(), url, false));
+}
+
+// Test opening "Site Details" via Page Info from an ASCII origin does the
+// correct URL canonicalization.
+IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
+                       SiteSettingsLinkWithSiteDetailsEnabled) {
+  GURL url = GURL("https://www.google.com/");
+  std::string expected_origin = "https%3A%2F%2Fwww.google.com";
+  EXPECT_EQ(GURL(chrome::kChromeUISiteDetailsPrefixURL + expected_origin),
+            OpenSiteSettingsForUrl(browser(), url, true));
+}
+
+// Test opening "Site Details" via Page Info from a non-ASCII URL converts it to
+// an origin and does punycode conversion as well as URL canonicalization.
+IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
+                       SiteSettingsLinkWithSiteDetailsEnabledAndNonAsciiUrl) {
+  GURL url = GURL("http://🥄.ws/other/stuff.htm");
+  std::string expected_origin = "http%3A%2F%2Fxn--9q9h.ws";
+  EXPECT_EQ(GURL(chrome::kChromeUISiteDetailsPrefixURL + expected_origin),
+            OpenSiteSettingsForUrl(browser(), url, true));
+}
+
+// Test opening "Site Details" via Page Info from an origin with a non-default
+// (scheme, port) pair will specify port # in the origin passed to query params.
+IN_PROC_BROWSER_TEST_F(
+    PageInfoBubbleViewBrowserTest,
+    SiteSettingsLinkWithSiteDetailsEnabledAndNonDefaultPort) {
+  GURL url = GURL("https://www.example.com:8372");
+  std::string expected_origin = "https%3A%2F%2Fwww.example.com%3A8372";
+  EXPECT_EQ(GURL(chrome::kChromeUISiteDetailsPrefixURL + expected_origin),
+            OpenSiteSettingsForUrl(browser(), url, true));
+}
+
+// Test opening "Site Details" via Page Info from about:blank goes to "Content
+// Settings" (the alternative is a blank origin being sent to "Site Details").
+IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
+                       SiteSettingsLinkAboutBlankWithSiteDetailsEnabled) {
+  EXPECT_EQ(GURL(chrome::kChromeUIContentSettingsURL),
+            OpenSiteSettingsForUrl(browser(), GURL(url::kAboutBlankURL), true));
+}
+
+}  // namespace
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_interactive_uitest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_interactive_uitest.cc
deleted file mode 100644
index ee22800..0000000
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view_interactive_uitest.cc
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/views/page_info/page_info_bubble_view.h"
-
-#include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
-#include "chrome/browser/ui/views/location_bar/location_icon_view.h"
-#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/interactive_test_utils.h"
-
-namespace {
-
-typedef InProcessBrowserTest PageInfoBubbleViewBrowserTest;
-
-// Clicks the location icon to open the page info bubble.
-void ClickAndWait(Browser* browser) {
-  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
-  views::View* location_icon_view =
-      browser_view->toolbar()->location_bar()->location_icon_view();
-  ASSERT_TRUE(location_icon_view);
-
-  scoped_refptr<content::MessageLoopRunner> runner =
-      new content::MessageLoopRunner;
-  ui_test_utils::MoveMouseToCenterAndPress(
-      location_icon_view, ui_controls::LEFT,
-      ui_controls::DOWN | ui_controls::UP, runner->QuitClosure());
-  runner->Run();
-}
-
-IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, ShowBubble) {
-  ClickAndWait(browser());
-  EXPECT_EQ(PageInfoBubbleView::BUBBLE_PAGE_INFO,
-            PageInfoBubbleView::GetShownBubbleType());
-}
-
-IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, ChromeURL) {
-  ui_test_utils::NavigateToURL(browser(), GURL("chrome://settings"));
-  ClickAndWait(browser());
-  EXPECT_EQ(PageInfoBubbleView::BUBBLE_INTERNAL_PAGE,
-            PageInfoBubbleView::GetShownBubbleType());
-}
-
-IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, ChromeExtensionURL) {
-  ui_test_utils::NavigateToURL(
-      browser(), GURL("chrome-extension://extension-id/options.html"));
-  ClickAndWait(browser());
-  EXPECT_EQ(PageInfoBubbleView::BUBBLE_INTERNAL_PAGE,
-            PageInfoBubbleView::GetShownBubbleType());
-}
-
-IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, ChromeDevtoolsURL) {
-  ui_test_utils::NavigateToURL(
-      browser(), GURL("chrome-devtools://devtools/bundled/inspector.html"));
-  ClickAndWait(browser());
-  EXPECT_EQ(PageInfoBubbleView::BUBBLE_INTERNAL_PAGE,
-            PageInfoBubbleView::GetShownBubbleType());
-}
-
-IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, ViewSourceURL) {
-  ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
-  chrome::ViewSelectedSource(browser());
-  ClickAndWait(browser());
-  EXPECT_EQ(PageInfoBubbleView::BUBBLE_INTERNAL_PAGE,
-            PageInfoBubbleView::GetShownBubbleType());
-}
-
-}  // namespace
diff --git a/chrome/browser/ui/webui/md_bookmarks/bookmarks_message_handler.cc b/chrome/browser/ui/webui/md_bookmarks/bookmarks_message_handler.cc
new file mode 100644
index 0000000..47dea5bc
--- /dev/null
+++ b/chrome/browser/ui/webui/md_bookmarks/bookmarks_message_handler.cc
@@ -0,0 +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.
+
+#include "chrome/browser/ui/webui/md_bookmarks/bookmarks_message_handler.h"
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_service.h"
+
+BookmarksMessageHandler::BookmarksMessageHandler() {}
+
+BookmarksMessageHandler::~BookmarksMessageHandler() {}
+
+void BookmarksMessageHandler::RegisterMessages() {
+  web_ui()->RegisterMessageCallback(
+      "getIncognitoAvailability",
+      base::Bind(&BookmarksMessageHandler::HandleGetIncognitoAvailability,
+                 base::Unretained(this)));
+}
+
+void BookmarksMessageHandler::OnJavascriptAllowed() {
+  PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
+  pref_change_registrar_.Init(prefs);
+  pref_change_registrar_.Add(
+      prefs::kIncognitoModeAvailability,
+      base::Bind(&BookmarksMessageHandler::UpdateIncognitoAvailability,
+                 base::Unretained(this)));
+}
+
+void BookmarksMessageHandler::OnJavascriptDisallowed() {
+  pref_change_registrar_.RemoveAll();
+}
+
+void BookmarksMessageHandler::UpdateIncognitoAvailability() {
+  FireWebUIListener("incognito-availability-changed",
+                    base::Value(GetIncognitoAvailability()));
+}
+
+int BookmarksMessageHandler::GetIncognitoAvailability() {
+  PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
+  return prefs->GetInteger(prefs::kIncognitoModeAvailability);
+}
+
+void BookmarksMessageHandler::HandleGetIncognitoAvailability(
+    const base::ListValue* args) {
+  CHECK_EQ(1U, args->GetSize());
+  const base::Value* callback_id;
+  CHECK(args->Get(0, &callback_id));
+
+  AllowJavascript();
+
+  ResolveJavascriptCallback(*callback_id,
+                            base::Value(GetIncognitoAvailability()));
+}
diff --git a/chrome/browser/ui/webui/md_bookmarks/bookmarks_message_handler.h b/chrome/browser/ui/webui/md_bookmarks/bookmarks_message_handler.h
new file mode 100644
index 0000000..e76640c
--- /dev/null
+++ b/chrome/browser/ui/webui/md_bookmarks/bookmarks_message_handler.h
@@ -0,0 +1,35 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MD_BOOKMARKS_BOOKMARKS_MESSAGE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_MD_BOOKMARKS_BOOKMARKS_MESSAGE_HANDLER_H_
+
+#include "components/prefs/pref_change_registrar.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace base {
+class ListValue;
+};
+
+class BookmarksMessageHandler : public content::WebUIMessageHandler {
+ public:
+  BookmarksMessageHandler();
+  ~BookmarksMessageHandler() override;
+
+ private:
+  int GetIncognitoAvailability();
+  void HandleGetIncognitoAvailability(const base::ListValue* args);
+  void UpdateIncognitoAvailability();
+
+  // content::WebUIMessageHandler:
+  void RegisterMessages() override;
+  void OnJavascriptAllowed() override;
+  void OnJavascriptDisallowed() override;
+
+  PrefChangeRegistrar pref_change_registrar_;
+
+  DISALLOW_COPY_AND_ASSIGN(BookmarksMessageHandler);
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_MD_BOOKMARKS_BOOKMARKS_MESSAGE_HANDLER_H_
diff --git a/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_browsertest.cc b/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_browsertest.cc
new file mode 100644
index 0000000..fa9ce17
--- /dev/null
+++ b/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_browsertest.cc
@@ -0,0 +1,48 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/md_bookmarks/md_bookmarks_browsertest.h"
+
+#include "chrome/browser/prefs/incognito_mode_prefs.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
+
+MdBookmarksBrowserTest::MdBookmarksBrowserTest() {}
+
+MdBookmarksBrowserTest::~MdBookmarksBrowserTest() {}
+
+void MdBookmarksBrowserTest::RegisterMessages() {
+  web_ui()->RegisterMessageCallback(
+      "testSetIncognito",
+      base::Bind(&MdBookmarksBrowserTest::HandleSetIncognitoAvailability,
+                 base::Unretained(this)));
+}
+
+void MdBookmarksBrowserTest::SetIncognitoAvailability(int availability) {
+  ASSERT_TRUE(availability >= 0 &&
+              availability < IncognitoModePrefs::AVAILABILITY_NUM_TYPES);
+  browser()->profile()->GetPrefs()->SetInteger(
+      prefs::kIncognitoModeAvailability, availability);
+}
+
+void MdBookmarksBrowserTest::HandleSetIncognitoAvailability(
+    const base::ListValue* args) {
+  AllowJavascript();
+
+  ASSERT_EQ(2U, args->GetSize());
+  const base::Value* callback_id;
+  ASSERT_TRUE(args->Get(0, &callback_id));
+  int pref_value;
+  ASSERT_TRUE(args->GetInteger(1, &pref_value));
+
+  SetIncognitoAvailability(pref_value);
+
+  ResolveJavascriptCallback(*callback_id, base::Value());
+}
+
+content::WebUIMessageHandler* MdBookmarksBrowserTest::GetMockMessageHandler() {
+  return this;
+}
diff --git a/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_browsertest.h b/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_browsertest.h
new file mode 100644
index 0000000..4fd3d57
--- /dev/null
+++ b/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_browsertest.h
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MD_BOOKMARKS_MD_BOOKMARKS_BROWSERTEST_H_
+#define CHROME_BROWSER_UI_WEBUI_MD_BOOKMARKS_MD_BOOKMARKS_BROWSERTEST_H_
+
+#include "chrome/test/base/web_ui_browser_test.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+class MdBookmarksBrowserTest : public WebUIBrowserTest,
+                               public content::WebUIMessageHandler {
+ public:
+  MdBookmarksBrowserTest();
+  ~MdBookmarksBrowserTest() override;
+
+  void SetIncognitoAvailability(int availability);
+
+ private:
+  void HandleSetIncognitoAvailability(const base::ListValue* args);
+
+  // content::WebUIMessageHandler:
+  void RegisterMessages() override;
+
+  // WebUIBrowserTest:
+  content::WebUIMessageHandler* GetMockMessageHandler() override;
+
+  DISALLOW_COPY_AND_ASSIGN(MdBookmarksBrowserTest);
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_MD_BOOKMARKS_MD_BOOKMARKS_BROWSERTEST_H_
diff --git a/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc b/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc
index a7e62da5..76d0fec 100644
--- a/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc
+++ b/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc
@@ -5,9 +5,11 @@
 #include "chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.h"
 
 #include <algorithm>
+#include <string>
 
 #include "base/strings/string16.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/md_bookmarks/bookmarks_message_handler.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/browser_resources.h"
@@ -152,6 +154,8 @@
   Profile* profile = Profile::FromWebUI(web_ui);
   content::WebUIDataSource::Add(profile,
                                 CreateMdBookmarksUIHTMLSource(profile));
+
+  web_ui->AddMessageHandler(base::MakeUnique<BookmarksMessageHandler>());
 }
 
 // static
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 008760be..21c8ad2 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -1899,6 +1899,8 @@
   html_source->AddBoolean("enableSiteSettings",
                           base::CommandLine::ForCurrentProcess()->HasSwitch(
                               switches::kEnableSiteSettings));
+  html_source->AddBoolean("enableSiteDetails",
+                          base::FeatureList::IsEnabled(features::kSiteDetails));
   html_source->AddBoolean(
       "enableSafeBrowsingSubresourceFilter",
       base::FeatureList::IsEnabled(
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index ea8be8c..82bd662 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -82,6 +82,8 @@
 const char kChromeUIRestartURL[] = "chrome://restart/";
 const char kChromeUISettingsURL[] = "chrome://settings/";
 const char kChromeUIContentSettingsURL[] = "chrome://settings/content";
+const char kChromeUISiteDetailsPrefixURL[] =
+    "chrome://settings/content/siteDetails?site=";
 const char kChromeUISettingsFrameURL[] = "chrome://settings-frame/";
 const char kChromeUISigninEmailConfirmationURL[] =
     "chrome://signin-email-confirmation";
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index caade9df..a9f875a 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -79,6 +79,7 @@
 extern const char kChromeUISettingsFrameURL[];
 extern const char kChromeUISigninEmailConfirmationURL[];
 extern const char kChromeUISigninErrorURL[];
+extern const char kChromeUISiteDetailsPrefixURL[];
 extern const char kChromeUISiteEngagementHost[];
 extern const char kChromeUISuggestionsURL[];
 extern const char kChromeUISupervisedUserPassphrasePageURL[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 9e73427..603fa20 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -624,7 +624,6 @@
           "../browser/ui/views/location_bar/location_icon_view_interactive_uitest.cc",
           "../browser/ui/views/location_bar/star_view_browsertest.cc",
           "../browser/ui/views/omnibox/omnibox_view_views_browsertest.cc",
-          "../browser/ui/views/page_info/page_info_bubble_view_interactive_uitest.cc",
           "../browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc",
           "../browser/ui/views/ssl_client_certificate_selector_browsertest.cc",
           "../browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc",
@@ -1711,6 +1710,8 @@
       "../browser/ui/webui/inspect_ui_browsertest.cc",
       "../browser/ui/webui/interstitials/interstitial_ui_browsertest.cc",
       "../browser/ui/webui/log_web_ui_url_browsertest.cc",
+      "../browser/ui/webui/md_bookmarks/md_bookmarks_browsertest.cc",
+      "../browser/ui/webui/md_bookmarks/md_bookmarks_browsertest.h",
       "../browser/ui/webui/media_router/media_router_dialog_controller_impl_browsertest.cc",
       "../browser/ui/webui/net_internals/net_internals_ui_browsertest.cc",
       "../browser/ui/webui/net_internals/net_internals_ui_browsertest.h",
@@ -2098,6 +2099,7 @@
           "../browser/ui/views/frame/browser_view_browsertest.cc",
           "../browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc",
           "../browser/ui/views/media_router/media_router_ui_browsertest.cc",
+          "../browser/ui/views/page_info/page_info_bubble_view_browsertest.cc",
           "../browser/ui/views/passwords/password_dialog_view_browsertest.cc",
           "../browser/ui/views/session_crashed_bubble_view_browsertest.cc",
           "../browser/ui/views/task_manager_view_browsertest.cc",
diff --git a/chrome/test/data/webui/md_bookmarks/md_bookmarks_browsertest.js b/chrome/test/data/webui/md_bookmarks/md_bookmarks_browsertest.js
index d36ae63..7b89786 100644
--- a/chrome/test/data/webui/md_bookmarks/md_bookmarks_browsertest.js
+++ b/chrome/test/data/webui/md_bookmarks/md_bookmarks_browsertest.js
@@ -10,6 +10,8 @@
 GEN_INCLUDE(
     [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
 GEN('#include "base/command_line.h"');
+GEN('#include "chrome/browser/prefs/incognito_mode_prefs.h"');
+GEN('#include "chrome/browser/ui/webui/md_bookmarks/md_bookmarks_browsertest.h"');
 
 function MaterialBookmarksBrowserTest() {}
 
@@ -21,6 +23,8 @@
   commandLineSwitches: [{switchName: 'enable-features',
                          switchValue: 'MaterialDesignBookmarks'}],
 
+  typedefCppFixture: 'MdBookmarksBrowserTest',
+
   extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
     'test_command_manager.js',
     'test_store.js',
@@ -168,6 +172,24 @@
   mocha.run();
 });
 
+function MaterialBookmarksPolicyTest() {}
+
+MaterialBookmarksPolicyTest.prototype = {
+  __proto__: MaterialBookmarksBrowserTest.prototype,
+
+  testGenPreamble: function() {
+    GEN('SetIncognitoAvailability(IncognitoModePrefs::DISABLED);');
+  },
+
+  extraLibraries: MaterialBookmarksBrowserTest.prototype.extraLibraries.concat([
+    'policy_test.js',
+  ]),
+};
+
+TEST_F('MaterialBookmarksPolicyTest', 'All', function() {
+  mocha.run();
+});
+
 function MaterialBookmarksStoreClientTest() {}
 
 MaterialBookmarksStoreClientTest.prototype = {
diff --git a/chrome/test/data/webui/md_bookmarks/policy_test.js b/chrome/test/data/webui/md_bookmarks/policy_test.js
new file mode 100644
index 0000000..f04fe3df3
--- /dev/null
+++ b/chrome/test/data/webui/md_bookmarks/policy_test.js
@@ -0,0 +1,45 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+suite('Incognito policy', function() {
+  var store;
+  var app;
+
+  setup(function() {
+    store = new bookmarks.TestStore({
+      nodes: testTree(createFolder(
+          '1',
+          [
+            createItem('11'),
+          ])),
+      selectedFolder: '1',
+    });
+    store.setReducersEnabled(true);
+    store.expectAction('set-incognito-availability');
+    bookmarks.Store.instance_ = store;
+
+    app = document.createElement('bookmarks-app');
+    replaceBody(app);
+  });
+
+  test('updates when changed by the browser', function() {
+    var commandManager = bookmarks.CommandManager.getInstance();
+    // Incognito is disabled during testGenPreamble(). Wait for the front-end to
+    // load the config.
+    return store.waitForAction('set-incognito-availability').then(action => {
+      assertEquals(IncognitoAvailability.DISABLED,
+          store.data.prefs.incognitoAvailability);
+      assertFalse(
+          commandManager.canExecute(Command.OPEN_INCOGNITO, new Set(['11'])));
+
+      return cr.sendWithPromise(
+          'testSetIncognito', IncognitoAvailability.ENABLED);
+    }).then(() => {
+      assertEquals(IncognitoAvailability.ENABLED,
+          store.data.prefs.incognitoAvailability);
+      assertTrue(
+          commandManager.canExecute(Command.OPEN_INCOGNITO, new Set(['11'])));
+    });
+  });
+});
diff --git a/chrome/test/data/webui/md_bookmarks/test_store.js b/chrome/test/data/webui/md_bookmarks/test_store.js
index 10fab15..cdf2571c 100644
--- a/chrome/test/data/webui/md_bookmarks/test_store.js
+++ b/chrome/test/data/webui/md_bookmarks/test_store.js
@@ -12,6 +12,8 @@
       this.lastAction_ = null;
       this.acceptInit_ = false;
       this.enableReducers_ = false;
+      /** @type {!Map<string, !PromiseResolver>} */
+      this.resolverMap_ = new Map();
     };
 
     TestStore.prototype = {
@@ -54,6 +56,8 @@
         this.lastAction_ = action;
         if (this.enableReducers_)
           bookmarks.Store.prototype.reduce_.call(this, action);
+        if (this.resolverMap_.has(action.name))
+          this.resolverMap_.get(action.name).resolve(action);
       },
 
       /**
@@ -71,6 +75,32 @@
         this.acceptInit_ = true;
         this.initialized_ = false;
       },
+
+      /**
+       * Track actions called |name|, allowing that type of action to be waited
+       * for with `waitForAction`.
+       * @param {string} name
+       */
+      expectAction: function(name) {
+        this.resolverMap_.set(name, new PromiseResolver());
+      },
+
+      /**
+       * Returns a Promise that will resolve when an action called |name| is
+       * dispatched. The promise must be prepared by calling
+       * `expectAction(name)` before the action is dispatched.
+       * @param {string} name
+       * @return {!Promise<!Action>}
+       */
+      waitForAction: function(name) {
+        assertTrue(
+            this.resolverMap_.has(name),
+            'Must call expectAction before each call to waitForAction');
+        return this.resolverMap_.get(name).promise.then((action) => {
+          this.resolverMap_.delete(name);
+          return action;
+        });
+      },
     };
 
     return {
diff --git a/components/arc/common/file_system.mojom b/components/arc/common/file_system.mojom
index 24e4041..aa45972 100644
--- a/components/arc/common/file_system.mojom
+++ b/components/arc/common/file_system.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Next MinVersion: 4
+// Next MinVersion: 5
 
 module arc.mojom;
 
@@ -51,7 +51,7 @@
   [MinVersion=3] OnDocumentChanged@0(int64 watcher_id, ChangeType type);
 };
 
-// Next method ID: 8
+// Next method ID: 9
 interface FileSystemInstance {
   // Notes about Android Documents Provider:
   //
@@ -105,6 +105,10 @@
   // streams), -1 is returned.
   [MinVersion=1] GetFileSize@1(string url) => (int64 size);
 
+  // Asks the ContentResolver to get the MIME type of the file specified by the
+  // URL. When an error occurs, returns null value.
+  [MinVersion=4] GetMimeType@8(string url) => (string? mime_type);
+
   // Establishes full-duplex communication with the host.
   [MinVersion=3] Init@5(FileSystemHost host_ptr);
 
diff --git a/components/arc/test/fake_file_system_instance.cc b/components/arc/test/fake_file_system_instance.cc
index f522024..88eabd9 100644
--- a/components/arc/test/fake_file_system_instance.cc
+++ b/components/arc/test/fake_file_system_instance.cc
@@ -64,8 +64,9 @@
 
 FakeFileSystemInstance::File::File(const std::string& url,
                                    const std::string& content,
+                                   const std::string& mime_type,
                                    Seekable seekable)
-    : url(url), content(content), seekable(seekable) {}
+    : url(url), content(content), mime_type(mime_type), seekable(seekable) {}
 
 FakeFileSystemInstance::File::~File() = default;
 
@@ -170,6 +171,20 @@
       FROM_HERE, base::Bind(callback, file.content.size()));
 }
 
+void FakeFileSystemInstance::GetMimeType(const std::string& url,
+                                         const GetMimeTypeCallback& callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  auto iter = files_.find(url);
+  if (iter == files_.end()) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(callback, base::nullopt));
+    return;
+  }
+  const File& file = iter->second;
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(callback, file.mime_type));
+}
+
 void FakeFileSystemInstance::OpenFileToRead(
     const std::string& url,
     const OpenFileToReadCallback& callback) {
diff --git a/components/arc/test/fake_file_system_instance.h b/components/arc/test/fake_file_system_instance.h
index b852244..0d84eba 100644
--- a/components/arc/test/fake_file_system_instance.h
+++ b/components/arc/test/fake_file_system_instance.h
@@ -29,6 +29,7 @@
 //
 // Content URL based functions are:
 // - GetFileSize()
+// - GetMimeType()
 // - OpenFileToRead()
 // Fake files for those functions can be set up by AddFile().
 //
@@ -56,10 +57,16 @@
     // The content of a file.
     std::string content;
 
+    // The MIME type of a file.
+    std::string mime_type;
+
     // Whether this file is seekable or not.
     Seekable seekable;
 
-    File(const std::string& url, const std::string& content, Seekable seekable);
+    File(const std::string& url,
+         const std::string& content,
+         const std::string& mime_type,
+         Seekable seekable);
     File(const File& that);
     ~File();
   };
@@ -130,6 +137,8 @@
                    const GetDocumentCallback& callback) override;
   void GetFileSize(const std::string& url,
                    const GetFileSizeCallback& callback) override;
+  void GetMimeType(const std::string& url,
+                   const GetMimeTypeCallback& callback) override;
   void Init(mojom::FileSystemHostPtr host) override;
   void OpenFileToRead(const std::string& url,
                       const OpenFileToReadCallback& callback) override;
diff --git a/components/drive/service/drive_api_service.cc b/components/drive/service/drive_api_service.cc
index fd0a027..b62a058f 100644
--- a/components/drive/service/drive_api_service.cc
+++ b/components/drive/service/drive_api_service.cc
@@ -17,6 +17,7 @@
 #include "google_apis/drive/base_requests.h"
 #include "google_apis/drive/drive_api_parser.h"
 #include "google_apis/drive/drive_api_requests.h"
+#include "google_apis/drive/drive_switches.h"
 #include "google_apis/drive/files_list_request_runner.h"
 #include "google_apis/drive/request_sender.h"
 #include "google_apis/google_api_keys.h"
@@ -39,6 +40,7 @@
 using google_apis::FileListCallback;
 using google_apis::FileResource;
 using google_apis::FileResourceCallback;
+using google_apis::FilesListCorpora;
 using google_apis::FilesListRequestRunner;
 using google_apis::GetContentCallback;
 using google_apis::GetShareUrlCallback;
@@ -347,6 +349,10 @@
   request->set_max_results(kMaxNumFilesResourcePerRequest);
   request->set_q("trashed = false");  // Exclude trashed files.
   request->set_fields(kFileListFields);
+  if (google_apis::GetTeamDrivesIntegrationSwitch() ==
+      google_apis::TEAM_DRIVES_INTEGRATION_ENABLED) {
+    request->set_corpora(google_apis::FilesListCorpora::ALL_TEAM_DRIVES);
+  }
   return sender_->StartRequestWithAuthRetry(std::move(request));
 }
 
@@ -357,6 +363,15 @@
   DCHECK(!directory_resource_id.empty());
   DCHECK(!callback.is_null());
 
+  // TODO(yamaguchi): Use FileListScope::CreateForTeamDrive instead of
+  // kAllTeamDrives for efficiency. It'll require to add a new parameter to tell
+  // which team drive the directory resource belongs to.
+  FilesListCorpora corpora =
+      (google_apis::GetTeamDrivesIntegrationSwitch() ==
+       google_apis::TEAM_DRIVES_INTEGRATION_ENABLED)
+          ? google_apis::FilesListCorpora::ALL_TEAM_DRIVES
+          : google_apis::FilesListCorpora::DEFAULT;
+
   // Because children.list method on Drive API v2 returns only the list of
   // children's references, but we need all file resource list.
   // So, here we use files.list method instead, with setting parents query.
@@ -365,7 +380,7 @@
   // to client side.
   // We aren't interested in files in trash in this context, neither.
   return files_list_request_runner_->CreateAndStartWithSizeBackoff(
-      kMaxNumFilesResourcePerRequest,
+      kMaxNumFilesResourcePerRequest, corpora, std::string(),
       base::StringPrintf(
           "'%s' in parents and trashed = false",
           util::EscapeQueryStringValue(directory_resource_id).c_str()),
@@ -379,8 +394,14 @@
   DCHECK(!search_query.empty());
   DCHECK(!callback.is_null());
 
+  FilesListCorpora corpora =
+      (google_apis::GetTeamDrivesIntegrationSwitch() ==
+       google_apis::TEAM_DRIVES_INTEGRATION_ENABLED)
+          ? google_apis::FilesListCorpora::ALL_TEAM_DRIVES
+          : google_apis::FilesListCorpora::DEFAULT;
+
   return files_list_request_runner_->CreateAndStartWithSizeBackoff(
-      kMaxNumFilesResourcePerRequestForSearch,
+      kMaxNumFilesResourcePerRequestForSearch, corpora, std::string(),
       util::TranslateQuery(search_query), kFileListFields, callback);
 }
 
diff --git a/content/browser/bluetooth/bluetooth_device_chooser_controller.cc b/content/browser/bluetooth/bluetooth_device_chooser_controller.cc
index 5c35cc36..ed7de74 100644
--- a/content/browser/bluetooth/bluetooth_device_chooser_controller.cc
+++ b/content/browser/bluetooth/bluetooth_device_chooser_controller.cc
@@ -491,6 +491,7 @@
 }
 
 void BluetoothDeviceChooserController::PopulateConnectedDevices() {
+  // TODO(crbug.com/728897): Use RetrieveGattConnectedDevices once implemented.
   for (const device::BluetoothDevice* device : adapter_->GetDevices()) {
     if (device->IsGattConnected()) {
       AddFilteredDevice(*device);
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 264a1b2..cb79637 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -180,6 +180,25 @@
   return trace_id;
 }
 
+void OnEventDispatcherConnectionError(
+    base::WeakPtr<EmbeddedWorkerInstance> embedded_worker) {
+  if (!embedded_worker)
+    return;
+
+  switch (embedded_worker->status()) {
+    case EmbeddedWorkerStatus::STARTING:
+    case EmbeddedWorkerStatus::RUNNING:
+      // In this case the disconnection might be happening because of sudden
+      // renderer shutdown like crash.
+      embedded_worker->Detach();
+      break;
+    case EmbeddedWorkerStatus::STOPPING:
+    case EmbeddedWorkerStatus::STOPPED:
+      // Do nothing
+      break;
+  }
+}
+
 }  // namespace
 
 const int ServiceWorkerVersion::kTimeoutTimerDelaySeconds = 30;
@@ -1440,6 +1459,8 @@
       std::move(params), mojo::MakeRequest(&event_dispatcher_),
       base::Bind(&ServiceWorkerVersion::OnStartSentAndScriptEvaluated,
                  weak_factory_.GetWeakPtr()));
+  event_dispatcher_.set_connection_error_handler(base::Bind(
+      &OnEventDispatcherConnectionError, embedded_worker_->AsWeakPtr()));
 }
 
 void ServiceWorkerVersion::StartTimeoutTimer() {
diff --git a/content/browser/service_worker/service_worker_version_unittest.cc b/content/browser/service_worker/service_worker_version_unittest.cc
index 5dc91dc..8bd2ee2 100644
--- a/content/browser/service_worker/service_worker_version_unittest.cc
+++ b/content/browser/service_worker/service_worker_version_unittest.cc
@@ -282,12 +282,15 @@
         // Prepare for OnStopWorker().
         instance_host_ptr_map_[embedded_worker_id].Bind(
             std::move(instance_host));
-        break;  // Do nothing.
+        // Just keep the connection alive.
+        event_dispatcher_request_map_[embedded_worker_id] = std::move(request);
+        break;
       case StartMode::FAIL:
         ASSERT_EQ(current_mock_instance_index_ + 1,
                   mock_instance_clients()->size());
         // Remove the connection by peer
         mock_instance_clients()->at(current_mock_instance_index_).reset();
+        std::move(request);
         break;
       case StartMode::SUCCEED:
         MessageReceiver::OnStartWorker(
@@ -317,6 +320,9 @@
       int /* embedded_worker_id */,
       mojom::EmbeddedWorkerInstanceHostAssociatedPtr /* instance_host_ptr */>
       instance_host_ptr_map_;
+  std::map<int /* embedded_worker_id */,
+           mojom::ServiceWorkerEventDispatcherRequest>
+      event_dispatcher_request_map_;
   DISALLOW_COPY_AND_ASSIGN(MessageReceiverDisallowStart);
 };
 
diff --git a/content/shell/test_runner/app_banner_service.cc b/content/shell/test_runner/app_banner_service.cc
index f912715..2437435a 100644
--- a/content/shell/test_runner/app_banner_service.cc
+++ b/content/shell/test_runner/app_banner_service.cc
@@ -33,7 +33,7 @@
                             base::Unretained(this), callback));
 }
 
-void AppBannerService::DisplayAppBanner() { /* do nothing */
+void AppBannerService::DisplayAppBanner(bool user_gesture) { /* do nothing */
 }
 
 void AppBannerService::OnBannerPromptReply(
diff --git a/content/shell/test_runner/app_banner_service.h b/content/shell/test_runner/app_banner_service.h
index 31a31133..9e040d7 100644
--- a/content/shell/test_runner/app_banner_service.h
+++ b/content/shell/test_runner/app_banner_service.h
@@ -30,7 +30,7 @@
                                const base::Callback<void(bool)>& callback);
 
   // blink::mojom::AppBannerService overrides.
-  void DisplayAppBanner() override;
+  void DisplayAppBanner(bool user_gesture) override;
 
  private:
   void OnBannerPromptReply(const base::Callback<void(bool)>& callback,
diff --git a/device/bluetooth/public/interfaces/test/fake_bluetooth.mojom b/device/bluetooth/public/interfaces/test/fake_bluetooth.mojom
index a9d71fa..98da605 100644
--- a/device/bluetooth/public/interfaces/test/fake_bluetooth.mojom
+++ b/device/bluetooth/public/interfaces/test/fake_bluetooth.mojom
@@ -30,6 +30,10 @@
   SimulateCentral(CentralState state) => (FakeCentral fake_central);
 };
 
+// HCI Error Codes from BT 4.2 Vol 2 Part D 1.3 List Of Error Codes.
+const uint16 kHCISuccess = 0x0000;
+const uint16 kHCIConnectionTimeout = 0x0008;
+
 // FakeCentral allows clients to simulate events that a device in the
 // Central/Observer role would receive as well as monitor the operations
 // performed by the device in the Central/Observer role.
@@ -48,7 +52,20 @@
   // connected to the system or weren't connected through the UA e.g. a user
   // connected a peripheral through the system's settings. This method is
   // intended to simulate peripherals that those methods would return.
+  // Even though these devices are already connected to the OS, clients
+  // need to call the respective connect functions to signal they intend to keep
+  // the connection alive.
   SimulatePreconnectedPeripheral(string address,
                                  string name,
                                  array<UUID> known_service_uuids) => ();
+
+  // Sets the next GATT Connection request response for peripheral with
+  // |address| to |code|. |code| could be an HCI Error Code from
+  // BT 4.2 Vol 2 Part D 1.3 List Of Error Codes or a number outside that range
+  // returned by specific platforms e.g. Android returns 0x101 to signal a GATT
+  // failure
+  // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#GATT_FAILURE
+  // Calls callback with false if there was any error when simulating the next
+  // response.
+  SetNextGATTConnectionResponse(string address, uint16 code) => (bool success);
 };
diff --git a/device/bluetooth/test/fake_central.cc b/device/bluetooth/test/fake_central.cc
index 8bc994f..2b11378 100644
--- a/device/bluetooth/test/fake_central.cc
+++ b/device/bluetooth/test/fake_central.cc
@@ -37,13 +37,29 @@
   FakePeripheral* fake_peripheral =
       static_cast<FakePeripheral*>(device_iter->second.get());
   fake_peripheral->SetName(name);
-  fake_peripheral->SetGattConnected(true);
+  fake_peripheral->SetSystemConnected(true);
   fake_peripheral->SetServiceUUIDs(device::BluetoothDevice::UUIDSet(
       known_service_uuids.begin(), known_service_uuids.end()));
 
   std::move(callback).Run();
 }
 
+void FakeCentral::SetNextGATTConnectionResponse(
+    const std::string& address,
+    uint16_t code,
+    SetNextGATTConnectionResponseCallback callback) {
+  auto device_iter = devices_.find(address);
+  if (device_iter == devices_.end()) {
+    std::move(callback).Run(false);
+    return;
+  }
+
+  FakePeripheral* fake_peripheral =
+      static_cast<FakePeripheral*>(device_iter->second.get());
+  fake_peripheral->SetNextGATTConnectionResponse(code);
+  std::move(callback).Run(true);
+}
+
 std::string FakeCentral::GetAddress() const {
   NOTREACHED();
   return std::string();
diff --git a/device/bluetooth/test/fake_central.h b/device/bluetooth/test/fake_central.h
index 93fbd93..4b096eca 100644
--- a/device/bluetooth/test/fake_central.h
+++ b/device/bluetooth/test/fake_central.h
@@ -29,6 +29,10 @@
       const std::string& name,
       const std::vector<device::BluetoothUUID>& known_service_uuids,
       SimulatePreconnectedPeripheralCallback callback) override;
+  void SetNextGATTConnectionResponse(
+      const std::string& address,
+      uint16_t code,
+      SetNextGATTConnectionResponseCallback) override;
 
   // BluetoothAdapter overrides:
   std::string GetAddress() const override;
diff --git a/device/bluetooth/test/fake_peripheral.cc b/device/bluetooth/test/fake_peripheral.cc
index acf93c73..a8524e2 100644
--- a/device/bluetooth/test/fake_peripheral.cc
+++ b/device/bluetooth/test/fake_peripheral.cc
@@ -4,13 +4,17 @@
 
 #include "device/bluetooth/test/fake_peripheral.h"
 
+#include "base/memory/weak_ptr.h"
+
 namespace bluetooth {
 
 FakePeripheral::FakePeripheral(FakeCentral* fake_central,
                                const std::string& address)
     : device::BluetoothDevice(fake_central),
       address_(address),
-      gatt_connected_(false) {}
+      system_connected_(false),
+      gatt_connected_(false),
+      weak_ptr_factory_(this) {}
 
 FakePeripheral::~FakePeripheral() {}
 
@@ -18,14 +22,20 @@
   name_ = std::move(name);
 }
 
-void FakePeripheral::SetGattConnected(bool connected) {
-  gatt_connected_ = connected;
+void FakePeripheral::SetSystemConnected(bool connected) {
+  system_connected_ = connected;
 }
 
 void FakePeripheral::SetServiceUUIDs(UUIDSet service_uuids) {
   service_uuids_ = std::move(service_uuids);
 }
 
+void FakePeripheral::SetNextGATTConnectionResponse(uint16_t code) {
+  DCHECK(!next_connection_response_);
+  DCHECK(create_gatt_connection_error_callbacks_.empty());
+  next_connection_response_ = code;
+}
+
 uint32_t FakePeripheral::GetBluetoothClass() const {
   NOTREACHED();
   return 0;
@@ -92,7 +102,10 @@
 }
 
 bool FakePeripheral::IsGattConnected() const {
-  return gatt_connected_;
+  // TODO(crbug.com/728870): Return gatt_connected_ only once system connected
+  // peripherals are supported and Web Bluetooth uses them. See issue for more
+  // details.
+  return system_connected_ || gatt_connected_;
 }
 
 bool FakePeripheral::IsConnectable() const {
@@ -184,11 +197,43 @@
   NOTREACHED();
 }
 
+void FakePeripheral::CreateGattConnection(
+    const GattConnectionCallback& callback,
+    const ConnectErrorCallback& error_callback) {
+  create_gatt_connection_success_callbacks_.push_back(callback);
+  create_gatt_connection_error_callbacks_.push_back(error_callback);
+
+  // TODO(crbug.com/728870): Stop overriding CreateGattConnection once
+  // IsGattConnected() is fixed. See issue for more details.
+  if (gatt_connected_)
+    return DidConnectGatt();
+
+  CreateGattConnectionImpl();
+}
+
 void FakePeripheral::CreateGattConnectionImpl() {
-  NOTREACHED();
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::Bind(&FakePeripheral::DispatchConnectionResponse,
+                            weak_ptr_factory_.GetWeakPtr()));
+}
+
+void FakePeripheral::DispatchConnectionResponse() {
+  DCHECK(next_connection_response_);
+
+  uint16_t code = next_connection_response_.value();
+  next_connection_response_.reset();
+
+  if (code == mojom::kHCISuccess) {
+    gatt_connected_ = true;
+    DidConnectGatt();
+  } else if (code == mojom::kHCIConnectionTimeout) {
+    DidFailToConnectGatt(ERROR_FAILED);
+  } else {
+    DidFailToConnectGatt(ERROR_UNKNOWN);
+  }
 }
 
 void FakePeripheral::DisconnectGatt() {
-  NOTREACHED();
 }
+
 }  // namespace bluetooth
diff --git a/device/bluetooth/test/fake_peripheral.h b/device/bluetooth/test/fake_peripheral.h
index 8e6222234..b105c4f1 100644
--- a/device/bluetooth/test/fake_peripheral.h
+++ b/device/bluetooth/test/fake_peripheral.h
@@ -8,6 +8,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/test/fake_central.h"
@@ -24,13 +25,20 @@
   // Changes the name of the device.
   void SetName(base::Optional<std::string> name);
 
-  // Set it to indicate if the Peripheral is connected or not.
-  void SetGattConnected(bool gatt_connected);
+  // Set it to indicate if the system has connected to the Peripheral outside of
+  // the Bluetooth interface e.g. the user connected to the device through
+  // system settings.
+  void SetSystemConnected(bool gatt_connected);
 
   // Updates the peripheral's UUIDs that are returned by
   // BluetoothDevice::GetUUIDs().
   void SetServiceUUIDs(UUIDSet service_uuids);
 
+  // If |code| is kHCISuccess calls a pending success callback for
+  // CreateGattConnection. Otherwise calls a pending error callback
+  // with the ConnectErrorCode corresponding to |code|.
+  void SetNextGATTConnectionResponse(uint16_t code);
+
   // BluetoothDevice overrides:
   uint32_t GetBluetoothClass() const override;
 #if defined(OS_CHROMEOS) || defined(OS_LINUX)
@@ -78,16 +86,31 @@
       const device::BluetoothUUID& uuid,
       const ConnectToServiceCallback& callback,
       const ConnectToServiceErrorCallback& error_callback) override;
+  void CreateGattConnection(
+      const GattConnectionCallback& callback,
+      const ConnectErrorCallback& error_callback) override;
 
  protected:
   void CreateGattConnectionImpl() override;
   void DisconnectGatt() override;
 
  private:
+  void DispatchConnectionResponse();
+
   const std::string address_;
   base::Optional<std::string> name_;
-  bool gatt_connected_;
   UUIDSet service_uuids_;
+  // True when the system has connected to the device outside of the Bluetooth
+  // interface e.g. the user connected to the device through system settings.
+  bool system_connected_;
+  // True when this Bluetooth interface is connected to the device.
+  bool gatt_connected_;
+
+  // Used to decide which callback should be called when
+  // CreateGattConnection is called.
+  base::Optional<uint16_t> next_connection_response_;
+
+  base::WeakPtrFactory<FakePeripheral> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(FakePeripheral);
 };
diff --git a/google_apis/drive/drive_api_requests.cc b/google_apis/drive/drive_api_requests.cc
index 0c3c3e1..e9799c6 100644
--- a/google_apis/drive/drive_api_requests.cc
+++ b/google_apis/drive/drive_api_requests.cc
@@ -557,20 +557,21 @@
 
 //============================= FilesListRequest =============================
 
-FilesListRequest::FilesListRequest(
-    RequestSender* sender,
-    const DriveApiUrlGenerator& url_generator,
-    const FileListCallback& callback)
+FilesListRequest::FilesListRequest(RequestSender* sender,
+                                   const DriveApiUrlGenerator& url_generator,
+                                   const FileListCallback& callback)
     : DriveApiDataRequest<FileList>(sender, callback),
       url_generator_(url_generator),
-      max_results_(100) {
+      max_results_(100),
+      corpora_(FilesListCorpora::DEFAULT) {
   DCHECK(!callback.is_null());
 }
 
 FilesListRequest::~FilesListRequest() {}
 
 GURL FilesListRequest::GetURLInternal() const {
-  return url_generator_.GetFilesListUrl(max_results_, page_token_, q_);
+  return url_generator_.GetFilesListUrl(max_results_, page_token_, corpora_,
+                                        team_drive_id_, q_);
 }
 
 //======================== FilesListNextPageRequest =========================
diff --git a/google_apis/drive/drive_api_requests.h b/google_apis/drive/drive_api_requests.h
index 3649291..4cb9d6b 100644
--- a/google_apis/drive/drive_api_requests.h
+++ b/google_apis/drive/drive_api_requests.h
@@ -532,6 +532,14 @@
     page_token_ = page_token;
   }
 
+  FilesListCorpora corpora() const { return corpora_; }
+  void set_corpora(FilesListCorpora corpora) { corpora_ = corpora; }
+
+  const std::string& team_drive_id() const { return team_drive_id_; }
+  void set_team_drive_id(const std::string& team_drive_id) {
+    team_drive_id_ = team_drive_id;
+  }
+
   const std::string& q() const { return q_; }
   void set_q(const std::string& q) { q_ = q; }
 
@@ -543,6 +551,8 @@
   const DriveApiUrlGenerator url_generator_;
   int max_results_;
   std::string page_token_;
+  FilesListCorpora corpora_;
+  std::string team_drive_id_;
   std::string q_;
 
   DISALLOW_COPY_AND_ASSIGN(FilesListRequest);
diff --git a/google_apis/drive/drive_api_url_generator.cc b/google_apis/drive/drive_api_url_generator.cc
index e79bf0de..8daf59e 100644
--- a/google_apis/drive/drive_api_url_generator.cc
+++ b/google_apis/drive/drive_api_url_generator.cc
@@ -42,6 +42,11 @@
 
 const char kIncludeTeamDriveItems[] = "includeTeamDriveItems";
 const char kSupportsTeamDrives[] = "supportsTeamDrives";
+const char kCorpora[] = "corpora";
+const char kCorporaAllTeamDrives[] = "default,allTeamDrives";
+const char kCorporaDefault[] = "default";
+const char kCorporaTeamDrive[] = "teamDrive";
+const char kTeamDriveId[] = "teamDriveId";
 
 // apps.delete and file.authorize API is exposed through a special endpoint
 // v2internal that is accessible only by the official API key for Chrome.
@@ -59,6 +64,19 @@
   return net::AppendOrReplaceQueryParameter(url, "uploadType", "multipart");
 }
 
+const char* GetCorporaString(FilesListCorpora corpora) {
+  switch (corpora) {
+    case FilesListCorpora::DEFAULT:
+      return kCorporaDefault;
+    case FilesListCorpora::TEAM_DRIVE:
+      return kCorporaTeamDrive;
+    case FilesListCorpora::ALL_TEAM_DRIVES:
+      return kCorporaAllTeamDrives;
+  }
+  NOTREACHED();
+  return kCorporaDefault;
+}
+
 }  // namespace
 
 DriveApiUrlGenerator::DriveApiUrlGenerator(
@@ -185,12 +203,21 @@
 
 GURL DriveApiUrlGenerator::GetFilesListUrl(int max_results,
                                            const std::string& page_token,
+                                           FilesListCorpora corpora,
+                                           const std::string& team_drive_id,
                                            const std::string& q) const {
   GURL url = base_url_.Resolve(kDriveV2FilesUrl);
   if (enable_team_drives_) {
     url = net::AppendOrReplaceQueryParameter(url, kSupportsTeamDrives, "true");
-    url = net::AppendOrReplaceQueryParameter(url, kIncludeTeamDriveItems,
-                                             "true");
+    if (corpora != FilesListCorpora::DEFAULT) {
+      url = net::AppendOrReplaceQueryParameter(url, kIncludeTeamDriveItems,
+                                               "true");
+    }
+    url = net::AppendOrReplaceQueryParameter(url, kCorpora,
+                                             GetCorporaString(corpora));
+    if (!team_drive_id.empty())
+      url =
+          net::AppendOrReplaceQueryParameter(url, kTeamDriveId, team_drive_id);
   }
   // maxResults is 100 by default.
   if (max_results != 100) {
@@ -238,7 +265,7 @@
                                              "true");
     if (!team_drive_id.empty()) {
       url =
-          net::AppendOrReplaceQueryParameter(url, "teamDriveId", team_drive_id);
+          net::AppendOrReplaceQueryParameter(url, kTeamDriveId, team_drive_id);
     }
   }
   // includeDeleted is "true" by default.
diff --git a/google_apis/drive/drive_api_url_generator.h b/google_apis/drive/drive_api_url_generator.h
index 08991274..9e6efc8 100644
--- a/google_apis/drive/drive_api_url_generator.h
+++ b/google_apis/drive/drive_api_url_generator.h
@@ -15,6 +15,17 @@
 
 namespace google_apis {
 
+// This enum class is used to express a corpora parameter configuration for
+// Files:list.
+enum class FilesListCorpora {
+  // 'default': The user's subscribed items.
+  DEFAULT,
+  // 'teamDrives': A Team Drive.
+  TEAM_DRIVE,
+  // 'default,allTeamDrives': All Team Drives and the user's subscribed items.
+  ALL_TEAM_DRIVES
+};
+
 // This class is used to generate URLs for communicating with drive api
 // servers for production, and a local server for testing.
 class DriveApiUrlGenerator {
@@ -68,6 +79,8 @@
   // Returns a URL to fetch file list.
   GURL GetFilesListUrl(int max_results,
                        const std::string& page_token,
+                       FilesListCorpora corpora,
+                       const std::string& team_drive_id,
                        const std::string& q) const;
 
   // Returns a URL to delete a resource with the given |file_id|.
diff --git a/google_apis/drive/drive_api_url_generator_unittest.cc b/google_apis/drive/drive_api_url_generator_unittest.cc
index 43bc452..70be39b 100644
--- a/google_apis/drive/drive_api_url_generator_unittest.cc
+++ b/google_apis/drive/drive_api_url_generator_unittest.cc
@@ -204,24 +204,39 @@
   const std::string kV2FilesUrlPrefix =
       "https://www.example.com/drive/v2/files";
   const std::string kV2FilesUrlPrefixWithTeamDrives =
-      "https://www.example.com/drive/v2/files?"
-      "supportsTeamDrives=true&includeTeamDriveItems=true";
+      "https://www.example.com/drive/v2/files?supportsTeamDrives=true&"
+      "includeTeamDriveItems=true&corpora=default%2CallTeamDrives";
 
   for (size_t i = 0; i < arraysize(kTestPatterns); ++i) {
     EXPECT_EQ(kV2FilesUrlPrefix +
                   (kTestPatterns[i].expected_query.empty() ? "" : "?") +
                   kTestPatterns[i].expected_query,
-              url_generator_.GetFilesListUrl(kTestPatterns[i].max_results,
-                                             kTestPatterns[i].page_token,
-                                             kTestPatterns[i].q).spec());
+              url_generator_
+                  .GetFilesListUrl(kTestPatterns[i].max_results,
+                                   kTestPatterns[i].page_token,
+                                   FilesListCorpora::DEFAULT, std::string(),
+                                   kTestPatterns[i].q)
+                  .spec());
     EXPECT_EQ(kV2FilesUrlPrefixWithTeamDrives +
                   (kTestPatterns[i].expected_query.empty() ? "" : "&") +
                   kTestPatterns[i].expected_query,
-              team_drives_url_generator_.GetFilesListUrl(
-                  kTestPatterns[i].max_results,
-                  kTestPatterns[i].page_token,
-                  kTestPatterns[i].q).spec());
+              team_drives_url_generator_
+                  .GetFilesListUrl(kTestPatterns[i].max_results,
+                                   kTestPatterns[i].page_token,
+                                   FilesListCorpora::ALL_TEAM_DRIVES,
+                                   std::string(), kTestPatterns[i].q)
+                  .spec());
   }
+
+  EXPECT_EQ(
+      "https://www.example.com/drive/v2/files?supportsTeamDrives=true&"
+      "includeTeamDriveItems=true&corpora=teamDrive&"
+      "teamDriveId=TheTeamDriveId&q=query",
+      team_drives_url_generator_
+          .GetFilesListUrl(100, std::string() /* page_token */,
+                           FilesListCorpora::TEAM_DRIVE, "TheTeamDriveId",
+                           "query")
+          .spec());
 }
 
 TEST_F(DriveApiUrlGeneratorTest, GetFilesDeleteUrl) {
diff --git a/google_apis/drive/files_list_request_runner.cc b/google_apis/drive/files_list_request_runner.cc
index a7f29d6ea..3dde3d6c 100644
--- a/google_apis/drive/files_list_request_runner.cc
+++ b/google_apis/drive/files_list_request_runner.cc
@@ -29,6 +29,8 @@
 
 CancelCallback FilesListRequestRunner::CreateAndStartWithSizeBackoff(
     int max_results,
+    FilesListCorpora corpora,
+    const std::string& team_drive_id,
     const std::string& q,
     const std::string& fields,
     const FileListCallback& callback) {
@@ -39,8 +41,9 @@
       base::MakeUnique<drive::FilesListRequest>(
           request_sender_, url_generator_,
           base::Bind(&FilesListRequestRunner::OnCompleted,
-                     weak_ptr_factory_.GetWeakPtr(), max_results, q, fields,
-                     callback, base::Owned(cancel_callback)));
+                     weak_ptr_factory_.GetWeakPtr(), max_results, corpora,
+                     team_drive_id, q, fields, callback,
+                     base::Owned(cancel_callback)));
   request->set_max_results(max_results);
   request->set_q(q);
   request->set_fields(fields);
@@ -61,6 +64,8 @@
 }
 
 void FilesListRequestRunner::OnCompleted(int max_results,
+                                         FilesListCorpora corpora,
+                                         const std::string& team_drive_id,
                                          const std::string& q,
                                          const std::string& fields,
                                          const FileListCallback& callback,
@@ -74,7 +79,8 @@
       "Drive.FilesListRequestRunner.ApiErrorCode", error);
 
   if (error == google_apis::DRIVE_RESPONSE_TOO_LARGE && max_results > 1) {
-    CreateAndStartWithSizeBackoff(max_results / 2, q, fields, callback);
+    CreateAndStartWithSizeBackoff(max_results / 2, corpora, team_drive_id, q,
+                                  fields, callback);
     return;
   }
 
diff --git a/google_apis/drive/files_list_request_runner.h b/google_apis/drive/files_list_request_runner.h
index 788eb4d..a4993ceb 100644
--- a/google_apis/drive/files_list_request_runner.h
+++ b/google_apis/drive/files_list_request_runner.h
@@ -31,6 +31,8 @@
   // retry in case of DRIVE_RESPONSE_TOO_LARGE error code.
   CancelCallback CreateAndStartWithSizeBackoff(
       int max_results,
+      FilesListCorpora corpora,
+      const std::string& team_drive_id,
       const std::string& q,
       const std::string& fields,
       const FileListCallback& callback);
@@ -49,6 +51,8 @@
   // error. In case of DRIVE_RESPONSE_TOO_LARGE it will retry the request with
   // half of the requests.
   void OnCompleted(int max_results,
+                   FilesListCorpora corpora,
+                   const std::string& team_drive_id,
                    const std::string& q,
                    const std::string& fields,
                    const FileListCallback& callback,
diff --git a/google_apis/drive/files_list_request_runner_unittest.cc b/google_apis/drive/files_list_request_runner_unittest.cc
index 89e7f3a..926770e 100644
--- a/google_apis/drive/files_list_request_runner_unittest.cc
+++ b/google_apis/drive/files_list_request_runner_unittest.cc
@@ -139,7 +139,7 @@
 TEST_F(FilesListRequestRunnerTest, Success_NoBackoff) {
   SetFakeServerResponse(net::HTTP_OK, kSuccessResource);
   runner_->CreateAndStartWithSizeBackoff(
-      kMaxResults, kQuery, kFields,
+      kMaxResults, FilesListCorpora::DEFAULT, std::string(), kQuery, kFields,
       base::Bind(&FilesListRequestRunnerTest::OnCompleted,
                  base::Unretained(this)));
 
@@ -161,7 +161,7 @@
   SetFakeServerResponse(net::HTTP_INTERNAL_SERVER_ERROR,
                         kResponseTooLargeErrorResource);
   runner_->CreateAndStartWithSizeBackoff(
-      kMaxResults, kQuery, kFields,
+      kMaxResults, FilesListCorpora::DEFAULT, std::string(), kQuery, kFields,
       base::Bind(&FilesListRequestRunnerTest::OnCompleted,
                  base::Unretained(this)));
   {
@@ -199,7 +199,7 @@
   SetFakeServerResponse(net::HTTP_INTERNAL_SERVER_ERROR,
                         kResponseTooLargeErrorResource);
   runner_->CreateAndStartWithSizeBackoff(
-      kMaxResults, kQuery, kFields,
+      kMaxResults, FilesListCorpora::DEFAULT, std::string(), kQuery, kFields,
       base::Bind(&FilesListRequestRunnerTest::OnCompleted,
                  base::Unretained(this)));
   {
@@ -255,7 +255,7 @@
   SetFakeServerResponse(net::HTTP_INTERNAL_SERVER_ERROR,
                         kQuotaExceededErrorResource);
   runner_->CreateAndStartWithSizeBackoff(
-      kMaxResults, kQuery, kFields,
+      kMaxResults, FilesListCorpora::DEFAULT, std::string(), kQuery, kFields,
       base::Bind(&FilesListRequestRunnerTest::OnCompleted,
                  base::Unretained(this)));
 
diff --git a/services/preferences/README.md b/services/preferences/README.md
new file mode 100644
index 0000000..7831ca2
--- /dev/null
+++ b/services/preferences/README.md
@@ -0,0 +1,82 @@
+# Preference Service User Guide
+
+[TOC]
+
+## What is the Preference Service?
+
+Preferences, also known as "prefs", are key-value pairs stored by
+Chrome. Examples include the settings in chrome://settings, all per-extension
+metadata, the list of plugins and so on. Individual prefs are keyed by a string
+and have a type. E.g., the "browser.enable_spellchecking" pref stores a boolean
+indicating whether spell-checking is enabled.
+
+The prefs service persists prefs to disk and communicates updates to them
+between services, including Chrome itself. There's one service instance per
+profile and prefs are persisted on a pre-profile basis.
+
+## Using the service
+
+The service is used through a client library that offers clients fast and
+synchronous access to prefs. To connect to the pref service and start reading
+and writing prefs simply use the `ConnectToPrefService` factory function:
+
+``` cpp
+#include "services/preferences/public/cpp/pref_service_factory.h"
+
+class MyService : public service_manager::Service {
+  void OnStart() {
+    auto* connector = context()->connector();
+    auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>();
+    // Register any preferences you intend to use in |pref_registry|.
+    prefs::ConnectToPrefService(
+        connector, std::move(pref_registry), {},
+        base::Bind(&MyService::OnPrefServiceConnected, base::Unretained(this)));
+  }
+
+  void OnPrefServiceConnected(std::unique_ptr<::PrefService> pref_service) {
+    // Use |pref_service|.
+  }
+};
+```
+
+The returned `PrefService` class predates the Pref Service and its behavior
+hasn't changed (i.e. all existing documentation still applies).
+
+## Semantics
+
+Updates made on the `PrefService` object are reflected immediately in the
+originating service and eventually in all other services. In other words,
+updates are eventually consistent.
+
+## Registering your preferences
+
+Every pref should be owned by one service. The owning service provides the type
+and default value for that pref. Owned prefs can be registered as public,
+meaning other services can read and/or write them, or private (the default). Services
+that want to access a pref not owned by them must still register those prefs as
+"foreign" prefs. Registration happens through the `PrefRegistry` passed to
+`ConnectToPrefService`. For example:
+
+**`//services/my_service/my_service.cc`**
+``` cpp
+void MyService::OnStart() {
+  auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>();
+  pref_registry->RegisterIntegerPref(kKey, kInitialValue, PrefRegistry::PUBLIC);
+  prefs::ConnectToPrefService(...);
+}
+```
+
+**`//services/other_service/other_service.cc`**
+``` cpp
+void OtherService::OnStart() {
+  auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>();
+  pref_registry->RegisterForeignPref(kKey);
+  prefs::ConnectToPrefService(...);
+}
+```
+
+## Design
+
+The design doc is here:
+
+https://docs.google.com/document/d/1JU8QUWxMEXWMqgkvFUumKSxr7Z-nfq0YvreSJTkMVmU/edit?usp=sharing
diff --git a/services/preferences/pref_service_factory_unittest.cc b/services/preferences/pref_service_factory_unittest.cc
index 3fb34b7..02752863 100644
--- a/services/preferences/pref_service_factory_unittest.cc
+++ b/services/preferences/pref_service_factory_unittest.cc
@@ -174,23 +174,12 @@
   }
 
  private:
-  // Called when the PrefService has been initialized.
-  static void OnInit(const base::Closure& quit_closure, bool success) {
-    quit_closure.Run();
-  }
-
   // Called when the PrefService has been created.
   static void OnCreate(const base::Closure& quit_closure,
                        std::unique_ptr<PrefService>* out,
                        std::unique_ptr<PrefService> pref_service) {
     DCHECK(pref_service);
     *out = std::move(pref_service);
-    if ((*out)->GetInitializationStatus() ==
-        PrefService::INITIALIZATION_STATUS_WAITING) {
-      (*out)->AddPrefInitObserver(
-          base::Bind(PrefServiceFactoryTest::OnInit, quit_closure));
-      return;
-    }
     quit_closure.Run();
   }
 
diff --git a/services/preferences/public/cpp/pref_service_factory.cc b/services/preferences/public/cpp/pref_service_factory.cc
index eda3a20d..51d01de9 100644
--- a/services/preferences/public/cpp/pref_service_factory.cc
+++ b/services/preferences/public/cpp/pref_service_factory.cc
@@ -57,6 +57,16 @@
   return nullptr;
 }
 
+void OnPrefServiceInit(std::unique_ptr<PrefService> pref_service,
+                       ConnectCallback callback,
+                       bool success) {
+  if (success) {
+    callback.Run(std::move(pref_service));
+  } else {
+    callback.Run(nullptr);
+  }
+}
+
 void OnConnect(
     scoped_refptr<RefCountedInterfacePtr<mojom::PrefStoreConnector>>
         connector_ptr,
@@ -102,9 +112,23 @@
       managed_prefs.get(), supervised_user_prefs.get(), extension_prefs.get(),
       command_line_prefs.get(), user_pref_store.get(), recommended_prefs.get(),
       pref_registry->defaults().get(), pref_notifier);
-  callback.Run(base::MakeUnique<::PrefService>(
+  auto pref_service = base::MakeUnique<PrefService>(
       pref_notifier, pref_value_store, user_pref_store.get(),
-      pref_registry.get(), base::Bind(&DoNothingHandleReadError), true));
+      pref_registry.get(), base::Bind(&DoNothingHandleReadError), true);
+  switch (pref_service->GetInitializationStatus()) {
+    case PrefService::INITIALIZATION_STATUS_WAITING:
+      pref_service->AddPrefInitObserver(base::Bind(&OnPrefServiceInit,
+                                                   base::Passed(&pref_service),
+                                                   base::Passed(&callback)));
+      break;
+    case PrefService::INITIALIZATION_STATUS_SUCCESS:
+    case PrefService::INITIALIZATION_STATUS_CREATED_NEW_PREF_STORE:
+      callback.Run(std::move(pref_service));
+      break;
+    case PrefService::INITIALIZATION_STATUS_ERROR:
+      callback.Run(nullptr);
+      break;
+  }
   connector_ptr->reset();
 }
 
@@ -139,9 +163,9 @@
   auto serialized_pref_registry = SerializePrefRegistry(*pref_registry);
   connector_ptr->get()->Connect(
       std::move(serialized_pref_registry), std::move(already_connected_types),
-      base::Bind(&OnConnect, connector_ptr, base::Passed(&pref_registry),
-                 base::Passed(&local_layered_pref_stores),
-                 base::Passed(&callback)));
+      base::BindOnce(&OnConnect, connector_ptr, std::move(pref_registry),
+                     std::move(local_layered_pref_stores),
+                     std::move(callback)));
 }
 
 }  // namespace prefs
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/invalid-service-name.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/invalid-service-name.js
index fec4374..32544f0 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/invalid-service-name.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/invalid-service-name.js
@@ -1,12 +1,9 @@
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('HeartRateAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => device.gatt.connect())
-    .then(gatt => {
+  return getHealthThermometerDevice()
+    .then(([device]) => {
       return assert_promise_rejects_with_message(
-        gatt.CALLS([
+        device.gatt.CALLS([
           getPrimaryService('wrong_name')|
           getPrimaryServices('wrong_name')
         ]),
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-for-any-service.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-for-any-service.js
index 83580da7..e2e95c2e 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-for-any-service.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-for-any-service.js
@@ -1,11 +1,8 @@
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('HeartRateAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{name: 'Heart Rate Device'}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => assert_promise_rejects_with_message(
-        gattServer.CALLS([
+  return getHealthThermometerDevice({acceptAllDevices: true})
+    .then(([device]) => assert_promise_rejects_with_message(
+        device.gatt.CALLS([
           getPrimaryService('heart_rate')|
           getPrimaryServices()|
           getPrimaryServices('heart_rate')[UUID]]),
@@ -15,4 +12,3 @@
                          'SecurityError')));
 }, 'Request for present service without permission to access any service. ' +
    'Reject with SecurityError.');
-</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/connect/connection-succeeds.html b/third_party/WebKit/LayoutTests/bluetooth/server/connect/connection-succeeds.html
index f0d82e1..a5fb002 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/connect/connection-succeeds.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/connect/connection-succeeds.html
@@ -2,13 +2,16 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/bluetooth/web-bluetooth-test.js"></script>
+<script src="../../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('HeartRateAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => assert_true(gattServer.connected));
+  return getDiscoveredHealthThermometerDevice()
+    .then(([device, fake_peripheral]) => {
+      return fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS})
+        .then(() => device.gatt.connect())
+        .then(gatt => assert_true(gatt.connected));
+    });
 }, 'Device will connect');
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/connect/garbage-collection-ran-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/connect/garbage-collection-ran-during-success.html
index 00b5c0a..60156c1 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/connect/garbage-collection-ran-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/connect/garbage-collection-ran-during-success.html
@@ -2,16 +2,21 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/bluetooth/web-bluetooth-test.js"></script>
+<script src="../../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}]}))
-    .then(device => {
-      device.gatt.connect();
+  return getDiscoveredHealthThermometerDevice()
+    .then(([device, fake_peripheral]) => {
+      return fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS})
+        .then(() => {
+          // Don't return the promise and let |device| go out of scope
+          // so that it gets garbage collected.
+          device.gatt.connect();
+        });
     })
-    .then(runGarbageCollection);
+    .then(runGarbageCollection)
 }, 'Garbage Collection ran during a connect call that succeeds. ' +
    'Should not crash.');
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/connect/get-same-gatt-server.html b/third_party/WebKit/LayoutTests/bluetooth/server/connect/get-same-gatt-server.html
index 1c7774f..d6d1a42 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/connect/get-same-gatt-server.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/connect/get-same-gatt-server.html
@@ -2,16 +2,24 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/bluetooth/web-bluetooth-test.js"></script>
+<script src="../../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('HeartRateAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => {
-      return Promise.all([device.gatt.connect(), device.gatt.connect()])
-    }).then(gattServers => {
-      assert_equals(gattServers[0], gattServers[1]);
-    });
+  return getDiscoveredHealthThermometerDevice()
+    .then(([device, fake_peripheral]) => {
+      return fake_peripheral
+        .setNextGATTConnectionResponse({code: HCI_SUCCESS})
+        .then(() => device.gatt.connect())
+        .then(gatt1 => {
+          // No second response is necessary because an ATT Bearer
+          // already exists from the first connection.
+          // See https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connect
+          // step 5.1.
+          return device.gatt.connect().then(gatt2 => [gatt1, gatt2]);
+        });
+    })
+    .then(([gatt1, gatt2]) => assert_equals(gatt1, gatt2));
 }, 'Multiple connects should return the same gatt object.');
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/device-same-object.html b/third_party/WebKit/LayoutTests/bluetooth/server/device-same-object.html
index bbba6ddb..c6f391c 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/device-same-object.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/device-same-object.html
@@ -2,15 +2,18 @@
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
 <script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../resources/bluetooth/web-bluetooth-test.js"></script>
+<script src="../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('HeartRateAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      assert_equals(gattServer.device, gattServer.device);
+  return getDiscoveredHealthThermometerDevice()
+    .then(([device, fake_peripheral]) => {
+      return fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS})
+        .then(() => device.gatt.connect());
+    })
+    .then(gatt => {
+      assert_equals(gatt.device, gatt.device);
     });
 }, "[SameObject] test for BluetoothRemoteGATTServer's device.");
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-invalid-service-name.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-invalid-service-name.html
index aaa69c8..591757d1 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-invalid-service-name.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-invalid-service-name.html
@@ -8,13 +8,10 @@
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('HeartRateAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => device.gatt.connect())
-    .then(gatt => {
+  return getHealthThermometerDevice()
+    .then(([device]) => {
       return assert_promise_rejects_with_message(
-        gatt.getPrimaryService('wrong_name'),
+        device.gatt.getPrimaryService('wrong_name'),
         new DOMException(
           'Failed to execute \'getPrimaryService\' on ' +
           '\'BluetoothRemoteGATTServer\': Invalid Service name: ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.html
index c04a98f..d02d25d 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.html
@@ -8,18 +8,14 @@
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('HeartRateAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{name: 'Heart Rate Device'}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => assert_promise_rejects_with_message(
-        gattServer.getPrimaryService('heart_rate'),
+  return getHealthThermometerDevice({acceptAllDevices: true})
+    .then(([device]) => assert_promise_rejects_with_message(
+        device.gatt.getPrimaryService('heart_rate'),
         new DOMException('Origin is not allowed to access any service. Tip: ' +
                          'Add the service UUID to \'optionalServices\' in ' +
                          'requestDevice() options. https://goo.gl/HxfxSQ',
                          'SecurityError')));
 }, 'Request for present service without permission to access any service. ' +
    'Reject with SecurityError.');
-</script>
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-invalid-service-name.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-invalid-service-name.html
index 66c9a37..f8d010b 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-invalid-service-name.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-invalid-service-name.html
@@ -8,13 +8,10 @@
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('HeartRateAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => device.gatt.connect())
-    .then(gatt => {
+  return getHealthThermometerDevice()
+    .then(([device]) => {
       return assert_promise_rejects_with_message(
-        gatt.getPrimaryServices('wrong_name'),
+        device.gatt.getPrimaryServices('wrong_name'),
         new DOMException(
           'Failed to execute \'getPrimaryServices\' on ' +
           '\'BluetoothRemoteGATTServer\': Invalid Service name: ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.html
index 12b0ef9..c80c983 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.html
@@ -8,18 +8,14 @@
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('HeartRateAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{name: 'Heart Rate Device'}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => assert_promise_rejects_with_message(
-        gattServer.getPrimaryServices('heart_rate'),
+  return getHealthThermometerDevice({acceptAllDevices: true})
+    .then(([device]) => assert_promise_rejects_with_message(
+        device.gatt.getPrimaryServices('heart_rate'),
         new DOMException('Origin is not allowed to access any service. Tip: ' +
                          'Add the service UUID to \'optionalServices\' in ' +
                          'requestDevice() options. https://goo.gl/HxfxSQ',
                          'SecurityError')));
 }, 'Request for present service without permission to access any service. ' +
    'Reject with SecurityError.');
-</script>
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.html
index 0b19a37..c771e477 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.html
@@ -8,18 +8,14 @@
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('HeartRateAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{name: 'Heart Rate Device'}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => assert_promise_rejects_with_message(
-        gattServer.getPrimaryServices(),
+  return getHealthThermometerDevice({acceptAllDevices: true})
+    .then(([device]) => assert_promise_rejects_with_message(
+        device.gatt.getPrimaryServices(),
         new DOMException('Origin is not allowed to access any service. Tip: ' +
                          'Add the service UUID to \'optionalServices\' in ' +
                          'requestDevice() options. https://goo.gl/HxfxSQ',
                          'SecurityError')));
 }, 'Request for present service without permission to access any service. ' +
    'Reject with SecurityError.');
-</script>
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js b/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js
index 862950d..aae8a9b 100644
--- a/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js
+++ b/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js
@@ -1,5 +1,11 @@
 'use strict';
 
+// HCI Error Codes. Used for simulateGATT[Dis]ConnectionResponse.
+// For a complete list of possible error codes see
+// BT 4.2 Vol 2 Part D 1.3 List Of Error Codes.
+const HCI_SUCCESS = 0x0000;
+const HCI_CONNECTION_TIMEOUT = 0x0008;
+
 // Bluetooth UUID constants:
 // Services:
 var blocklist_test_service_uuid = "611c954a-263b-4f4a-aab6-01ddb953f985";
@@ -416,6 +422,8 @@
   }];
 }
 
+// Simulates a pre-connected device with |address|, |name| and
+// |knownServiceUUIDs|.
 function setUpPreconnectedDevice({
   address = '00:00:00:00:00:00', name = 'LE Device', knownServiceUUIDs = []}) {
   return navigator.bluetooth.test.simulateCentral({state: 'powered-on'})
@@ -426,6 +434,8 @@
     }));
 }
 
+// Returns an array containing two FakePeripherals corresponding
+// to the simulated devices.
 function setUpHealthThermometerAndHeartRateDevices() {
   return navigator.bluetooth.test.simulateCentral({state: 'powered-on'})
    .then(fake_central => Promise.all([
@@ -440,3 +450,35 @@
        knownServiceUUIDs: ['generic_access', 'heart_rate'],
      })]));
 }
+
+// Returns a BluetoothDevice discovered using |options| and its
+// corresponding FakePeripheral.
+// The simulated device is called 'Health Thermometer' it has two known service
+// UUIDs: 'generic_access' and 'health_thermometer'. The device has been
+// connected to and its services have been discovered.
+// TODO(crbug.com/719816): Add services, characteristics and descriptors,
+// and discover all the attributes.
+function getHealthThermometerDevice(options) {
+  return getDiscoveredHealthThermometerDevice(options)
+    .then(([device, fake_peripheral]) => {
+      return fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS})
+        .then(() => device.gatt.connect())
+        .then(gatt => [gatt.device, fake_peripheral]);
+    });
+}
+
+// Similar to getHealthThermometerDevice() except the device
+// is not connected and thus its services have not been
+// discovered.
+function getDiscoveredHealthThermometerDevice(
+  options = {filters: [{services: ['health_thermometer']}]}) {
+  return setUpPreconnectedDevice({
+    address: '09:09:09:09:09:09',
+    name: 'Health Thermometer',
+    knownServiceUUIDs: ['generic_access', 'health_thermometer'],
+  })
+  .then(fake_peripheral => {
+    return requestDeviceWithKeyDown(options)
+      .then(device => [device, fake_peripheral]);
+  });
+}
diff --git a/third_party/WebKit/LayoutTests/resources/bluetooth/web-bluetooth-test.js b/third_party/WebKit/LayoutTests/resources/bluetooth/web-bluetooth-test.js
index 450c27d..1f5e29de 100644
--- a/third_party/WebKit/LayoutTests/resources/bluetooth/web-bluetooth-test.js
+++ b/third_party/WebKit/LayoutTests/resources/bluetooth/web-bluetooth-test.js
@@ -139,7 +139,7 @@
 
       let peripheral = this.peripherals_.get(address);
       if (peripheral === undefined) {
-        peripheral = new FakePeripheral(address, this);
+        peripheral = new FakePeripheral(address, this.fake_central_ptr_);
         this.peripherals_.set(address, peripheral);
       }
 
@@ -148,9 +148,22 @@
   }
 
   class FakePeripheral {
-    constructor(address, fake_central) {
+    constructor(address, fake_central_ptr) {
       this.address = address;
-      this.fake_central_ = fake_central;
+      this.fake_central_ptr_ = fake_central_ptr;
+    }
+
+    // Sets the next GATT Connection request response to |code|. |code| could be
+    // an HCI Error Code from BT 4.2 Vol 2 Part D 1.3 List Of Error Codes or a
+    // number outside that range returned by specific platforms e.g. Android
+    // returns 0x101 to signal a GATT failure
+    // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#GATT_FAILURE
+    async setNextGATTConnectionResponse({code}) {
+      let {success} =
+        await this.fake_central_ptr_.setNextGATTConnectionResponse(
+          this.address, code);
+
+      if (success !== true) throw 'Cannot set next response.';
     }
   }
 
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.cpp
index 9cba88ca..01479869 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.cpp
@@ -24,54 +24,34 @@
 
 namespace blink {
 
-// Retrieves the custom elements constructor -> name map, creating it
-// if necessary.
-static v8::Local<v8::Map> EnsureCustomElementRegistryMap(
-    ScriptState* script_state,
-    CustomElementRegistry* registry) {
-  CHECK(script_state->World().IsMainWorld());
-  v8::Isolate* isolate = script_state->GetIsolate();
-
-  V8PrivateProperty::Symbol symbol =
-      V8PrivateProperty::GetCustomElementRegistryMap(isolate);
-  v8::Local<v8::Object> wrapper = ToV8(registry, script_state).As<v8::Object>();
-  v8::Local<v8::Value> map = symbol.GetOrUndefined(wrapper);
-  if (map->IsUndefined()) {
-    map = v8::Map::New(isolate);
-    symbol.Set(wrapper, map);
-  }
-  return map.As<v8::Map>();
-}
-
 ScriptCustomElementDefinition* ScriptCustomElementDefinition::ForConstructor(
     ScriptState* script_state,
     CustomElementRegistry* registry,
     const v8::Local<v8::Value>& constructor) {
-  v8::Local<v8::Map> map =
-      EnsureCustomElementRegistryMap(script_state, registry);
-  v8::Local<v8::Value> id_value =
-      map->Get(script_state->GetContext(), constructor).ToLocalChecked();
+  auto private_id =
+      script_state->PerContextData()->GetPrivateCustomElementDefinitionId();
+  v8::Local<v8::Value> id_value;
+  if (!constructor.As<v8::Object>()
+           ->GetPrivate(script_state->GetContext(), private_id)
+           .ToLocal(&id_value))
+    return nullptr;
   if (!id_value->IsUint32())
     return nullptr;
   uint32_t id = id_value.As<v8::Uint32>()->Value();
 
-  // This downcast is safe because only
-  // ScriptCustomElementDefinitions have an ID associated with a V8
-  // constructor in the map; see
-  // ScriptCustomElementDefinition::create. This relies on three
-  // things:
+  // This downcast is safe because only ScriptCustomElementDefinitions
+  // have an ID associated with them. This relies on three things:
   //
-  // 1. Only ScriptCustomElementDefinition adds entries to the map.
-  //    Audit the use of private properties in general and how the
-  //    map is handled--it should never be leaked to script.
+  // 1. Only ScriptCustomElementDefinition::Create sets the private
+  //    property on a constructor.
   //
   // 2. CustomElementRegistry adds ScriptCustomElementDefinitions
-  //    assigned an ID to the lis tof definitions without fail.
+  //    assigned an ID to the list of definitions without fail.
   //
   // 3. The relationship between the CustomElementRegistry and its
-  //    map is never mixed up; this is guaranteed by the bindings
-  //    system which provides a stable wrapper, and the map hangs
-  //    off the wrapper.
+  //    private property is never mixed up; this is guaranteed by the
+  //    bindings system because the registry is associated with its
+  //    context.
   //
   // At a meta-level, this downcast is safe because there is
   // currently only one implementation of CustomElementDefinition in
@@ -98,12 +78,14 @@
       disconnected_callback, adopted_callback, attribute_changed_callback,
       std::move(observed_attributes));
 
-  // Add a constructor -> ID mapping to the registry.
+  // Tag the JavaScript constructor object with its ID.
   v8::Local<v8::Value> id_value =
-      v8::Integer::New(script_state->GetIsolate(), id);
-  v8::Local<v8::Map> map =
-      EnsureCustomElementRegistryMap(script_state, registry);
-  map->Set(script_state->GetContext(), constructor, id_value).ToLocalChecked();
+      v8::Integer::NewFromUnsigned(script_state->GetIsolate(), id);
+  auto private_id =
+      script_state->PerContextData()->GetPrivateCustomElementDefinitionId();
+  CHECK(
+      constructor->SetPrivate(script_state->GetContext(), private_id, id_value)
+          .ToChecked());
 
   return definition;
 }
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp b/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp
index b9517bb7..7a8d0ca 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp
@@ -76,7 +76,7 @@
   int memory_usage_mb = Platform::Current()->ActualMemoryUsageMB();
   DVLOG(1) << "V8 error: " << message << " (" << location
            << ").  Current memory usage: " << memory_usage_mb << " MB";
-  IMMEDIATE_CRASH();
+  LOG(FATAL);
 }
 
 static void ReportOOMErrorInMainThread(const char* location, bool is_js_heap) {
@@ -485,7 +485,7 @@
                                      const char* message) {
   // FIXME: We temporarily deal with V8 internal error situations such as
   // out-of-memory by crashing the worker.
-  IMMEDIATE_CRASH();
+  LOG(FATAL);
 }
 
 static void MessageHandlerInWorker(v8::Local<v8::Message> message,
diff --git a/third_party/WebKit/Source/core/css/resolver/StyleResolverState.h b/third_party/WebKit/Source/core/css/resolver/StyleResolverState.h
index c1eea48..e558f76 100644
--- a/third_party/WebKit/Source/core/css/resolver/StyleResolverState.h
+++ b/third_party/WebKit/Source/core/css/resolver/StyleResolverState.h
@@ -202,8 +202,10 @@
     font_builder_.DidChangeWritingMode();
   }
   void SetTextOrientation(ETextOrientation text_orientation) {
-    if (style_->SetTextOrientation(text_orientation))
+    if (style_->GetTextOrientation() != text_orientation) {
+      style_->SetTextOrientation(text_orientation);
       font_builder_.DidChangeTextOrientation();
+    }
   }
 
   void SetHasDirAutoAttribute(bool value) { has_dir_auto_attribute_ = value; }
diff --git a/third_party/WebKit/Source/core/dom/DOMTokenList.cpp b/third_party/WebKit/Source/core/dom/DOMTokenList.cpp
index b2387b53..05214d0 100644
--- a/third_party/WebKit/Source/core/dom/DOMTokenList.cpp
+++ b/third_party/WebKit/Source/core/dom/DOMTokenList.cpp
@@ -93,7 +93,7 @@
 
 // https://dom.spec.whatwg.org/#dom-domtokenlist-contains
 bool DOMTokenList::contains(const AtomicString& token) const {
-  return tokens_.Contains(token);
+  return token_set_.Contains(token);
 }
 
 void DOMTokenList::Add(const AtomicString& token) {
@@ -194,22 +194,22 @@
   bool found_old_token = false;
   bool found_new_token = false;
   bool did_update = false;
-  for (size_t i = 0; i < tokens_.size(); ++i) {
-    const AtomicString& existing_token = tokens_[i];
+  for (size_t i = 0; i < token_set_.size(); ++i) {
+    const AtomicString& existing_token = token_set_[i];
     if (found_old_token) {
       if (existing_token == new_token) {
-        tokens_.Remove(i);
+        token_set_.Remove(i);
         break;
       }
     } else if (found_new_token) {
       if (existing_token == token) {
-        tokens_.Remove(i);
+        token_set_.Remove(i);
         did_update = true;
         break;
       }
     } else if (existing_token == token) {
       found_old_token = true;
-      tokens_.ReplaceAt(i, new_token);
+      token_set_.ReplaceAt(i, new_token);
       did_update = true;
     } else if (existing_token == new_token) {
       found_new_token = true;
@@ -222,7 +222,7 @@
   if (!did_update)
     return;
 
-  UpdateWithTokenSet(tokens_);
+  UpdateWithTokenSet(token_set_);
 }
 
 bool DOMTokenList::supports(const AtomicString& token,
@@ -234,25 +234,26 @@
 void DOMTokenList::AddTokens(const Vector<String>& tokens) {
   // 2. For each token in tokens, append token to context object’s token set.
   for (const auto& token : tokens)
-    tokens_.Add(AtomicString(token));
+    token_set_.Add(AtomicString(token));
   // 3. Run the update steps.
-  UpdateWithTokenSet(tokens_);
+  UpdateWithTokenSet(token_set_);
 }
 
 // https://dom.spec.whatwg.org/#dom-domtokenlist-remove
 void DOMTokenList::RemoveTokens(const Vector<String>& tokens) {
   // 2. For each token in tokens, remove token from context object’s token set.
   for (const auto& token : tokens)
-    tokens_.Remove(AtomicString(token));
+    token_set_.Remove(AtomicString(token));
   // 3. Run the update steps.
-  UpdateWithTokenSet(tokens_);
+  UpdateWithTokenSet(token_set_);
 }
 
 // https://dom.spec.whatwg.org/#concept-ordered-set-serializer
 // The ordered set serializer takes a set and returns the concatenation of the
 // strings in set, separated from each other by U+0020, if set is non-empty, and
 // the empty string otherwise.
-AtomicString DOMTokenList::SerializeSet(const SpaceSplitString& token_set) {
+AtomicString DOMTokenList::SerializeTokenSet(
+    const SpaceSplitString& token_set) {
   size_t size = token_set.size();
   if (size == 0)
     return g_empty_atom;
@@ -270,7 +271,7 @@
 // https://dom.spec.whatwg.org/#concept-dtl-update
 void DOMTokenList::UpdateWithTokenSet(const SpaceSplitString& token_set) {
   AutoReset<bool> updating(&is_in_update_step_, true);
-  setValue(SerializeSet(token_set));
+  setValue(SerializeTokenSet(token_set));
 }
 
 void DOMTokenList::setValue(const AtomicString& value) {
@@ -283,13 +284,13 @@
   if (is_in_update_step_)
     return;
   if (old_value != new_value)
-    tokens_.Set(new_value);
+    token_set_.Set(new_value);
 }
 
 const AtomicString DOMTokenList::item(unsigned index) const {
   if (index >= length())
     return AtomicString();
-  return tokens_[index];
+  return token_set_[index];
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/DOMTokenList.h b/third_party/WebKit/Source/core/dom/DOMTokenList.h
index 5f2db853..d0fd3d93 100644
--- a/third_party/WebKit/Source/core/dom/DOMTokenList.h
+++ b/third_party/WebKit/Source/core/dom/DOMTokenList.h
@@ -50,7 +50,7 @@
   virtual ~DOMTokenList() {}
   DECLARE_VIRTUAL_TRACE();
 
-  unsigned length() const { return tokens_.size(); }
+  unsigned length() const { return token_set_.size(); }
   const AtomicString item(unsigned index) const;
   bool contains(const AtomicString&) const;
   void add(const Vector<String>&, ExceptionState&);
@@ -70,7 +70,7 @@
   void DidUpdateAttributeValue(const AtomicString& old_value,
                                const AtomicString& new_value);
 
-  const SpaceSplitString& Tokens() const { return tokens_; }
+  const SpaceSplitString& TokenSet() const { return token_set_; }
   // Add() and Remove() have DCHECK for syntax of the specified token.
   void Add(const AtomicString&);
   void Remove(const AtomicString&);
@@ -86,9 +86,9 @@
   void AddTokens(const Vector<String>&);
   void RemoveTokens(const Vector<String>&);
   void UpdateWithTokenSet(const SpaceSplitString&);
-  static AtomicString SerializeSet(const SpaceSplitString&);
+  static AtomicString SerializeTokenSet(const SpaceSplitString&);
 
-  SpaceSplitString tokens_;
+  SpaceSplitString token_set_;
   AtomicString value_;
   const Member<Element> element_;
   const QualifiedName attribute_name_;
diff --git a/third_party/WebKit/Source/core/dom/Modulator.h b/third_party/WebKit/Source/core/dom/Modulator.h
index 9c53af3a..8d9f266 100644
--- a/third_party/WebKit/Source/core/dom/Modulator.h
+++ b/third_party/WebKit/Source/core/dom/Modulator.h
@@ -111,7 +111,7 @@
 
   virtual ScriptValue InstantiateModule(ScriptModule) = 0;
 
-  virtual ScriptValue GetInstantiationError(const ModuleScript*) = 0;
+  virtual ScriptValue GetError(const ModuleScript*) = 0;
 
   virtual Vector<String> ModuleRequestsFromScriptModule(ScriptModule) = 0;
 
diff --git a/third_party/WebKit/Source/core/dom/ModulatorImpl.cpp b/third_party/WebKit/Source/core/dom/ModulatorImpl.cpp
index 878ea9f5..9a132542 100644
--- a/third_party/WebKit/Source/core/dom/ModulatorImpl.cpp
+++ b/third_party/WebKit/Source/core/dom/ModulatorImpl.cpp
@@ -149,12 +149,10 @@
   return script_module.Instantiate(script_state_.Get());
 }
 
-ScriptValue ModulatorImpl::GetInstantiationError(
-    const ModuleScript* module_script) {
+ScriptValue ModulatorImpl::GetError(const ModuleScript* module_script) {
   ScriptState::Scope scope(script_state_.Get());
-  return ScriptValue(script_state_.Get(),
-                     module_script->CreateInstantiationErrorInternal(
-                         script_state_->GetIsolate()));
+  return ScriptValue(script_state_.Get(), module_script->CreateErrorInternal(
+                                              script_state_->GetIsolate()));
 }
 
 Vector<String> ModulatorImpl::ModuleRequestsFromScriptModule(
@@ -188,13 +186,11 @@
 
   // 3. "If s's instantiation state is "errored", then report the exception
   //     given by s's instantiation error for s and abort these steps."
-  ModuleInstantiationState instantiationState =
-      module_script->InstantiationState();
+  ModuleInstantiationState instantiationState = module_script->State();
   if (instantiationState == ModuleInstantiationState::kErrored) {
     v8::Isolate* isolate = script_state_->GetIsolate();
     ScriptModule::ReportException(
-        script_state_.Get(),
-        module_script->CreateInstantiationErrorInternal(isolate),
+        script_state_.Get(), module_script->CreateErrorInternal(isolate),
         module_script->BaseURL().GetString(), module_script->StartPosition());
     return;
   }
diff --git a/third_party/WebKit/Source/core/dom/ModulatorImpl.h b/third_party/WebKit/Source/core/dom/ModulatorImpl.h
index e0af185..35013d1 100644
--- a/third_party/WebKit/Source/core/dom/ModulatorImpl.h
+++ b/third_party/WebKit/Source/core/dom/ModulatorImpl.h
@@ -65,7 +65,7 @@
                              AccessControlStatus,
                              const TextPosition&) override;
   ScriptValue InstantiateModule(ScriptModule) override;
-  ScriptValue GetInstantiationError(const ModuleScript*) override;
+  ScriptValue GetError(const ModuleScript*) override;
   Vector<String> ModuleRequestsFromScriptModule(ScriptModule) override;
   void ExecuteModule(const ModuleScript*) override;
 
diff --git a/third_party/WebKit/Source/core/dom/ModuleMapTest.cpp b/third_party/WebKit/Source/core/dom/ModuleMapTest.cpp
index b29ebec4..1492bfc 100644
--- a/third_party/WebKit/Source/core/dom/ModuleMapTest.cpp
+++ b/third_party/WebKit/Source/core/dom/ModuleMapTest.cpp
@@ -184,7 +184,7 @@
             1);
   EXPECT_TRUE(client->WasNotifyFinished());
   EXPECT_TRUE(client->GetModuleScript());
-  EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
+  EXPECT_EQ(client->GetModuleScript()->State(),
             ModuleInstantiationState::kUninstantiated);
 
   // Secondary request
@@ -203,7 +203,7 @@
       << "registerModuleScript sholudn't be called in secondary request.";
   EXPECT_TRUE(client2->WasNotifyFinished());
   EXPECT_TRUE(client2->GetModuleScript());
-  EXPECT_EQ(client2->GetModuleScript()->InstantiationState(),
+  EXPECT_EQ(client2->GetModuleScript()->State(),
             ModuleInstantiationState::kUninstantiated);
 }
 
@@ -240,11 +240,11 @@
 
   EXPECT_TRUE(client->WasNotifyFinished());
   EXPECT_TRUE(client->GetModuleScript());
-  EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
+  EXPECT_EQ(client->GetModuleScript()->State(),
             ModuleInstantiationState::kUninstantiated);
   EXPECT_TRUE(client2->WasNotifyFinished());
   EXPECT_TRUE(client2->GetModuleScript());
-  EXPECT_EQ(client2->GetModuleScript()->InstantiationState(),
+  EXPECT_EQ(client2->GetModuleScript()->State(),
             ModuleInstantiationState::kUninstantiated);
 }
 
diff --git a/third_party/WebKit/Source/core/dom/ModulePendingScript.cpp b/third_party/WebKit/Source/core/dom/ModulePendingScript.cpp
index f0184b1..5675e938 100644
--- a/third_party/WebKit/Source/core/dom/ModulePendingScript.cpp
+++ b/third_party/WebKit/Source/core/dom/ModulePendingScript.cpp
@@ -24,7 +24,7 @@
 
 void ModulePendingScriptTreeClient::NotifyModuleTreeLoadFinished(
     ModuleScript* module_script) {
-  DCHECK(!(module_script && module_script->InstantiationState() ==
+  DCHECK(!(module_script && module_script->State() ==
                                 ModuleInstantiationState::kUninstantiated));
   DCHECK(!finished_);
   finished_ = true;
diff --git a/third_party/WebKit/Source/core/dom/ModuleScript.cpp b/third_party/WebKit/Source/core/dom/ModuleScript.cpp
index fd65ecc..3f1139d 100644
--- a/third_party/WebKit/Source/core/dom/ModuleScript.cpp
+++ b/third_party/WebKit/Source/core/dom/ModuleScript.cpp
@@ -38,6 +38,19 @@
                         parser_state, credentials_mode, start_position);
 }
 
+ModuleScript* ModuleScript::CreateForTest(
+    Modulator* modulator,
+    ScriptModule record,
+    const KURL& base_url,
+    const String& nonce,
+    ParserDisposition parser_state,
+    WebURLRequest::FetchCredentialsMode credentials_mode) {
+  String dummy_source_text = "";
+  return CreateInternal(dummy_source_text, modulator, record, base_url, nonce,
+                        parser_state, credentials_mode,
+                        TextPosition::MinimumPosition());
+}
+
 ModuleScript* ModuleScript::CreateInternal(
     const String& source_text,
     Modulator* modulator,
@@ -67,19 +80,6 @@
   return module_script;
 }
 
-ModuleScript* ModuleScript::CreateForTest(
-    Modulator* modulator,
-    ScriptModule record,
-    const KURL& base_url,
-    const String& nonce,
-    ParserDisposition parser_state,
-    WebURLRequest::FetchCredentialsMode credentials_mode) {
-  String dummy_source_text = "";
-  return CreateInternal(dummy_source_text, modulator, record, base_url, nonce,
-                        parser_state, credentials_mode,
-                        TextPosition::MinimumPosition());
-}
-
 ModuleScript::ModuleScript(Modulator* settings_object,
                            ScriptModule record,
                            const KURL& base_url,
@@ -91,7 +91,7 @@
     : settings_object_(settings_object),
       record_(this),
       base_url_(base_url),
-      instantiation_error_(this),
+      error_(this),
       nonce_(nonce),
       parser_state_(parser_state),
       credentials_mode_(credentials_mode),
@@ -118,23 +118,33 @@
   return ScriptModule(isolate, record_.NewLocal(isolate));
 }
 
-void ModuleScript::SetInstantiationErrorAndClearRecord(ScriptValue error) {
-  // Implements Step 7.1 of:
-  // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-script-graph-fetching-procedure
+void ModuleScript::SetErrorAndClearRecord(ScriptValue error) {
+  // https://html.spec.whatwg.org/multipage/webappapis.html#error-a-module-script
+  // Step 1. Assert: script's state is not "errored".
+  DCHECK_NE(state_, ModuleInstantiationState::kErrored);
 
-  // "set script's instantiation state to "errored", ..."
-  DCHECK_EQ(instantiation_state_, ModuleInstantiationState::kUninstantiated);
-  instantiation_state_ = ModuleInstantiationState::kErrored;
+  // Step 2. If script's module record is set, then:
+  if (!record_.IsEmpty()) {
+    // Step 2.1. Set script module record's [[HostDefined]] field to undefined.
+    // TODO(kouhei): Implement this step.
+    // if (ScriptModuleResolver* resolver =
+    // modulator_->GetScriptModuleResolver())
+    //   resolver->UnregisterModuleScript(this);
+    NOTIMPLEMENTED();
 
-  // "its instantiation error to instantiationStatus.[[Value]], and ..."
+    // Step 2.2. Set script's module record to null.
+    record_.Clear();
+  }
+
+  // Step 3. Set script's state to "errored".
+  state_ = ModuleInstantiationState::kErrored;
+
+  // Step 4. Set script's error to error.
   DCHECK(!error.IsEmpty());
   {
     ScriptState::Scope scope(error.GetScriptState());
-    instantiation_error_.Set(error.GetIsolate(), error.V8Value());
+    error_.Set(error.GetIsolate(), error.V8Value());
   }
-
-  // "its module record to null."
-  record_.Clear();
 }
 
 void ModuleScript::SetInstantiationSuccess() {
@@ -142,8 +152,8 @@
   // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-script-graph-fetching-procedure
 
   // "set script's instantiation state to "instantiated"."
-  DCHECK_EQ(instantiation_state_, ModuleInstantiationState::kUninstantiated);
-  instantiation_state_ = ModuleInstantiationState::kInstantiated;
+  DCHECK_EQ(state_, ModuleInstantiationState::kUninstantiated);
+  state_ = ModuleInstantiationState::kInstantiated;
 }
 
 DEFINE_TRACE(ModuleScript) {
@@ -154,7 +164,7 @@
   // TODO(mlippautz): Support TraceWrappers(const
   // TraceWrapperV8Reference<v8::Module>&) to remove the cast.
   visitor->TraceWrappers(record_.Cast<v8::Value>());
-  visitor->TraceWrappers(instantiation_error_);
+  visitor->TraceWrappers(error_);
 }
 
 bool ModuleScript::IsEmpty() const {
diff --git a/third_party/WebKit/Source/core/dom/ModuleScript.h b/third_party/WebKit/Source/core/dom/ModuleScript.h
index 389e1e75..b0d5081 100644
--- a/third_party/WebKit/Source/core/dom/ModuleScript.h
+++ b/third_party/WebKit/Source/core/dom/ModuleScript.h
@@ -20,7 +20,7 @@
 
 namespace blink {
 
-// https://html.spec.whatwg.org/multipage/webappapis.html#concept-module-script-instantiation-state
+// https://html.spec.whatwg.org/multipage/webappapis.html#concept-module-script-state
 enum class ModuleInstantiationState {
   kUninstantiated,
   kErrored,
@@ -56,19 +56,17 @@
   ScriptModule Record() const;
   const KURL& BaseURL() const { return base_url_; }
 
-  ModuleInstantiationState InstantiationState() const {
-    return instantiation_state_;
-  }
+  ModuleInstantiationState State() const { return state_; }
 
-  // Implements Step 7.1 of:
-  // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-script-graph-fetching-procedure
-  void SetInstantiationErrorAndClearRecord(ScriptValue error);
+  // https://html.spec.whatwg.org/multipage/webappapis.html#error-a-module-script
+  void SetErrorAndClearRecord(ScriptValue error);
+
   // Implements Step 7.2 of:
   // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-script-graph-fetching-procedure
   void SetInstantiationSuccess();
 
-  v8::Local<v8::Value> CreateInstantiationError(v8::Isolate* isolate) const {
-    return instantiation_error_.NewLocal(isolate);
+  v8::Local<v8::Value> CreateError(v8::Isolate* isolate) const {
+    return error_.NewLocal(isolate);
   }
 
   ParserDisposition ParserState() const { return parser_state_; }
@@ -110,11 +108,10 @@
 
   friend class ModulatorImpl;
   friend class ModuleTreeLinkerTestModulator;
-  // Access this func only via ModulatorImpl::GetInstantiationError(),
+  // Access this func only via ModulatorImpl::GetError(),
   // or via Modulator mocks for unit tests.
-  v8::Local<v8::Value> CreateInstantiationErrorInternal(
-      v8::Isolate* isolate) const {
-    return instantiation_error_.NewLocal(isolate);
+  v8::Local<v8::Value> CreateErrorInternal(v8::Isolate* isolate) const {
+    return error_.NewLocal(isolate);
   }
 
   // https://html.spec.whatwg.org/multipage/webappapis.html#settings-object
@@ -127,18 +124,17 @@
   const KURL base_url_;
 
   // https://html.spec.whatwg.org/multipage/webappapis.html#concept-module-script-instantiation-state
-  ModuleInstantiationState instantiation_state_ =
-      ModuleInstantiationState::kUninstantiated;
+  ModuleInstantiationState state_ = ModuleInstantiationState::kUninstantiated;
 
-  // https://html.spec.whatwg.org/multipage/webappapis.html#concept-module-script-instantiation-error
+  // https://html.spec.whatwg.org/multipage/webappapis.html#concept-module-script-error
   //
-  // |instantiation_error_| is TraceWrappers()ed and kept alive via the path of
+  // |error_| is TraceWrappers()ed and kept alive via the path of
   // v8::Context -> PerContextData -> Modulator/ModulatorImpl
-  // -> ModuleMap -> ModuleMap::Entry -> ModuleScript -> instantiation_error_.
+  // -> ModuleMap -> ModuleMap::Entry -> ModuleScript -> error_.
   // All the classes/references on the path above should be
   // TraceWrapperBase/TraceWrapperMember<>/etc.,
   // but other references to those classes can be normal Member<>.
-  TraceWrapperV8Reference<v8::Value> instantiation_error_;
+  TraceWrapperV8Reference<v8::Value> error_;
 
   // https://html.spec.whatwg.org/multipage/webappapis.html#concept-module-script-nonce
   const String nonce_;
diff --git a/third_party/WebKit/Source/core/dom/ScriptModuleResolverImpl.cpp b/third_party/WebKit/Source/core/dom/ScriptModuleResolverImpl.cpp
index 4366cf0..058afd3 100644
--- a/third_party/WebKit/Source/core/dom/ScriptModuleResolverImpl.cpp
+++ b/third_party/WebKit/Source/core/dom/ScriptModuleResolverImpl.cpp
@@ -66,9 +66,8 @@
 
   // Step 5. If resolved module script's instantiation state is "errored", then
   // throw resolved module script's instantiation error.
-  if (module_script->InstantiationState() ==
-      ModuleInstantiationState::kErrored) {
-    ScriptValue error = modulator_->GetInstantiationError(module_script);
+  if (module_script->State() == ModuleInstantiationState::kErrored) {
+    ScriptValue error = modulator_->GetError(module_script);
     exception_state.RethrowV8Exception(error.V8Value());
     return ScriptModule();
   }
diff --git a/third_party/WebKit/Source/core/dom/ScriptModuleResolverImplTest.cpp b/third_party/WebKit/Source/core/dom/ScriptModuleResolverImplTest.cpp
index e3fdc20..749d594 100644
--- a/third_party/WebKit/Source/core/dom/ScriptModuleResolverImplTest.cpp
+++ b/third_party/WebKit/Source/core/dom/ScriptModuleResolverImplTest.cpp
@@ -46,11 +46,10 @@
 
   ModuleScript* GetFetchedModuleScript(const KURL&) override;
 
-  ScriptValue GetInstantiationError(const ModuleScript* module_script) {
+  ScriptValue GetError(const ModuleScript* module_script) {
     ScriptState::Scope scope(script_state_.Get());
-    return ScriptValue(
-        script_state_.Get(),
-        module_script->CreateInstantiationError(script_state_->GetIsolate()));
+    return ScriptValue(script_state_.Get(),
+                       module_script->CreateError(script_state_->GetIsolate()));
   }
 
   RefPtr<ScriptState> script_state_;
@@ -101,7 +100,7 @@
     EXPECT_EQ(ModuleInstantiationState::kErrored, state);
     v8::Local<v8::Value> error =
         V8ThrowException::CreateError(scope.GetIsolate(), "hoge");
-    module_script->SetInstantiationErrorAndClearRecord(
+    module_script->SetErrorAndClearRecord(
         ScriptValue(scope.GetScriptState(), error));
   }
   return module_script;
diff --git a/third_party/WebKit/Source/core/dom/Text.cpp b/third_party/WebKit/Source/core/dom/Text.cpp
index 1b9a0dad0..776feb3 100644
--- a/third_party/WebKit/Source/core/dom/Text.cpp
+++ b/third_party/WebKit/Source/core/dom/Text.cpp
@@ -176,8 +176,8 @@
     if (!n->IsTextNode())
       continue;
     const String& data = ToText(n)->data();
-    if (std::numeric_limits<unsigned>::max() - data.length() < result_length)
-      IMMEDIATE_CRASH();
+    CHECK_GE(std::numeric_limits<unsigned>::max() - data.length(),
+             result_length);
     result_length += data.length();
   }
   StringBuilder result;
diff --git a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextNodeHandler.cpp b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextNodeHandler.cpp
index 1fd0e41d..d4656e6e 100644
--- a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextNodeHandler.cpp
+++ b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextNodeHandler.cpp
@@ -450,8 +450,13 @@
                                            LayoutText* layout_object,
                                            int text_start_offset,
                                            int text_end_offset) {
-  text_state_->EmitText(text_node, layout_object, text_start_offset,
-                        text_end_offset);
+  const String& string = behavior_.EmitsOriginalText()
+                             ? layout_object->OriginalText()
+                             : layout_object->GetText();
+  text_state_->EmitText(text_node,
+                        text_start_offset + layout_object->TextStartOffset(),
+                        text_end_offset + layout_object->TextStartOffset(),
+                        string, text_start_offset, text_end_offset);
   ResetCollapsedWhiteSpaceFixup();
 }
 
diff --git a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.cpp b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.cpp
index 6971b1f..060b538 100644
--- a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.cpp
+++ b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.cpp
@@ -28,7 +28,7 @@
 #include "core/editing/iterators/TextIteratorTextState.h"
 
 #include "core/editing/iterators/TextIteratorBehavior.h"
-#include "core/layout/LayoutText.h"
+#include "platform/wtf/text/StringBuilder.h"
 
 namespace blink {
 
@@ -141,15 +141,17 @@
   last_character_ = c;
 }
 
-// TODO(xiaochengh): Remove the dependency on LayoutText, so that the class can
-// also be used by Layout NG.
 void TextIteratorTextState::EmitText(Node* text_node,
-                                     LayoutText* layout_object,
+                                     int position_start_offset,
+                                     int position_end_offset,
+                                     const String& string,
                                      int text_start_offset,
                                      int text_end_offset) {
   DCHECK(text_node);
-  text_ = behavior_.EmitsOriginalText() ? layout_object->OriginalText()
-                                        : layout_object->GetText();
+  text_ = string;
+
+  // TODO(xiaochengh): Hoist the conversion to TextIteratorTextNodeHandler, so
+  // that we can remove |behavior_| from TextIteratorTextState.
   if (behavior_.EmitsSpaceForNbsp())
     text_.Replace(kNoBreakSpaceCharacter, kSpaceCharacter);
 
@@ -162,8 +164,8 @@
 
   position_node_ = text_node;
   position_offset_base_node_ = nullptr;
-  position_start_offset_ = text_start_offset + layout_object->TextStartOffset();
-  position_end_offset_ = text_end_offset + layout_object->TextStartOffset();
+  position_start_offset_ = position_start_offset;
+  position_end_offset_ = position_end_offset;
   single_character_buffer_ = 0;
   text_start_offset_ = text_start_offset;
   text_length_ = text_end_offset - text_start_offset;
diff --git a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.h b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.h
index 64dbf2f..1be6394 100644
--- a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.h
+++ b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.h
@@ -35,7 +35,6 @@
 
 namespace blink {
 
-class LayoutText;
 class TextIteratorBehavior;
 
 class CORE_EXPORT TextIteratorTextState
@@ -60,8 +59,10 @@
                     Node* offset_base_node,
                     int text_start_offset,
                     int text_end_offset);
-  void EmitText(Node* text_node,
-                LayoutText* layout_object,
+  void EmitText(Node*,
+                int position_start_offset,
+                int position_end_offset,
+                const String&,
                 int text_start_offset,
                 int text_end_offset);
   void EmitAltText(Node*);
diff --git a/third_party/WebKit/Source/core/html/HTMLIFrameElement.cpp b/third_party/WebKit/Source/core/html/HTMLIFrameElement.cpp
index 2bbce29..0a9e723 100644
--- a/third_party/WebKit/Source/core/html/HTMLIFrameElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLIFrameElement.cpp
@@ -128,7 +128,7 @@
     sandbox_->DidUpdateAttributeValue(params.old_value, value);
     String invalid_tokens;
     SetSandboxFlags(value.IsNull() ? kSandboxNone
-                                   : ParseSandboxPolicy(sandbox_->Tokens(),
+                                   : ParseSandboxPolicy(sandbox_->TokenSet(),
                                                         invalid_tokens));
     if (!invalid_tokens.IsNull()) {
       GetDocument().AddConsoleMessage(ConsoleMessage::Create(
diff --git a/third_party/WebKit/Source/core/html/HTMLIFrameElementAllow.cpp b/third_party/WebKit/Source/core/html/HTMLIFrameElementAllow.cpp
index 0e43dc3..37ec3f8 100644
--- a/third_party/WebKit/Source/core/html/HTMLIFrameElementAllow.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLIFrameElementAllow.cpp
@@ -21,18 +21,19 @@
   Vector<WebFeaturePolicyFeature> feature_names;
   unsigned num_token_errors = 0;
   StringBuilder token_errors;
-  const SpaceSplitString& tokens = this->Tokens();
+  const SpaceSplitString& token_set = this->TokenSet();
 
   // Collects a list of valid feature names.
   const FeatureNameMap& feature_name_map = GetDefaultFeatureNameMap();
-  for (size_t i = 0; i < tokens.size(); ++i) {
-    if (!feature_name_map.Contains(tokens[i])) {
+  for (size_t i = 0; i < token_set.size(); ++i) {
+    const AtomicString& token = token_set[i];
+    if (!feature_name_map.Contains(token)) {
       token_errors.Append(token_errors.IsEmpty() ? "'" : ", '");
-      token_errors.Append(tokens[i]);
+      token_errors.Append(token);
       token_errors.Append("'");
       ++num_token_errors;
     } else {
-      feature_names.push_back(feature_name_map.at(tokens[i]));
+      feature_names.push_back(feature_name_map.at(token));
     }
   }
 
diff --git a/third_party/WebKit/Source/core/html/media/HTMLMediaElementControlsList.cpp b/third_party/WebKit/Source/core/html/media/HTMLMediaElementControlsList.cpp
index d1fdd44..028488a 100644
--- a/third_party/WebKit/Source/core/html/media/HTMLMediaElementControlsList.cpp
+++ b/third_party/WebKit/Source/core/html/media/HTMLMediaElementControlsList.cpp
@@ -34,15 +34,15 @@
 }
 
 bool HTMLMediaElementControlsList::ShouldHideDownload() const {
-  return Tokens().Contains(kNoDownload);
+  return contains(kNoDownload);
 }
 
 bool HTMLMediaElementControlsList::ShouldHideFullscreen() const {
-  return Tokens().Contains(kNoFullscreen);
+  return contains(kNoFullscreen);
 }
 
 bool HTMLMediaElementControlsList::ShouldHideRemotePlayback() const {
-  return Tokens().Contains(kNoRemotePlayback);
+  return contains(kNoRemotePlayback);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableCell.h b/third_party/WebKit/Source/core/layout/LayoutTableCell.h
index b9bc03b..10a380d 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableCell.h
+++ b/third_party/WebKit/Source/core/layout/LayoutTableCell.h
@@ -103,9 +103,7 @@
   void ColSpanOrRowSpanChanged();
 
   void SetAbsoluteColumnIndex(unsigned column) {
-    if (UNLIKELY(column > kMaxColumnIndex))
-      IMMEDIATE_CRASH();
-
+    CHECK_LE(column, kMaxColumnIndex);
     absolute_column_index_ = column;
   }
 
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableRow.h b/third_party/WebKit/Source/core/layout/LayoutTableRow.h
index e9f841f..894db7c 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableRow.h
+++ b/third_party/WebKit/Source/core/layout/LayoutTableRow.h
@@ -85,9 +85,7 @@
   }
 
   void SetRowIndex(unsigned row_index) {
-    if (UNLIKELY(row_index > kMaxRowIndex))
-      IMMEDIATE_CRASH();
-
+    CHECK_LE(row_index, kMaxRowIndex);
     row_index_ = row_index;
   }
 
diff --git a/third_party/WebKit/Source/core/layout/LayoutText.cpp b/third_party/WebKit/Source/core/layout/LayoutText.cpp
index 8753bc1..80f0ce2 100644
--- a/third_party/WebKit/Source/core/layout/LayoutText.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutText.cpp
@@ -109,9 +109,7 @@
   unsigned length = string->length();
   const StringImpl& input = *string->Impl();
 
-  if (length >= std::numeric_limits<unsigned>::max())
-    IMMEDIATE_CRASH();
-
+  CHECK_LT(length, std::numeric_limits<unsigned>::max());
   StringBuffer<UChar> string_with_previous(length + 1);
   string_with_previous[0] =
       previous == kNoBreakSpaceCharacter ? kSpaceCharacter : previous;
diff --git a/third_party/WebKit/Source/core/loader/modulescript/ModuleScriptLoaderTest.cpp b/third_party/WebKit/Source/core/loader/modulescript/ModuleScriptLoaderTest.cpp
index 15466a3..92ea292 100644
--- a/third_party/WebKit/Source/core/loader/modulescript/ModuleScriptLoaderTest.cpp
+++ b/third_party/WebKit/Source/core/loader/modulescript/ModuleScriptLoaderTest.cpp
@@ -132,7 +132,7 @@
   EXPECT_TRUE(client->WasNotifyFinished())
       << "ModuleScriptLoader should finish synchronously.";
   ASSERT_TRUE(client->GetModuleScript());
-  EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
+  EXPECT_EQ(client->GetModuleScript()->State(),
             ModuleInstantiationState::kUninstantiated);
 }
 
diff --git a/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinker.cpp b/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinker.cpp
index fec4cbe..ea773dc 100644
--- a/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinker.cpp
+++ b/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinker.cpp
@@ -390,10 +390,9 @@
   ScriptValue error;
 
   // Step 6. If result's instantiation state is "errored",...
-  if (module_script_->InstantiationState() ==
-      ModuleInstantiationState::kErrored) {
+  if (module_script_->State() == ModuleInstantiationState::kErrored) {
     // ... Set instantiationStatus to record.ModuleDeclarationInstantiation().
-    error = modulator_->GetInstantiationError(module_script_);
+    error = modulator_->GetError(module_script_);
     DCHECK(!error.IsEmpty());
   } else {
     // Step 7. Otherwise:
@@ -419,7 +418,7 @@
       // Step 8.1.3. Set script's instantiation state to "errored".
       // Step 8.1.4. Set script's instantiation error to
       // instantiationStatus.[[Value]].
-      descendant->SetInstantiationErrorAndClearRecord(error);
+      descendant->SetErrorAndClearRecord(error);
     } else {
       // Step 8.2. Otherwise, set script's instantiation state to
       // "instantiated".
@@ -518,8 +517,7 @@
   // instantiation state is "uninstantiated".
   HeapHashSet<Member<ModuleScript>> uninstantiated_set;
   for (const auto& script : inclusive_descendants) {
-    if (script->InstantiationState() ==
-        ModuleInstantiationState::kUninstantiated)
+    if (script->State() == ModuleInstantiationState::kUninstantiated)
       uninstantiated_set.insert(script);
   }
   return uninstantiated_set;
diff --git a/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinkerTest.cpp b/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinkerTest.cpp
index d5fb8c1..fcf2142 100644
--- a/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinkerTest.cpp
+++ b/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinkerTest.cpp
@@ -91,12 +91,12 @@
     if (state == ModuleInstantiationState::kErrored) {
       v8::Local<v8::Value> error = V8ThrowException::CreateError(
           script_state_->GetIsolate(), "Instantiation failure.");
-      module_script->SetInstantiationErrorAndClearRecord(
+      module_script->SetErrorAndClearRecord(
           ScriptValue(script_state_.Get(), error));
     }
 
     EXPECT_EQ(url, pending_request_url_);
-    EXPECT_EQ(state, module_script->InstantiationState());
+    EXPECT_EQ(state, module_script->State());
     EXPECT_TRUE(pending_client_);
     pending_client_->NotifyModuleLoadFinished(module_script);
     pending_client_.Clear();
@@ -189,12 +189,10 @@
     return ScriptValue();
   }
 
-  ScriptValue GetInstantiationError(
-      const ModuleScript* module_script) override {
+  ScriptValue GetError(const ModuleScript* module_script) override {
     ScriptState::Scope scope(script_state_.Get());
-    return ScriptValue(script_state_.Get(),
-                       module_script->CreateInstantiationErrorInternal(
-                           script_state_->GetIsolate()));
+    return ScriptValue(script_state_.Get(), module_script->CreateErrorInternal(
+                                                script_state_->GetIsolate()));
   }
 
   Vector<String> ModuleRequestsFromScriptModule(
@@ -266,7 +264,7 @@
       url, {}, ModuleInstantiationState::kUninstantiated);
   EXPECT_TRUE(client->WasNotifyFinished());
   ASSERT_TRUE(client->GetModuleScript());
-  EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
+  EXPECT_EQ(client->GetModuleScript()->State(),
             ModuleInstantiationState::kInstantiated);
 }
 
@@ -295,7 +293,7 @@
 
   EXPECT_TRUE(client->WasNotifyFinished());
   ASSERT_TRUE(client->GetModuleScript());
-  EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
+  EXPECT_EQ(client->GetModuleScript()->State(),
             ModuleInstantiationState::kErrored);
 }
 
@@ -321,7 +319,7 @@
   EXPECT_TRUE(client->WasNotifyFinished());
   ASSERT_TRUE(client->GetModuleScript());
   EXPECT_EQ(ModuleInstantiationState::kErrored,
-            client->GetModuleScript()->InstantiationState());
+            client->GetModuleScript()->State());
 }
 
 TEST_F(ModuleTreeLinkerTest, FetchTreeWithSingleDependency) {
@@ -354,7 +352,7 @@
   EXPECT_TRUE(client->WasNotifyFinished());
 
   ASSERT_TRUE(client->GetModuleScript());
-  EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
+  EXPECT_EQ(client->GetModuleScript()->State(),
             ModuleInstantiationState::kInstantiated);
 }
 
@@ -405,7 +403,7 @@
 
   EXPECT_TRUE(client->WasNotifyFinished());
   ASSERT_TRUE(client->GetModuleScript());
-  EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
+  EXPECT_EQ(client->GetModuleScript()->State(),
             ModuleInstantiationState::kInstantiated);
 }
 
@@ -501,7 +499,7 @@
 
   EXPECT_TRUE(client->WasNotifyFinished());
   ASSERT_TRUE(client->GetModuleScript());
-  EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
+  EXPECT_EQ(client->GetModuleScript()->State(),
             ModuleInstantiationState::kInstantiated);
 }
 
@@ -529,7 +527,7 @@
 
   EXPECT_TRUE(client->WasNotifyFinished());
   ASSERT_TRUE(client->GetModuleScript());
-  EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
+  EXPECT_EQ(client->GetModuleScript()->State(),
             ModuleInstantiationState::kInstantiated);
 }
 
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.cpp b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
index f8f5725d..dc0ba2a 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.cpp
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
@@ -911,18 +911,18 @@
 void ComputedStyle::AddCursor(StyleImage* image,
                               bool hot_spot_specified,
                               const IntPoint& hot_spot) {
-  if (!rare_inherited_data_.Access()->cursor_data_)
-    rare_inherited_data_.Access()->cursor_data_ = new CursorList;
-  rare_inherited_data_.Access()->cursor_data_->push_back(
+  if (!CursorDataInternal())
+    SetCursorDataInternal(new CursorList);
+  MutableCursorDataInternal()->push_back(
       CursorData(image, hot_spot_specified, hot_spot));
 }
 
 void ComputedStyle::SetCursorList(CursorList* other) {
-  rare_inherited_data_.Access()->cursor_data_ = other;
+  SetCursorDataInternal(other);
 }
 
 void ComputedStyle::SetQuotes(RefPtr<QuotesData> q) {
-  rare_inherited_data_.Access()->quotes_ = std::move(q);
+  SetQuotesInternal(q);
 }
 
 bool ComputedStyle::QuotesDataEquivalent(const ComputedStyle& other) const {
@@ -930,8 +930,8 @@
 }
 
 void ComputedStyle::ClearCursorList() {
-  if (rare_inherited_data_->cursor_data_)
-    rare_inherited_data_.Access()->cursor_data_ = nullptr;
+  if (CursorDataInternal())
+    SetCursorDataInternal(nullptr);
 }
 
 static bool HasPropertyThatCreatesStackingContext(
@@ -1207,7 +1207,7 @@
 }
 
 void ComputedStyle::SetTextShadow(RefPtr<ShadowList> s) {
-  rare_inherited_data_.Access()->text_shadow_ = std::move(s);
+  SetTextShadowInternal(s);
 }
 
 bool ComputedStyle::TextShadowDataEquivalent(const ComputedStyle& other) const {
@@ -1239,11 +1239,10 @@
 }
 
 StyleImage* ComputedStyle::ListStyleImage() const {
-  return rare_inherited_data_->list_style_image_.Get();
+  return ListStyleImageInternal();
 }
 void ComputedStyle::SetListStyleImage(StyleImage* v) {
-  if (rare_inherited_data_->list_style_image_ != v)
-    rare_inherited_data_.Access()->list_style_image_ = v;
+  SetListStyleImageInternal(v);
 }
 
 Color ComputedStyle::GetColor() const {
@@ -1407,8 +1406,7 @@
 }
 
 const AtomicString& ComputedStyle::HyphenString() const {
-  const AtomicString& hyphenation_string =
-      rare_inherited_data_.Get()->hyphenation_string_;
+  const AtomicString& hyphenation_string = HyphenationString();
   if (!hyphenation_string.IsNull())
     return hyphenation_string;
 
@@ -1530,7 +1528,7 @@
 TextDecoration ComputedStyle::TextDecorationsInEffect() const {
   if (HasSimpleUnderlineInternal())
     return TextDecoration::kUnderline;
-  if (!rare_inherited_data_->applied_text_decorations_)
+  if (!AppliedTextDecorationsInternal())
     return TextDecoration::kNone;
 
   TextDecoration decorations = TextDecoration::kNone;
@@ -1557,16 +1555,16 @@
         VisitedDependentColor(CSSPropertyTextDecorationColor));
     return underline;
   }
-  if (!rare_inherited_data_->applied_text_decorations_) {
+  if (!AppliedTextDecorationsInternal()) {
     DEFINE_STATIC_LOCAL(Vector<AppliedTextDecoration>, empty, ());
     return empty;
   }
 
-  return rare_inherited_data_->applied_text_decorations_->GetVector();
+  return AppliedTextDecorationsInternal()->GetVector();
 }
 
 StyleInheritedVariables* ComputedStyle::InheritedVariables() const {
-  return rare_inherited_data_->variables_.Get();
+  return VariablesInternal().Get();
 }
 
 StyleNonInheritedVariables* ComputedStyle::NonInheritedVariables() const {
@@ -1574,8 +1572,7 @@
 }
 
 StyleInheritedVariables& ComputedStyle::MutableInheritedVariables() {
-  RefPtr<StyleInheritedVariables>& variables =
-      rare_inherited_data_.Access()->variables_;
+  RefPtr<StyleInheritedVariables>& variables = MutableVariablesInternal();
   if (!variables)
     variables = StyleInheritedVariables::Create();
   else if (!variables->HasOneRef())
@@ -1807,7 +1804,7 @@
 void ComputedStyle::AddAppliedTextDecoration(
     const AppliedTextDecoration& decoration) {
   RefPtr<AppliedTextDecorationList>& list =
-      rare_inherited_data_.Access()->applied_text_decorations_;
+      MutableAppliedTextDecorationsInternal();
 
   if (!list)
     list = AppliedTextDecorationList::Create();
@@ -1819,7 +1816,7 @@
 
 void ComputedStyle::OverrideTextDecorationColors(Color override_color) {
   RefPtr<AppliedTextDecorationList>& list =
-      rare_inherited_data_.Access()->applied_text_decorations_;
+      MutableAppliedTextDecorationsInternal();
   DCHECK(list);
   if (!list->HasOneRef())
     list = list->Copy();
@@ -1832,8 +1829,7 @@
     const Color& parent_text_decoration_color,
     bool override_existing_colors) {
   if (GetTextDecoration() == TextDecoration::kNone &&
-      !HasSimpleUnderlineInternal() &&
-      !rare_inherited_data_->applied_text_decorations_)
+      !HasSimpleUnderlineInternal() && !AppliedTextDecorationsInternal())
     return;
 
   // If there are any color changes or decorations set by this element, stop
@@ -1848,8 +1844,7 @@
         TextDecoration::kUnderline, kTextDecorationStyleSolid,
         parent_text_decoration_color));
   }
-  if (override_existing_colors &&
-      rare_inherited_data_->applied_text_decorations_)
+  if (override_existing_colors && AppliedTextDecorationsInternal())
     OverrideTextDecorationColors(current_text_decoration_color);
   if (GetTextDecoration() == TextDecoration::kNone)
     return;
@@ -1861,7 +1856,7 @@
   bool is_simple_underline = decoration_lines == TextDecoration::kUnderline &&
                              decoration_style == kTextDecorationStyleSolid &&
                              TextDecorationColor().IsCurrentColor();
-  if (is_simple_underline && !rare_inherited_data_->applied_text_decorations_) {
+  if (is_simple_underline && !AppliedTextDecorationsInternal()) {
     SetHasSimpleUnderlineInternal(true);
     return;
   }
@@ -1873,17 +1868,17 @@
 void ComputedStyle::ClearAppliedTextDecorations() {
   SetHasSimpleUnderlineInternal(false);
 
-  if (rare_inherited_data_->applied_text_decorations_)
-    rare_inherited_data_.Access()->applied_text_decorations_ = nullptr;
+  if (AppliedTextDecorationsInternal())
+    SetAppliedTextDecorationsInternal(nullptr);
 }
 
 void ComputedStyle::RestoreParentTextDecorations(
     const ComputedStyle& parent_style) {
   SetHasSimpleUnderlineInternal(parent_style.HasSimpleUnderlineInternal());
-  if (rare_inherited_data_->applied_text_decorations_ !=
-      parent_style.rare_inherited_data_->applied_text_decorations_) {
-    rare_inherited_data_.Access()->applied_text_decorations_ =
-        parent_style.rare_inherited_data_->applied_text_decorations_;
+  if (AppliedTextDecorationsInternal() !=
+      parent_style.AppliedTextDecorationsInternal()) {
+    SetAppliedTextDecorationsInternal(
+        parent_style.AppliedTextDecorationsInternal());
   }
 }
 
@@ -2207,8 +2202,7 @@
 }
 
 TextEmphasisMark ComputedStyle::GetTextEmphasisMark() const {
-  TextEmphasisMark mark =
-      static_cast<TextEmphasisMark>(rare_inherited_data_->text_emphasis_mark_);
+  TextEmphasisMark mark = TextEmphasisMarkInternal();
   if (mark != TextEmphasisMark::kAuto)
     return mark;
 
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.h b/third_party/WebKit/Source/core/style/ComputedStyle.h
index b772b0a..7d6c296 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.h
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.h
@@ -1005,10 +1005,10 @@
   }
   RespectImageOrientationEnum RespectImageOrientation() const {
     return static_cast<RespectImageOrientationEnum>(
-        rare_inherited_data_->respect_image_orientation_);
+        RespectImageOrientationInternal());
   }
   void SetRespectImageOrientation(RespectImageOrientationEnum v) {
-    SET_VAR(rare_inherited_data_, respect_image_orientation_, v);
+    SetRespectImageOrientationInternal(v);
   }
 
   // isolation
@@ -1468,12 +1468,10 @@
     return TextUnderlinePosition::kAuto;
   }
   TextUnderlinePosition GetTextUnderlinePosition() const {
-    return static_cast<TextUnderlinePosition>(
-        rare_inherited_data_->text_underline_position_);
+    return TextUnderlinePositionInternal();
   }
   void SetTextUnderlinePosition(TextUnderlinePosition v) {
-    SET_VAR(rare_inherited_data_, text_underline_position_,
-            static_cast<unsigned>(v));
+    SetTextUnderlinePositionInternal(v);
   }
 
   // text-decoration-skip
@@ -1481,12 +1479,10 @@
     return TextDecorationSkip::kObjects;
   }
   TextDecorationSkip GetTextDecorationSkip() const {
-    return static_cast<TextDecorationSkip>(
-        rare_inherited_data_->text_decoration_skip_);
+    return TextDecorationSkipInternal();
   }
   void SetTextDecorationSkip(TextDecorationSkip v) {
-    SET_VAR(rare_inherited_data_, text_decoration_skip_,
-            static_cast<unsigned>(v));
+    SetTextDecorationSkipInternal(v);
   }
 
   // text-overflow
@@ -1534,7 +1530,7 @@
     return rare_non_inherited_data_->will_change_->scroll_position_;
   }
   bool SubtreeWillChangeContents() const {
-    return rare_inherited_data_->subtree_will_change_contents_;
+    return SubtreeWillChangeContentsInternal();
   }
   void SetWillChangeProperties(const Vector<CSSPropertyID>& properties) {
     SET_NESTED_VAR(rare_non_inherited_data_, will_change_, properties_,
@@ -1547,7 +1543,7 @@
     SET_NESTED_VAR(rare_non_inherited_data_, will_change_, scroll_position_, b);
   }
   void SetSubtreeWillChangeContents(bool b) {
-    SET_VAR(rare_inherited_data_, subtree_will_change_contents_, b);
+    SetSubtreeWillChangeContentsInternal(b);
   }
 
   // z-index
@@ -1565,7 +1561,7 @@
   // zoom
   static float InitialZoom() { return 1.0f; }
   float Zoom() const { return ZoomInternal(); }
-  float EffectiveZoom() const { return rare_inherited_data_->effective_zoom_; }
+  float EffectiveZoom() const { return EffectiveZoomInternal(); }
   bool SetZoom(float);
   bool SetEffectiveZoom(float);
 
@@ -1654,28 +1650,19 @@
 
   // quotes
   static QuotesData* InitialQuotes() { return 0; }
-  QuotesData* Quotes() const { return rare_inherited_data_->quotes_.Get(); }
+  QuotesData* Quotes() const { return QuotesInternal().Get(); }
   void SetQuotes(RefPtr<QuotesData>);
 
   bool QuotesDataEquivalent(const ComputedStyle&) const;
 
   // text-justify
   static TextJustify InitialTextJustify() { return kTextJustifyAuto; }
-  TextJustify GetTextJustify() const {
-    return static_cast<TextJustify>(rare_inherited_data_->text_justify_);
-  }
-  void SetTextJustify(TextJustify v) {
-    SET_VAR(rare_inherited_data_, text_justify_, v);
-  }
-
-  // text-orientation (aka -webkit-text-orientation, -epub-text-orientation)
-  bool SetTextOrientation(ETextOrientation);
+  TextJustify GetTextJustify() const { return TextJustifyInternal(); }
+  void SetTextJustify(TextJustify v) { SetTextJustifyInternal(v); }
 
   // text-shadow
   static ShadowList* InitialTextShadow() { return 0; }
-  ShadowList* TextShadow() const {
-    return rare_inherited_data_->text_shadow_.Get();
-  }
+  ShadowList* TextShadow() const { return TextShadowInternal().Get(); }
   void SetTextShadow(RefPtr<ShadowList>);
 
   bool TextShadowDataEquivalent(const ComputedStyle&) const;
@@ -1686,16 +1673,14 @@
   }
   TextEmphasisMark GetTextEmphasisMark() const;
   void SetTextEmphasisMark(TextEmphasisMark mark) {
-    SET_VAR(rare_inherited_data_, text_emphasis_mark_,
-            static_cast<unsigned>(mark));
+    SetTextEmphasisMarkInternal(mark);
   }
   const AtomicString& TextEmphasisMarkString() const;
 
   // -webkit-text-emphasis-color (aka -epub-text-emphasis-color)
   void SetTextEmphasisColor(const StyleColor& color) {
-    SET_VAR(rare_inherited_data_, text_emphasis_color_, color.Resolve(Color()));
-    SET_VAR(rare_inherited_data_, text_emphasis_color_is_current_color_,
-            color.IsCurrentColor());
+    SetTextEmphasisColorInternal(color.Resolve(Color()));
+    SetTextEmphasisColorIsCurrentColorInternal(color.IsCurrentColor());
   }
 
   // -webkit-line-clamp
@@ -1709,16 +1694,14 @@
 
   // -webkit-text-fill-color
   void SetTextFillColor(const StyleColor& color) {
-    SET_VAR(rare_inherited_data_, text_fill_color_, color.Resolve(Color()));
-    SET_VAR(rare_inherited_data_, text_fill_color_is_current_color_,
-            color.IsCurrentColor());
+    SetTextFillColorInternal(color.Resolve(Color()));
+    SetTextFillColorIsCurrentColorInternal(color.IsCurrentColor());
   }
 
   // -webkit-text-stroke-color
   void SetTextStrokeColor(const StyleColor& color) {
-    SET_VAR(rare_inherited_data_, text_stroke_color_, color.Resolve(Color()));
-    SET_VAR(rare_inherited_data_, text_stroke_color_is_current_color_,
-            color.IsCurrentColor());
+    SetTextStrokeColorInternal(color.Resolve(Color()));
+    SetTextStrokeColorIsCurrentColorInternal(color.IsCurrentColor());
   }
 
   // -webkit-user-drag
@@ -1732,10 +1715,9 @@
 
   // caret-color
   void SetCaretColor(const StyleAutoColor& color) {
-    SET_VAR(rare_inherited_data_, caret_color_, color.Resolve(Color()));
-    SET_VAR(rare_inherited_data_, caret_color_is_current_color_,
-            color.IsCurrentColor());
-    SET_VAR(rare_inherited_data_, caret_color_is_auto_, color.IsAutoColor());
+    SetCaretColorInternal(color.Resolve(Color()));
+    SetCaretColorIsCurrentColorInternal(color.IsCurrentColor());
+    SetCaretColorIsAutoInternal(color.IsAutoColor());
   }
 
   // Font properties.
@@ -2026,10 +2008,10 @@
   void SetTextAutosizingMultiplier(float);
 
   bool SelfOrAncestorHasDirAutoAttribute() const {
-    return rare_inherited_data_->self_or_ancestor_has_dir_auto_attribute_;
+    return SelfOrAncestorHasDirAutoAttributeInternal();
   }
   void SetSelfOrAncestorHasDirAutoAttribute(bool v) {
-    SET_VAR(rare_inherited_data_, self_or_ancestor_has_dir_auto_attribute_, v);
+    SetSelfOrAncestorHasDirAutoAttributeInternal(v);
   }
 
   // Animation flags.
@@ -2970,9 +2952,7 @@
   bool HasContent() const { return GetContentData(); }
 
   // Cursor utility functions.
-  CursorList* Cursors() const {
-    return rare_inherited_data_->cursor_data_.Get();
-  }
+  CursorList* Cursors() const { return CursorDataInternal().Get(); }
   void AddCursor(StyleImage*,
                  bool hot_spot_specified,
                  const IntPoint& hot_spot = IntPoint());
@@ -3310,33 +3290,22 @@
     SET_VAR(rare_non_inherited_data_, visited_link_text_decoration_color_, v);
   }
   void SetVisitedLinkTextEmphasisColor(const StyleColor& color) {
-    SET_VAR(rare_inherited_data_, visited_link_text_emphasis_color_,
-            color.Resolve(Color()));
-    SET_VAR(rare_inherited_data_,
-            visited_link_text_emphasis_color_is_current_color_,
-            color.IsCurrentColor());
+    SetVisitedLinkTextEmphasisColorInternal(color.Resolve(Color()));
+    SetVisitedLinkTextEmphasisColorIsCurrentColorInternal(
+        color.IsCurrentColor());
   }
   void SetVisitedLinkTextFillColor(const StyleColor& color) {
-    SET_VAR(rare_inherited_data_, visited_link_text_fill_color_,
-            color.Resolve(Color()));
-    SET_VAR(rare_inherited_data_,
-            visited_link_text_fill_color_is_current_color_,
-            color.IsCurrentColor());
+    SetVisitedLinkTextFillColorInternal(color.Resolve(Color()));
+    SetVisitedLinkTextFillColorIsCurrentColorInternal(color.IsCurrentColor());
   }
   void SetVisitedLinkTextStrokeColor(const StyleColor& color) {
-    SET_VAR(rare_inherited_data_, visited_link_text_stroke_color_,
-            color.Resolve(Color()));
-    SET_VAR(rare_inherited_data_,
-            visited_link_text_stroke_color_is_current_color_,
-            color.IsCurrentColor());
+    SetVisitedLinkTextStrokeColorInternal(color.Resolve(Color()));
+    SetVisitedLinkTextStrokeColorIsCurrentColorInternal(color.IsCurrentColor());
   }
   void SetVisitedLinkCaretColor(const StyleAutoColor& color) {
-    SET_VAR(rare_inherited_data_, visited_link_caret_color_,
-            color.Resolve(Color()));
-    SET_VAR(rare_inherited_data_, visited_link_caret_color_is_current_color_,
-            color.IsCurrentColor());
-    SET_VAR(rare_inherited_data_, visited_link_caret_color_is_auto_,
-            color.IsAutoColor());
+    SetVisitedLinkCaretColorInternal(color.Resolve(Color()));
+    SetVisitedLinkCaretColorIsCurrentColorInternal(color.IsCurrentColor());
+    SetVisitedLinkCaretColorIsAutoInternal(color.IsAutoColor());
   }
 
   static bool IsDisplayBlockContainer(EDisplay display) {
@@ -3403,11 +3372,11 @@
 
   StyleColor BackgroundColor() const { return BackgroundColorInternal(); }
   StyleAutoColor CaretColor() const {
-    if (rare_inherited_data_->caret_color_is_current_color_)
+    if (CaretColorIsCurrentColorInternal())
       return StyleAutoColor::CurrentColor();
-    if (rare_inherited_data_->caret_color_is_auto_)
+    if (CaretColorIsAutoInternal())
       return StyleAutoColor::AutoColor();
-    return StyleAutoColor(rare_inherited_data_->caret_color_);
+    return StyleAutoColor(CaretColorInternal());
   }
   Color GetColor() const;
   StyleColor ColumnRuleColor() const {
@@ -3417,26 +3386,26 @@
     return rare_non_inherited_data_->outline_.GetColor();
   }
   StyleColor TextEmphasisColor() const {
-    return rare_inherited_data_->text_emphasis_color_is_current_color_
+    return TextEmphasisColorIsCurrentColorInternal()
                ? StyleColor::CurrentColor()
-               : StyleColor(rare_inherited_data_->text_emphasis_color_);
+               : StyleColor(TextEmphasisColorInternal());
   }
   StyleColor TextFillColor() const {
-    return rare_inherited_data_->text_fill_color_is_current_color_
+    return TextFillColorIsCurrentColorInternal()
                ? StyleColor::CurrentColor()
-               : StyleColor(rare_inherited_data_->text_fill_color_);
+               : StyleColor(TextFillColorInternal());
   }
   StyleColor TextStrokeColor() const {
-    return rare_inherited_data_->text_stroke_color_is_current_color_
+    return TextStrokeColorIsCurrentColorInternal()
                ? StyleColor::CurrentColor()
-               : StyleColor(rare_inherited_data_->text_stroke_color_);
+               : StyleColor(TextStrokeColorInternal());
   }
   StyleAutoColor VisitedLinkCaretColor() const {
-    if (rare_inherited_data_->visited_link_caret_color_is_current_color_)
+    if (VisitedLinkCaretColorIsCurrentColorInternal())
       return StyleAutoColor::CurrentColor();
-    if (rare_inherited_data_->visited_link_caret_color_is_auto_)
+    if (VisitedLinkCaretColorIsAutoInternal())
       return StyleAutoColor::AutoColor();
-    return StyleAutoColor(rare_inherited_data_->visited_link_caret_color_);
+    return StyleAutoColor(VisitedLinkCaretColorInternal());
   }
   StyleColor VisitedLinkBackgroundColor() const {
     return rare_non_inherited_data_->visited_link_background_color_;
@@ -3467,24 +3436,19 @@
     return rare_non_inherited_data_->visited_link_text_decoration_color_;
   }
   StyleColor VisitedLinkTextEmphasisColor() const {
-    return rare_inherited_data_
-                   ->visited_link_text_emphasis_color_is_current_color_
+    return VisitedLinkTextEmphasisColorIsCurrentColorInternal()
                ? StyleColor::CurrentColor()
-               : StyleColor(
-                     rare_inherited_data_->visited_link_text_emphasis_color_);
+               : StyleColor(VisitedLinkTextEmphasisColorInternal());
   }
   StyleColor VisitedLinkTextFillColor() const {
-    return rare_inherited_data_->visited_link_text_fill_color_is_current_color_
+    return VisitedLinkTextFillColorIsCurrentColorInternal()
                ? StyleColor::CurrentColor()
-               : StyleColor(
-                     rare_inherited_data_->visited_link_text_fill_color_);
+               : StyleColor(VisitedLinkTextFillColorInternal());
   }
   StyleColor VisitedLinkTextStrokeColor() const {
-    return rare_inherited_data_
-                   ->visited_link_text_stroke_color_is_current_color_
+    return VisitedLinkTextStrokeColorIsCurrentColorInternal()
                ? StyleColor::CurrentColor()
-               : StyleColor(
-                     rare_inherited_data_->visited_link_text_stroke_color_);
+               : StyleColor(VisitedLinkTextStrokeColorInternal());
   }
 
   StyleColor DecorationColorIncludingFallback(bool visited_link) const;
@@ -3573,10 +3537,9 @@
   // Clamp the effective zoom value to a smaller (but hopeful still large
   // enough) range, to avoid overflow in derived computations.
   float clamped_effective_zoom = clampTo<float>(f, 1e-6, 1e6);
-  if (compareEqual(rare_inherited_data_->effective_zoom_,
-                   clamped_effective_zoom))
+  if (compareEqual(EffectiveZoomInternal(), clamped_effective_zoom))
     return false;
-  rare_inherited_data_.Access()->effective_zoom_ = clamped_effective_zoom;
+  SetEffectiveZoomInternal(clamped_effective_zoom);
   return true;
 }
 
@@ -3588,17 +3551,6 @@
   return true;
 }
 
-inline bool ComputedStyle::SetTextOrientation(
-    ETextOrientation text_orientation) {
-  if (compareEqual(rare_inherited_data_->text_orientation_,
-                   static_cast<unsigned>(text_orientation)))
-    return false;
-
-  rare_inherited_data_.Access()->text_orientation_ =
-      static_cast<unsigned>(text_orientation);
-  return true;
-}
-
 inline bool ComputedStyle::HasAnyPublicPseudoStyles() const {
   return PseudoBitsInternal() != kPseudoIdNone;
 }
diff --git a/third_party/WebKit/Source/core/testing/DummyModulator.cpp b/third_party/WebKit/Source/core/testing/DummyModulator.cpp
index d5c46fde..4cfd94f5 100644
--- a/third_party/WebKit/Source/core/testing/DummyModulator.cpp
+++ b/third_party/WebKit/Source/core/testing/DummyModulator.cpp
@@ -113,7 +113,7 @@
   return ScriptValue();
 }
 
-ScriptValue DummyModulator::GetInstantiationError(const ModuleScript*) {
+ScriptValue DummyModulator::GetError(const ModuleScript*) {
   NOTREACHED();
   return ScriptValue();
 }
diff --git a/third_party/WebKit/Source/core/testing/DummyModulator.h b/third_party/WebKit/Source/core/testing/DummyModulator.h
index e22cb37..f9f4b6c 100644
--- a/third_party/WebKit/Source/core/testing/DummyModulator.h
+++ b/third_party/WebKit/Source/core/testing/DummyModulator.h
@@ -57,7 +57,7 @@
                              AccessControlStatus,
                              const TextPosition&) override;
   ScriptValue InstantiateModule(ScriptModule) override;
-  ScriptValue GetInstantiationError(const ModuleScript*) override;
+  ScriptValue GetError(const ModuleScript*) override;
   Vector<String> ModuleRequestsFromScriptModule(ScriptModule) override;
   void ExecuteModule(const ModuleScript*) override;
 
diff --git a/third_party/WebKit/Source/core/xml/XSLTProcessorLibxslt.cpp b/third_party/WebKit/Source/core/xml/XSLTProcessorLibxslt.cpp
index d60272e..b610c52 100644
--- a/third_party/WebKit/Source/core/xml/XSLTProcessorLibxslt.cpp
+++ b/third_party/WebKit/Source/core/xml/XSLTProcessorLibxslt.cpp
@@ -344,17 +344,14 @@
 
     xsltSecurityPrefsPtr security_prefs = xsltNewSecurityPrefs();
     // Read permissions are checked by docLoaderFunc.
-    if (0 != xsltSetSecurityPrefs(security_prefs, XSLT_SECPREF_WRITE_FILE,
-                                  xsltSecurityForbid))
-      IMMEDIATE_CRASH();
-    if (0 != xsltSetSecurityPrefs(security_prefs, XSLT_SECPREF_CREATE_DIRECTORY,
-                                  xsltSecurityForbid))
-      IMMEDIATE_CRASH();
-    if (0 != xsltSetSecurityPrefs(security_prefs, XSLT_SECPREF_WRITE_NETWORK,
-                                  xsltSecurityForbid))
-      IMMEDIATE_CRASH();
-    if (0 != xsltSetCtxtSecurityPrefs(security_prefs, transform_context))
-      IMMEDIATE_CRASH();
+    CHECK_EQ(0, xsltSetSecurityPrefs(security_prefs, XSLT_SECPREF_WRITE_FILE,
+                                     xsltSecurityForbid));
+    CHECK_EQ(0,
+             xsltSetSecurityPrefs(security_prefs, XSLT_SECPREF_CREATE_DIRECTORY,
+                                  xsltSecurityForbid));
+    CHECK_EQ(0, xsltSetSecurityPrefs(security_prefs, XSLT_SECPREF_WRITE_NETWORK,
+                                     xsltSecurityForbid));
+    CHECK_EQ(0, xsltSetCtxtSecurityPrefs(security_prefs, transform_context));
 
     // <http://bugs.webkit.org/show_bug.cgi?id=16077>: XSLT processor
     // <xsl:sort> algorithm only compares by code point.
diff --git a/third_party/WebKit/Source/modules/app_banner/BeforeInstallPromptEvent.cpp b/third_party/WebKit/Source/modules/app_banner/BeforeInstallPromptEvent.cpp
index 096767a..47d459e 100644
--- a/third_party/WebKit/Source/modules/app_banner/BeforeInstallPromptEvent.cpp
+++ b/third_party/WebKit/Source/modules/app_banner/BeforeInstallPromptEvent.cpp
@@ -8,6 +8,7 @@
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
 #include "core/dom/ExecutionContext.h"
+#include "core/dom/UserGestureIndicator.h"
 #include "core/frame/UseCounter.h"
 #include "modules/app_banner/BeforeInstallPromptEventInit.h"
 
@@ -79,7 +80,8 @@
                     UseCounter::kBeforeInstallPromptEventPrompt);
 
   prompt_called_ = true;
-  banner_service_->DisplayAppBanner();
+  banner_service_->DisplayAppBanner(
+      UserGestureIndicator::ProcessingUserGesture());
   return ScriptPromise::CastUndefined(script_state);
 }
 
diff --git a/third_party/WebKit/Source/platform/bindings/V8PerContextData.h b/third_party/WebKit/Source/platform/bindings/V8PerContextData.h
index e6f2981..8e016e2 100644
--- a/third_party/WebKit/Source/platform/bindings/V8PerContextData.h
+++ b/third_party/WebKit/Source/platform/bindings/V8PerContextData.h
@@ -101,6 +101,20 @@
 
   void AddCustomElementBinding(std::unique_ptr<V0CustomElementBinding>);
 
+  // Gets a Private to store custom element definition IDs on a
+  // constructor that has been registered as a custom element in this
+  // context. This private has to be per-context because the same
+  // constructor could be simultaneously registered as a custom
+  // element in many contexts and they each need to give it a unique
+  // identifier.
+  v8::Local<v8::Private> GetPrivateCustomElementDefinitionId() {
+    if (UNLIKELY(private_custom_element_definition_id_.IsEmpty())) {
+      private_custom_element_definition_id_.Set(isolate_,
+                                                v8::Private::New(isolate_));
+    }
+    return private_custom_element_definition_id_.NewLocal(isolate_);
+  }
+
   V8DOMActivityLogger* ActivityLogger() const { return activity_logger_; }
   void SetActivityLogger(V8DOMActivityLogger* activity_logger) {
     activity_logger_ = activity_logger;
@@ -138,6 +152,8 @@
   ScopedPersistent<v8::Context> context_;
   ScopedPersistent<v8::Value> error_prototype_;
 
+  ScopedPersistent<v8::Private> private_custom_element_definition_id_;
+
   typedef Vector<std::unique_ptr<V0CustomElementBinding>>
       V0CustomElementBindingList;
   V0CustomElementBindingList custom_element_bindings_;
diff --git a/third_party/WebKit/Source/platform/bindings/V8PrivateProperty.h b/third_party/WebKit/Source/platform/bindings/V8PrivateProperty.h
index 2129c9c1..7136a10 100644
--- a/third_party/WebKit/Source/platform/bindings/V8PrivateProperty.h
+++ b/third_party/WebKit/Source/platform/bindings/V8PrivateProperty.h
@@ -27,7 +27,6 @@
   X(CustomElement, Document)                          \
   X(CustomElement, IsInterfacePrototypeObject)        \
   X(CustomElement, NamespaceURI)                      \
-  X(CustomElement, RegistryMap)                       \
   X(CustomElement, TagName)                           \
   X(CustomElement, Type)                              \
   X(CustomElementLifecycle, AttachedCallback)         \
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
index 1f1931d8..521ba090 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
@@ -387,15 +387,13 @@
 void GraphicsLayer::RegisterContentsLayer(WebLayer* layer) {
   if (!g_registered_layer_set)
     g_registered_layer_set = new HashSet<int>;
-  if (g_registered_layer_set->Contains(layer->Id()))
-    IMMEDIATE_CRASH();
+  CHECK(!g_registered_layer_set->Contains(layer->Id()));
   g_registered_layer_set->insert(layer->Id());
 }
 
 void GraphicsLayer::UnregisterContentsLayer(WebLayer* layer) {
   DCHECK(g_registered_layer_set);
-  if (!g_registered_layer_set->Contains(layer->Id()))
-    IMMEDIATE_CRASH();
+  CHECK(g_registered_layer_set->Contains(layer->Id()));
   g_registered_layer_set->erase(layer->Id());
 }
 
@@ -403,8 +401,7 @@
   bool children_changed = false;
   if (layer) {
     DCHECK(g_registered_layer_set);
-    if (!g_registered_layer_set->Contains(layer->Id()))
-      IMMEDIATE_CRASH();
+    CHECK(g_registered_layer_set->Contains(layer->Id()));
     if (contents_layer_id_ != layer->Id()) {
       SetupContentsLayer(layer);
       children_changed = true;
diff --git a/third_party/WebKit/Source/platform/mac/BlockExceptions.mm b/third_party/WebKit/Source/platform/mac/BlockExceptions.mm
index 6ae8c89..b1b06e7 100644
--- a/third_party/WebKit/Source/platform/mac/BlockExceptions.mm
+++ b/third_party/WebKit/Source/platform/mac/BlockExceptions.mm
@@ -33,7 +33,7 @@
                << [[exception description] UTF8String];
   // This function is marked as NO_RETURN_DUE_TO_ASSERT, but NOTREACHED() and
   // DCHECK(false) are not recognized as NO_RETURN.
-  IMMEDIATE_CRASH();
+  LOG(FATAL);
 #else
   NSLog(@"*** WebKit discarding exception: <%@> %@", [exception name],
         [exception reason]);
diff --git a/third_party/WebKit/Source/platform/wtf/AssertionsTest.cpp b/third_party/WebKit/Source/platform/wtf/AssertionsTest.cpp
index abaa2a7521..3093f7c 100644
--- a/third_party/WebKit/Source/platform/wtf/AssertionsTest.cpp
+++ b/third_party/WebKit/Source/platform/wtf/AssertionsTest.cpp
@@ -27,9 +27,6 @@
 
   SECURITY_CHECK(true);
   EXPECT_DEATH_IF_SUPPORTED(SECURITY_CHECK(false), "");
-
-  EXPECT_DEATH_IF_SUPPORTED(IMMEDIATE_CRASH(), "");
-  EXPECT_DEATH_IF_SUPPORTED(IMMEDIATE_CRASH(), "");
 };
 
 #if !LOG_DISABLED
diff --git a/third_party/WebKit/Source/platform/wtf/ThreadSpecific.h b/third_party/WebKit/Source/platform/wtf/ThreadSpecific.h
index f1582d9..cad2797 100644
--- a/third_party/WebKit/Source/platform/wtf/ThreadSpecific.h
+++ b/third_party/WebKit/Source/platform/wtf/ThreadSpecific.h
@@ -125,14 +125,12 @@
 inline void ThreadSpecificKeyCreate(ThreadSpecificKey* key,
                                     void (*destructor)(void*)) {
   int error = pthread_key_create(key, destructor);
-  if (error)
-    IMMEDIATE_CRASH();
+  CHECK(!error);
 }
 
 inline void ThreadSpecificKeyDelete(ThreadSpecificKey key) {
   int error = pthread_key_delete(key);
-  if (error)
-    IMMEDIATE_CRASH();
+  CHECK(!error);
 }
 
 inline void ThreadSpecificSet(ThreadSpecificKey key, void* value) {
@@ -146,8 +144,7 @@
 template <typename T>
 inline ThreadSpecific<T>::ThreadSpecific() {
   int error = pthread_key_create(&key_, Destroy);
-  if (error)
-    IMMEDIATE_CRASH();
+  CHECK(!error);
 }
 
 template <typename T>
@@ -191,12 +188,10 @@
 template <typename T>
 inline ThreadSpecific<T>::ThreadSpecific() : index_(-1) {
   DWORD tls_key = TlsAlloc();
-  if (tls_key == TLS_OUT_OF_INDEXES)
-    IMMEDIATE_CRASH();
+  CHECK_NE(tls_key, TLS_OUT_OF_INDEXES);
 
   index_ = InterlockedIncrement(&TlsKeyCount()) - 1;
-  if (index_ >= kMaxTlsKeySize)
-    IMMEDIATE_CRASH();
+  CHECK_LE(index_, kMaxTlsKeySize);
   TlsKeys()[index_] = tls_key;
 }
 
diff --git a/third_party/WebKit/Source/platform/wtf/ThreadSpecificWin.cpp b/third_party/WebKit/Source/platform/wtf/ThreadSpecificWin.cpp
index b54917ec..5f12896 100644
--- a/third_party/WebKit/Source/platform/wtf/ThreadSpecificWin.cpp
+++ b/third_party/WebKit/Source/platform/wtf/ThreadSpecificWin.cpp
@@ -52,8 +52,7 @@
   PlatformThreadSpecificKey(void (*destructor)(void*))
       : destructor_(destructor) {
     tls_key_ = TlsAlloc();
-    if (tls_key_ == TLS_OUT_OF_INDEXES)
-      IMMEDIATE_CRASH();
+    CHECK_NE(tls_key_, TLS_OUT_OF_INDEXES);
   }
 
   ~PlatformThreadSpecificKey() { TlsFree(tls_key_); }
diff --git a/third_party/WebKit/Source/platform/wtf/text/StringImpl.cpp b/third_party/WebKit/Source/platform/wtf/text/StringImpl.cpp
index 10def1fa..aeba8335 100644
--- a/third_party/WebKit/Source/platform/wtf/text/StringImpl.cpp
+++ b/third_party/WebKit/Source/platform/wtf/text/StringImpl.cpp
@@ -650,8 +650,7 @@
   else
     return LowerUnicode();
 
-  if (length_ > static_cast<unsigned>(numeric_limits<int32_t>::max()))
-    IMMEDIATE_CRASH();
+  CHECK_LE(length_, static_cast<unsigned>(numeric_limits<int32_t>::max()));
   int length = length_;
 
   RefPtr<StringImpl> upconverted = UpconvertedString();
@@ -676,8 +675,7 @@
   else
     return UpperUnicode();
 
-  if (length_ > static_cast<unsigned>(numeric_limits<int32_t>::max()))
-    IMMEDIATE_CRASH();
+  CHECK_LE(length_, static_cast<unsigned>(numeric_limits<int32_t>::max()));
   int length = length_;
 
   RefPtr<StringImpl> upconverted = UpconvertedString();
diff --git a/third_party/WebKit/Source/platform/wtf/text/TextCodecUTF8.cpp b/third_party/WebKit/Source/platform/wtf/text/TextCodecUTF8.cpp
index 55f0ce5..315f451 100644
--- a/third_party/WebKit/Source/platform/wtf/text/TextCodecUTF8.cpp
+++ b/third_party/WebKit/Source/platform/wtf/text/TextCodecUTF8.cpp
@@ -463,8 +463,7 @@
   // (3x).
   // Non-BMP characters take two UTF-16 code units and can take up to 4 bytes
   // (2x).
-  if (length > std::numeric_limits<size_t>::max() / 3)
-    IMMEDIATE_CRASH();
+  CHECK_LE(length, std::numeric_limits<size_t>::max() / 3);
   Vector<uint8_t> bytes(length * 3);
 
   size_t i = 0;
diff --git a/third_party/WebKit/Source/web/WebPagePopupImpl.cpp b/third_party/WebKit/Source/web/WebPagePopupImpl.cpp
index 6a3e3e1..5a6fa555 100644
--- a/third_party/WebKit/Source/web/WebPagePopupImpl.cpp
+++ b/third_party/WebKit/Source/web/WebPagePopupImpl.cpp
@@ -599,8 +599,7 @@
 // WebPagePopup ----------------------------------------------------------------
 
 WebPagePopup* WebPagePopup::Create(WebWidgetClient* client) {
-  if (!client)
-    IMMEDIATE_CRASH();
+  CHECK(client);
   // A WebPagePopupImpl instance usually has two references.
   //  - One owned by the instance itself. It represents the visible widget.
   //  - One owned by a WebViewBase. It's released when the WebViewBase ask the
diff --git a/third_party/WebKit/public/platform/modules/app_banner/app_banner.mojom b/third_party/WebKit/public/platform/modules/app_banner/app_banner.mojom
index b0fd835..c2cd2c3b 100644
--- a/third_party/WebKit/public/platform/modules/app_banner/app_banner.mojom
+++ b/third_party/WebKit/public/platform/modules/app_banner/app_banner.mojom
@@ -24,5 +24,5 @@
 
 interface AppBannerService {
   // The renderer asks the browser to display a previously offered app banner.
-  DisplayAppBanner();
+  DisplayAppBanner(bool user_gesture);
 };
diff --git a/ui/file_manager/file_manager/background/js/file_operation_util.js b/ui/file_manager/file_manager/background/js/file_operation_util.js
index 19ba98c..73bb726 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_util.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_util.js
@@ -684,6 +684,13 @@
     fileOperationUtil.Task.prototype;
 
 /**
+ * Number of consecutive errors to stop CopyTask.
+ * @const {number}
+ * @private
+ */
+fileOperationUtil.CopyTask.CONSECUTIVE_ERROR_LIMIT_ = 100;
+
+/**
  * Initializes the CopyTask.
  * @param {function()} callback Called when the initialize is completed.
  */
@@ -800,6 +807,11 @@
 
   this.updateProgressRateLimiter_ = new AsyncUtil.RateLimiter(progressCallback);
 
+  // Number of consecutive errors. Increases while failing and resets to zero
+  // when one of them succeeds.
+  var errorCount = 0;
+  var lastError;
+
   AsyncUtil.forEach(
       this.sourceEntries,
       function(callback, entry, index) {
@@ -830,16 +842,29 @@
               // Update current source index and processing bytes.
               this.processingSourceIndex_ = index + 1;
               this.processedBytes = this.calcProcessedBytes_();
+              errorCount = 0;
               callback();
             }.bind(this),
             function(error) {
               // Finishes off delayed updates if necessary.
               this.updateProgressRateLimiter_.runImmediately();
-              errorCallback(error);
+              // Update current source index and processing bytes.
+              this.processingSourceIndex_ = index + 1;
+              this.processedBytes = this.calcProcessedBytes_();
+              errorCount++;
+              lastError = error;
+              if (errorCount <
+                  fileOperationUtil.CopyTask.CONSECUTIVE_ERROR_LIMIT_) {
+                callback();
+              } else {
+                errorCallback(error);
+              }
             }.bind(this));
       },
       function() {
-        if (this.deleteAfterCopy) {
+        if (lastError) {
+          errorCallback(lastError);
+        } else if (this.deleteAfterCopy) {
           deleteOriginals();
         } else {
           successCallback();