diff --git a/chrome/browser/chrome_content_utility_manifest_overlay.json b/chrome/browser/chrome_content_utility_manifest_overlay.json
index b6937f6..d53f298 100644
--- a/chrome/browser/chrome_content_utility_manifest_overlay.json
+++ b/chrome/browser/chrome_content_utility_manifest_overlay.json
@@ -10,6 +10,7 @@
           "chrome::mojom::SafeArchiveAnalyzer",
           "chrome::mojom::ShellHandler",
           "chrome::mojom::ZipFileCreator",
+          "extensions::mojom::ExtensionUnpacker",
           "extensions::mojom::MediaParser",
           "extensions::mojom::RemovableStorageWriter",
           "extensions::mojom::WiFiCredentialsGetter",
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 13c322c4..1c6ccd6 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -314,6 +314,8 @@
     "arc/process/arc_process.h",
     "arc/process/arc_process_service.cc",
     "arc/process/arc_process_service.h",
+    "arc/tracing/arc_tracing_bridge.cc",
+    "arc/tracing/arc_tracing_bridge.h",
     "arc/tts/arc_tts_service.cc",
     "arc/tts/arc_tts_service.h",
     "arc/video/gpu_arc_video_service_host.cc",
diff --git a/chrome/browser/chromeos/arc/arc_service_launcher.cc b/chrome/browser/chromeos/arc/arc_service_launcher.cc
index 0365cbb..336048c 100644
--- a/chrome/browser/chromeos/arc/arc_service_launcher.cc
+++ b/chrome/browser/chromeos/arc/arc_service_launcher.cc
@@ -27,6 +27,7 @@
 #include "chrome/browser/chromeos/arc/policy/arc_policy_util.h"
 #include "chrome/browser/chromeos/arc/print/arc_print_service.h"
 #include "chrome/browser/chromeos/arc/process/arc_process_service.h"
+#include "chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.h"
 #include "chrome/browser/chromeos/arc/tts/arc_tts_service.h"
 #include "chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.h"
 #include "chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service.h"
@@ -135,6 +136,8 @@
   arc_service_manager_->AddService(
       base::MakeUnique<ArcStorageManager>(arc_bridge_service));
   arc_service_manager_->AddService(
+      base::MakeUnique<ArcTracingBridge>(arc_bridge_service));
+  arc_service_manager_->AddService(
       base::MakeUnique<ArcTtsService>(arc_bridge_service));
   arc_service_manager_->AddService(
       base::MakeUnique<ArcWallpaperService>(arc_bridge_service));
diff --git a/chrome/browser/chromeos/arc/tracing/DEPS b/chrome/browser/chromeos/arc/tracing/DEPS
new file mode 100644
index 0000000..e94e96a
--- /dev/null
+++ b/chrome/browser/chromeos/arc/tracing/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+content/browser/tracing/arc_tracing_agent.h"
+]
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.cc b/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.cc
new file mode 100644
index 0000000..b45a4e1
--- /dev/null
+++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.cc
@@ -0,0 +1,106 @@
+// 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/chromeos/arc/tracing/arc_tracing_bridge.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/arc/arc_bridge_service.h"
+#include "components/arc/arc_service_manager.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace arc {
+
+namespace {
+
+// The prefix of the categories to be shown on the trace selection UI.
+// The space at the end of the string is intentional as the separator between
+// the prefix and the real categories.
+constexpr char kCategoryPrefix[] = TRACE_DISABLED_BY_DEFAULT("android ");
+
+}  // namespace
+
+struct ArcTracingBridge::Category {
+  // The name used by Android to trigger tracing.
+  std::string name;
+  // The full name shown in the tracing UI in chrome://tracing.
+  std::string full_name;
+};
+
+ArcTracingBridge::ArcTracingBridge(ArcBridgeService* bridge_service)
+    : ArcService(bridge_service), weak_ptr_factory_(this) {
+  arc_bridge_service()->tracing()->AddObserver(this);
+  content::ArcTracingAgent::GetInstance()->SetDelegate(this);
+}
+
+ArcTracingBridge::~ArcTracingBridge() {
+  content::ArcTracingAgent::GetInstance()->SetDelegate(nullptr);
+  arc_bridge_service()->tracing()->RemoveObserver(this);
+}
+
+void ArcTracingBridge::OnInstanceReady() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  mojom::TracingInstance* tracing_instance = ARC_GET_INSTANCE_FOR_METHOD(
+      arc_bridge_service()->tracing(), QueryAvailableCategories);
+  if (!tracing_instance)
+    return;
+  tracing_instance->QueryAvailableCategories(base::Bind(
+      &ArcTracingBridge::OnCategoriesReady, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ArcTracingBridge::OnCategoriesReady(
+    const std::vector<std::string>& categories) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  // There is no API in TraceLog to remove a category from the UI. As an
+  // alternative, the old category that is no longer in |categories_| will be
+  // ignored when calling |StartTracing|.
+  categories_.clear();
+  for (const auto& category : categories) {
+    categories_.push_back({category, kCategoryPrefix + category});
+    // Show the category name in the selection UI.
+    base::trace_event::TraceLog::GetCategoryGroupEnabled(
+        categories_.back().full_name.c_str());
+  }
+}
+
+void ArcTracingBridge::StartTracing(
+    const base::trace_event::TraceConfig& trace_config,
+    const StartTracingCallback& callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  mojom::TracingInstance* tracing_instance = ARC_GET_INSTANCE_FOR_METHOD(
+      arc_bridge_service()->tracing(), StartTracing);
+  if (!tracing_instance) {
+    // Use PostTask as the convention of TracingAgent. The caller expects
+    // callback to be called after this function returns.
+    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                  base::Bind(callback, false));
+    return;
+  }
+
+  std::vector<std::string> selected_categories;
+  for (const auto& category : categories_) {
+    if (trace_config.IsCategoryGroupEnabled(category.full_name.c_str()))
+      selected_categories.push_back(category.name);
+  }
+
+  tracing_instance->StartTracing(selected_categories, callback);
+}
+
+void ArcTracingBridge::StopTracing(const StopTracingCallback& callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  mojom::TracingInstance* tracing_instance =
+      ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->tracing(), StopTracing);
+  if (!tracing_instance) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                  base::Bind(callback, false));
+    return;
+  }
+  tracing_instance->StopTracing(callback);
+}
+
+}  // namespace arc
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.h b/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.h
new file mode 100644
index 0000000..0fd911a1
--- /dev/null
+++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.h
@@ -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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_ARC_TRACING_ARC_TRACING_BRIDGE_H_
+#define CHROME_BROWSER_CHROMEOS_ARC_TRACING_ARC_TRACING_BRIDGE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/trace_event/trace_event.h"
+#include "components/arc/arc_service.h"
+#include "components/arc/common/tracing.mojom.h"
+#include "components/arc/instance_holder.h"
+#include "content/browser/tracing/arc_tracing_agent.h"
+
+namespace arc {
+
+class ArcBridgeService;
+
+// This class provides the interface to trigger tracing in the container.
+class ArcTracingBridge
+    : public ArcService,
+      public content::ArcTracingAgent::Delegate,
+      public InstanceHolder<mojom::TracingInstance>::Observer {
+ public:
+  explicit ArcTracingBridge(ArcBridgeService* bridge_service);
+  ~ArcTracingBridge() override;
+
+  // InstanceHolder<mojom::TracingInstance>::Observer overrides:
+  void OnInstanceReady() override;
+
+  // content::ArcTracingAgent::Delegate overrides:
+  void StartTracing(const base::trace_event::TraceConfig& trace_config,
+                    const StartTracingCallback& callback) override;
+  void StopTracing(const StopTracingCallback& callback) override;
+
+ private:
+  struct Category;
+
+  // Callback for QueryAvailableCategories.
+  void OnCategoriesReady(const std::vector<std::string>& categories);
+
+  // List of available categories.
+  std::vector<Category> categories_;
+
+  // NOTE: Weak pointers must be invalidated before all other member variables
+  // so it must be the last member.
+  base::WeakPtrFactory<ArcTracingBridge> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArcTracingBridge);
+};
+
+}  // namespace arc
+
+#endif  // CHROME_BROWSER_CHROMEOS_ARC_TRACING_ARC_TRACING_BRIDGE_H_
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index c07cb583..6ba3c6d 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -895,6 +895,7 @@
     "//extensions:extensions_resources",
     "//extensions/browser",
     "//extensions/browser/api:api_registration",
+    "//extensions/common",
     "//extensions/common/api",
     "//extensions/features",
     "//extensions/strings",
diff --git a/chrome/browser/extensions/zipfile_installer.cc b/chrome/browser/extensions/zipfile_installer.cc
index f0da06d..d838136 100644
--- a/chrome/browser/extensions/zipfile_installer.cc
+++ b/chrome/browser/extensions/zipfile_installer.cc
@@ -6,128 +6,115 @@
 
 #include "base/files/file_util.h"
 #include "base/path_service.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/extensions/extension_error_reporter.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/unpacked_installer.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/grit/generated_resources.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/utility_process_host.h"
-#include "extensions/common/extension_utility_messages.h"
+#include "extensions/common/extension_unpacker.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
 
-using content::BrowserThread;
-using content::UtilityProcessHost;
-
 namespace {
 
 const char kExtensionHandlerTempDirError[] =
     "Could not create temporary directory for zipped extension.";
+const char kExtensionHandlerFileUnzipError[] =
+    "Could not unzip extension for install.";
 
 }  // namespace
 
 namespace extensions {
 
-ZipFileInstaller::ZipFileInstaller(ExtensionService* extension_service)
-    : be_noisy_on_failure_(true),
-      extension_service_weak_(extension_service->AsWeakPtr()) {
-}
-
-void ZipFileInstaller::LoadFromZipFile(const base::FilePath& path) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  zip_path_ = path;
-  BrowserThread::PostTask(BrowserThread::FILE,
-                          FROM_HERE,
-                          base::Bind(&ZipFileInstaller::PrepareTempDir, this));
-}
-
-void ZipFileInstaller::PrepareTempDir() {
-  CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
-  base::FilePath temp_dir;
-  PathService::Get(base::DIR_TEMP, &temp_dir);
-  base::FilePath new_temp_dir;
-  if (!base::CreateTemporaryDirInDir(
-          temp_dir,
-          zip_path_.RemoveExtension().BaseName().value() +
-              FILE_PATH_LITERAL("_"),
-          &new_temp_dir)) {
-    OnUnzipFailed(std::string(kExtensionHandlerTempDirError));
-    return;
-  }
-  BrowserThread::PostTask(
-      BrowserThread::IO,
-      FROM_HERE,
-      base::Bind(&ZipFileInstaller::StartWorkOnIOThread, this, new_temp_dir));
-}
-
-ZipFileInstaller::~ZipFileInstaller() {
-}
-
 // static
 scoped_refptr<ZipFileInstaller> ZipFileInstaller::Create(
-    ExtensionService* extension_service) {
-  DCHECK(extension_service);
-  return scoped_refptr<ZipFileInstaller>(
-      new ZipFileInstaller(extension_service));
+    ExtensionService* service) {
+  DCHECK(service);
+  return make_scoped_refptr(new ZipFileInstaller(service));
 }
 
-void ZipFileInstaller::StartWorkOnIOThread(const base::FilePath& temp_dir) {
-  CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  UtilityProcessHost* host =
-      UtilityProcessHost::Create(this,
-                                 base::ThreadTaskRunnerHandle::Get().get());
-  host->SetName(l10n_util::GetStringUTF16(
-      IDS_UTILITY_PROCESS_ZIP_FILE_INSTALLER_NAME));
-  host->SetExposedDir(temp_dir);
-  host->Send(new ExtensionUtilityMsg_UnzipToDir(zip_path_, temp_dir));
+void ZipFileInstaller::LoadFromZipFile(const base::FilePath& zip_file) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(!zip_file.empty());
+
+  zip_file_ = zip_file;
+
+  content::BrowserThread::PostTask(
+      content::BrowserThread::FILE, FROM_HERE,
+      base::Bind(&ZipFileInstaller::PrepareUnzipDir, this, zip_file));
 }
 
-void ZipFileInstaller::ReportSuccessOnUIThread(
-    const base::FilePath& unzipped_path) {
-  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  if (extension_service_weak_.get())
-    UnpackedInstaller::Create(extension_service_weak_.get())
-        ->Load(unzipped_path);
+ZipFileInstaller::ZipFileInstaller(ExtensionService* service)
+    : be_noisy_on_failure_(true),
+      extension_service_weak_(service->AsWeakPtr()) {}
+
+ZipFileInstaller::~ZipFileInstaller() = default;
+
+void ZipFileInstaller::PrepareUnzipDir(const base::FilePath& zip_file) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
+
+  base::FilePath dir_temp;
+  base::PathService::Get(base::DIR_TEMP, &dir_temp);
+
+  base::FilePath::StringType dir_name =
+      zip_file.RemoveExtension().BaseName().value() + FILE_PATH_LITERAL("_");
+
+  base::FilePath unzip_dir;
+  if (!base::CreateTemporaryDirInDir(dir_temp, dir_name, &unzip_dir)) {
+    content::BrowserThread::PostTask(
+        content::BrowserThread::UI, FROM_HERE,
+        base::Bind(&ZipFileInstaller::ReportFailure, this,
+                   std::string(kExtensionHandlerTempDirError)));
+    return;
+  }
+
+  content::BrowserThread::PostTask(
+      content::BrowserThread::UI, FROM_HERE,
+      base::Bind(&ZipFileInstaller::Unzip, this, unzip_dir));
 }
 
-void ZipFileInstaller::ReportErrorOnUIThread(const std::string& error) {
-  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  if (extension_service_weak_.get()) {
+void ZipFileInstaller::Unzip(const base::FilePath& unzip_dir) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(!utility_process_mojo_client_);
+
+  utility_process_mojo_client_ = base::MakeUnique<
+      content::UtilityProcessMojoClient<mojom::ExtensionUnpacker>>(
+      l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_ZIP_FILE_INSTALLER_NAME));
+  utility_process_mojo_client_->set_error_callback(
+      base::Bind(&ZipFileInstaller::UnzipDone, this, unzip_dir, false));
+
+  utility_process_mojo_client_->set_exposed_directory(unzip_dir);
+
+  utility_process_mojo_client_->Start();
+
+  utility_process_mojo_client_->service()->Unzip(
+      zip_file_, unzip_dir,
+      base::Bind(&ZipFileInstaller::UnzipDone, this, unzip_dir));
+}
+
+void ZipFileInstaller::UnzipDone(const base::FilePath& unzip_dir,
+                                 bool success) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  utility_process_mojo_client_.reset();
+
+  if (!success) {
+    ReportFailure(kExtensionHandlerFileUnzipError);
+    return;
+  }
+
+  if (extension_service_weak_)
+    UnpackedInstaller::Create(extension_service_weak_.get())->Load(unzip_dir);
+}
+
+void ZipFileInstaller::ReportFailure(const std::string& error) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  if (extension_service_weak_) {
     ExtensionErrorReporter::GetInstance()->ReportLoadError(
-        zip_path_,
-        error,
-        extension_service_weak_->profile(),
+        zip_file_, error, extension_service_weak_->profile(),
         be_noisy_on_failure_);
   }
 }
 
-void ZipFileInstaller::OnUnzipSucceeded(const base::FilePath& unzipped_path) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  BrowserThread::PostTask(
-      BrowserThread::UI,
-      FROM_HERE,
-      base::Bind(
-          &ZipFileInstaller::ReportSuccessOnUIThread, this, unzipped_path));
-}
-
-void ZipFileInstaller::OnUnzipFailed(const std::string& error) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  BrowserThread::PostTask(
-      BrowserThread::UI,
-      FROM_HERE,
-      base::Bind(&ZipFileInstaller::ReportErrorOnUIThread, this, error));
-}
-
-bool ZipFileInstaller::OnMessageReceived(const IPC::Message& message) {
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(ZipFileInstaller, message)
-  IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnzipToDir_Succeeded,
-                      OnUnzipSucceeded)
-  IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnzipToDir_Failed, OnUnzipFailed)
-  IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-  return handled;
-}
-
 }  // namespace extensions
diff --git a/chrome/browser/extensions/zipfile_installer.h b/chrome/browser/extensions/zipfile_installer.h
index 39de613..608885c 100644
--- a/chrome/browser/extensions/zipfile_installer.h
+++ b/chrome/browser/extensions/zipfile_installer.h
@@ -8,50 +8,57 @@
 #include <memory>
 #include <string>
 
-#include "base/callback.h"
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
-#include "content/public/browser/utility_process_host_client.h"
+#include "content/public/browser/utility_process_mojo_client.h"
 
 class ExtensionService;
 
-namespace IPC {
-class Message;
-}
-
 namespace extensions {
 
-// ZipFileInstaller unzips an extension that is zipped up via a utility process.
-// The contents are then loaded via UnpackedInstaller.
-class ZipFileInstaller : public content::UtilityProcessHostClient {
- public:
-  static scoped_refptr<ZipFileInstaller> Create(
-      ExtensionService* extension_service);
+namespace mojom {
+class ExtensionUnpacker;
+}
 
-  void LoadFromZipFile(const base::FilePath& path);
+// ZipFileInstaller unzips an extension in a utility process. On success, the
+// extension content is loaded by an extensions::UnpackedInstaller. The class
+// lives on the UI thread.
+class ZipFileInstaller : public base::RefCountedThreadSafe<ZipFileInstaller> {
+ public:
+  static scoped_refptr<ZipFileInstaller> Create(ExtensionService* service);
+
+  void LoadFromZipFile(const base::FilePath& zip_file);
 
   void set_be_noisy_on_failure(bool value) { be_noisy_on_failure_ = value; }
 
-  // UtilityProcessHostClient
-  bool OnMessageReceived(const IPC::Message& message) override;
-
  private:
-  explicit ZipFileInstaller(ExtensionService* extension_service);
-  ~ZipFileInstaller() override;
+  friend class base::RefCountedThreadSafe<ZipFileInstaller>;
 
-  void PrepareTempDir();
-  void StartWorkOnIOThread(const base::FilePath& temp_dir);
-  void ReportSuccessOnUIThread(const base::FilePath& unzipped_path);
-  void ReportErrorOnUIThread(const std::string& error);
+  explicit ZipFileInstaller(ExtensionService* service);
+  ~ZipFileInstaller();
 
-  void OnUnzipSucceeded(const base::FilePath& unzipped_path);
-  void OnUnzipFailed(const std::string& error);
+  // Unzip an extension into |unzip_dir| and load it with an UnpackedInstaller.
+  void PrepareUnzipDir(const base::FilePath& zip_file);
+  void Unzip(const base::FilePath& unzip_dir);
+  void UnzipDone(const base::FilePath& unzip_dir, bool success);
 
+  // On failure, report the |error| reason.
+  void ReportFailure(const std::string& error);
+
+  // Passed to the ExtensionErrorReporter when reporting errors.
   bool be_noisy_on_failure_;
+
+  // Pointer to our controlling extension service.
   base::WeakPtr<ExtensionService> extension_service_weak_;
-  base::FilePath zip_path_;
+
+  // File containing the extension to unzip.
+  base::FilePath zip_file_;
+
+  // Utility process used to perform the unzip.
+  std::unique_ptr<content::UtilityProcessMojoClient<mojom::ExtensionUnpacker>>
+      utility_process_mojo_client_;
 
   DISALLOW_COPY_AND_ASSIGN(ZipFileInstaller);
 };
diff --git a/chrome/browser/memory/tab_manager.cc b/chrome/browser/memory/tab_manager.cc
index 74baafa..0cccbee9 100644
--- a/chrome/browser/memory/tab_manager.cc
+++ b/chrome/browser/memory/tab_manager.cc
@@ -20,6 +20,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/observer_list.h"
 #include "base/process/process.h"
+#include "base/rand_util.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -80,35 +81,17 @@
 const int kRecentTabDiscardIntervalSeconds = 60;
 #endif
 
-// If there has been no priority adjustment in this interval, assume the
-// machine was suspended and correct the timing statistics.
-const int kSuspendThresholdSeconds = kAdjustmentIntervalSeconds * 4;
-
-// A suspended renderer is suspended for this duration.
-constexpr base::TimeDelta kDurationOfRendererSuspension =
-    base::TimeDelta::FromSeconds(1200);
-
-// A resumed renderer is resumed for this duration.
-constexpr base::TimeDelta kDurationOfRendererResumption =
-    base::TimeDelta::FromSeconds(10);
-
 // The time during which a tab is protected from discarding after it stops being
 // audible.
 const int kAudioProtectionTimeSeconds = 60;
 
-int FindTabStripModelById(int64_t target_web_contents_id,
-                          TabStripModel** model) {
-  DCHECK(model);
-  for (auto* browser : *BrowserList::GetInstance()) {
-    TabStripModel* local_model = browser->tab_strip_model();
-    for (int idx = 0; idx < local_model->count(); idx++) {
-      WebContents* web_contents = local_model->GetWebContentsAt(idx);
-      int64_t web_contents_id = TabManager::IdFromWebContents(web_contents);
-      if (web_contents_id == target_web_contents_id) {
-        *model = local_model;
-        return idx;
-      }
-    }
+int FindWebContentsById(const TabStripModel* model,
+                        int64_t target_web_contents_id) {
+  for (int idx = 0; idx < model->count(); idx++) {
+    WebContents* web_contents = model->GetWebContentsAt(idx);
+    int64_t web_contents_id = TabManager::IdFromWebContents(web_contents);
+    if (web_contents_id == target_web_contents_id)
+      return idx;
   }
 
   return -1;
@@ -139,7 +122,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 // TabManager
 
-constexpr base::TimeDelta TabManager::kDefaultTimeToFirstPurge;
+constexpr base::TimeDelta TabManager::kDefaultMinTimeToPurge;
 
 TabManager::TabManager()
     : discard_count_(0),
@@ -227,13 +210,12 @@
   // https://docs.google.com/document/d/1hPHkKtXXBTlsZx9s-9U17XC-ofEIzPo9FYbBEc7PPbk/edit?usp=sharing
   std::string purge_and_suspend_time = variations::GetVariationParamValue(
       "PurgeAndSuspend", "purge-and-suspend-time");
-  unsigned int time_to_first_purge_sec = 0;
+  unsigned int min_time_to_purge_sec = 0;
   if (purge_and_suspend_time.empty() ||
-      !base::StringToUint(purge_and_suspend_time, &time_to_first_purge_sec))
-    time_to_first_suspension_ = kDefaultTimeToFirstPurge;
+      !base::StringToUint(purge_and_suspend_time, &min_time_to_purge_sec))
+    min_time_to_purge_ = kDefaultMinTimeToPurge;
   else
-    time_to_first_suspension_ =
-        base::TimeDelta::FromSeconds(time_to_first_purge_sec);
+    min_time_to_purge_ = base::TimeDelta::FromSeconds(min_time_to_purge_sec);
 }
 
 void TabManager::Stop() {
@@ -242,6 +224,36 @@
   memory_pressure_listener_.reset();
 }
 
+int TabManager::FindTabStripModelById(int64_t target_web_contents_id,
+                                      TabStripModel** model) const {
+  DCHECK(model);
+  // TODO(tasak): Move this code to a TabStripModel enumeration delegate!
+  if (!test_tab_strip_models_.empty()) {
+    for (size_t i = 0; i < test_tab_strip_models_.size(); ++i) {
+      TabStripModel* local_model =
+          const_cast<TabStripModel*>(test_tab_strip_models_[i].first);
+      int idx = FindWebContentsById(local_model, target_web_contents_id);
+      if (idx != -1) {
+        *model = local_model;
+        return idx;
+      }
+    }
+
+    return -1;
+  }
+
+  for (auto* browser : *BrowserList::GetInstance()) {
+    TabStripModel* local_model = browser->tab_strip_model();
+    int idx = FindWebContentsById(local_model, target_web_contents_id);
+    if (idx != -1) {
+      *model = local_model;
+      return idx;
+    }
+  }
+
+  return -1;
+}
+
 TabStatsList TabManager::GetTabStats() const {
   TabStatsList stats_list(GetUnsortedTabStats());
 
@@ -452,7 +464,7 @@
 }
 
 bool TabManager::CanSuspendBackgroundedRenderer(int render_process_id) const {
-  // A renderer can be suspended if it's not playing media.
+  // A renderer can be purged if it's not playing media.
   auto tab_stats = GetUnsortedTabStats();
   for (auto& tab : tab_stats) {
     if (tab.child_process_host_id != render_process_id)
@@ -685,8 +697,7 @@
 // This function is called when |update_timer_| fires. It will adjust the clock
 // if needed (if it detects that the machine was asleep) and will fire the stats
 // updating on ChromeOS via the delegate. This function also tries to purge
-// cache memory and suspend tabs which becomes and keeps backgrounded for a
-// while.
+// cache memory.
 void TabManager::UpdateTimerCallback() {
   // If Chrome is shutting down, do not do anything.
   if (g_browser_process->IsShuttingDown())
@@ -695,17 +706,6 @@
   if (BrowserList::GetInstance()->empty())
     return;
 
-  // Check for a discontinuity in time caused by the machine being suspended.
-  if (!last_adjust_time_.is_null()) {
-    TimeDelta suspend_time = NowTicks() - last_adjust_time_;
-    if (suspend_time.InSeconds() > kSuspendThresholdSeconds) {
-      // System was probably suspended, move the event timers forward in time so
-      // when they get subtracted out later, "uptime" is being counted.
-      start_time_ += suspend_time;
-      if (!last_discard_time_.is_null())
-        last_discard_time_ += suspend_time;
-    }
-  }
   last_adjust_time_ = NowTicks();
 
 #if defined(OS_CHROMEOS)
@@ -714,38 +714,26 @@
   delegate_->AdjustOomPriorities(stats_list);
 #endif
 
-  PurgeAndSuspendBackgroundedTabs();
+  PurgeBackgroundedTabsIfNeeded();
 }
 
-TabManager::PurgeAndSuspendState TabManager::GetNextPurgeAndSuspendState(
-    content::WebContents* content,
-    base::TimeTicks current_time,
-    const base::TimeDelta& time_to_first_suspension) const {
-  DCHECK(content);
-  PurgeAndSuspendState state =
-      GetWebContentsData(content)->GetPurgeAndSuspendState();
-
-  auto time_passed = current_time -
-      GetWebContentsData(content)->LastPurgeAndSuspendModifiedTime();
-  switch (state) {
-    case RUNNING:
-      if (time_passed > time_to_first_suspension)
-        return SUSPENDED;
-      break;
-    case RESUMED:
-      if (time_passed > kDurationOfRendererResumption)
-        return SUSPENDED;
-      break;
-    case SUSPENDED:
-      if (time_passed > kDurationOfRendererSuspension)
-        return RESUMED;
-      break;
-  }
-  return state;
+base::TimeDelta TabManager::GetTimeToPurge(
+    base::TimeDelta min_time_to_purge) const {
+  return base::TimeDelta::FromSeconds(
+      base::RandInt(min_time_to_purge.InSeconds(),
+                    min_time_to_purge.InSeconds() * kMinMaxTimeToPurgeRatio));
 }
 
-void TabManager::PurgeAndSuspendBackgroundedTabs() {
-  base::TimeTicks current_time = NowTicks();
+bool TabManager::ShouldPurgeNow(content::WebContents* content) const {
+  if (GetWebContentsData(content)->is_purged())
+    return false;
+
+  base::TimeDelta time_passed =
+      NowTicks() - GetWebContentsData(content)->LastInactiveTime();
+  return time_passed > GetWebContentsData(content)->time_to_purge();
+}
+
+void TabManager::PurgeBackgroundedTabsIfNeeded() {
   auto tab_stats = GetUnsortedTabStats();
   for (auto& tab : tab_stats) {
     if (!tab.render_process_host->IsProcessBackgrounded())
@@ -757,34 +745,16 @@
     if (!content)
       continue;
 
-    PurgeAndSuspendState current_state =
-        GetWebContentsData(content)->GetPurgeAndSuspendState();
-    // If the tab's purge-and-suspend state is not RUNNING, the tab should be
-    // backgrounded. Since tab.last_hidden is updated everytime the tab is
-    // hidden, we should see tab.last_hidden < last_modified_time.
-    DCHECK(current_state == RUNNING ||
-           tab.last_hidden <
-               GetWebContentsData(content)->LastPurgeAndSuspendModifiedTime());
-    PurgeAndSuspendState next_state = GetNextPurgeAndSuspendState(
-        content, current_time, time_to_first_suspension_);
-    if (current_state == next_state)
+    bool purge_now = ShouldPurgeNow(content);
+    if (!purge_now)
       continue;
 
-    // TODO(hajimehoshi): Now calling PurgeAndSuspend is implemented without
-    // timers for simplicity, so PurgeAndSuspend is called even after the
-    // renderer is purged and suspended once. This should be replaced with
-    // timers if we want necessary and sufficient signals.
-    GetWebContentsData(content)->SetPurgeAndSuspendState(next_state);
-    switch (next_state) {
-      case SUSPENDED:
-        tab.render_process_host->PurgeAndSuspend();
-        break;
-      case RESUMED:
-        tab.render_process_host->Resume();
-        break;
-      case RUNNING:
-        NOTREACHED();
-    }
+    // Since |content|'s tab is kept inactive and background for more than
+    // time-to-purge time, its purged state changes: false => true.
+    GetWebContentsData(content)->set_is_purged(true);
+    // TODO(tasak): rename PurgeAndSuspend with a better name, e.g.
+    // RequestPurgeCache, because we don't suspend any renderers.
+    tab.render_process_host->PurgeAndSuspend();
   }
 }
 
@@ -824,6 +794,9 @@
   GetWebContentsData(null_contents)->SetDiscardState(true);
   GetWebContentsData(null_contents)->IncrementDiscardCount();
 
+  // Make the tab PURGED to avoid purging null_contents.
+  GetWebContentsData(null_contents)->set_is_purged(true);
+
   // Discard the old tab's renderer.
   // TODO(jamescook): This breaks script connections with other tabs.
   // Find a different approach that doesn't do that, perhaps based on navigation
@@ -885,11 +858,16 @@
                                   int index,
                                   int reason) {
   GetWebContentsData(new_contents)->SetDiscardState(false);
-  GetWebContentsData(new_contents)->SetPurgeAndSuspendState(RUNNING);
+  // When ActiveTabChanged, |new_contents| purged state changes to be false.
+  GetWebContentsData(new_contents)->set_is_purged(false);
   // If |old_contents| is set, that tab has switched from being active to
   // inactive, so record the time of that transition.
-  if (old_contents)
+  if (old_contents) {
     GetWebContentsData(old_contents)->SetLastInactiveTime(NowTicks());
+    // Re-setting time-to-purge every time a tab becomes inactive.
+    GetWebContentsData(old_contents)
+        ->set_time_to_purge(GetTimeToPurge(min_time_to_purge_));
+  }
 }
 
 void TabManager::TabInsertedAt(TabStripModel* tab_strip_model,
@@ -904,6 +882,9 @@
   // A new background tab is similar to having a tab switch from being active to
   // inactive.
   GetWebContentsData(contents)->SetLastInactiveTime(NowTicks());
+  // Re-setting time-to-purge every time a tab becomes inactive.
+  GetWebContentsData(contents)->set_time_to_purge(
+      GetTimeToPurge(min_time_to_purge_));
 }
 
 bool TabManager::IsMediaTab(WebContents* contents) const {
diff --git a/chrome/browser/memory/tab_manager.h b/chrome/browser/memory/tab_manager.h
index c384b7e..ecc23053 100644
--- a/chrome/browser/memory/tab_manager.h
+++ b/chrome/browser/memory/tab_manager.h
@@ -147,7 +147,9 @@
   // Sets/clears the auto-discardable state of the tab.
   void SetTabAutoDiscardableState(content::WebContents* contents, bool state);
 
-  // Returns true when a given renderer can suspend when it is backgrounded.
+  // Returns true when a given renderer can be purged if the specified
+  // renderer is eligible for purging.
+  // TODO(tasak): rename this to CanPurgeBackgroundedRenderer.
   bool CanSuspendBackgroundedRenderer(int render_process_id) const;
 
   // Returns true if |first| is considered less desirable to be killed than
@@ -159,9 +161,10 @@
   static int64_t IdFromWebContents(content::WebContents* web_contents);
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(TabManagerTest,
-                           ActivateTabResetPurgeAndSuspendState);
-  FRIEND_TEST_ALL_PREFIXES(TabManagerTest, NextPurgeAndSuspendState);
+  FRIEND_TEST_ALL_PREFIXES(TabManagerTest, PurgeBackgroundRenderer);
+  FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ActivateTabResetPurgeState);
+  FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ShouldPurgeAtDefaultTime);
+  FRIEND_TEST_ALL_PREFIXES(TabManagerTest, DefaultTimeToPurgeInCorrectRange);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, AutoDiscardable);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, CanOnlyDiscardOnce);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ChildProcessNotifications);
@@ -176,18 +179,26 @@
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ProtectVideoTabs);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ReloadDiscardedTabContextMenu);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, TabManagerBasics);
-  FRIEND_TEST_ALL_PREFIXES(TabManagerWebContentsDataTest, PurgeAndSuspendState);
 
   // The time of the first purging after a renderer is backgrounded.
   // The initial value was chosen because most of users activate backgrounded
   // tabs within 30 minutes. (c.f. Tabs.StateTransfer.Time_Inactive_Active)
-  static constexpr base::TimeDelta kDefaultTimeToFirstPurge =
+  static constexpr base::TimeDelta kDefaultMinTimeToPurge =
       base::TimeDelta::FromMinutes(30);
 
+  // The min/max time to purge ratio. The max time to purge is set to be
+  // min time to purge times this value.
+  const int kMinMaxTimeToPurgeRatio = 2;
+
   // This is needed so WebContentsData can call OnDiscardedStateChange, and
-  // can use PurgeAndSuspendState.
+  // can use PurgeState.
   friend class WebContentsData;
 
+  // Finds TabStripModel which has a WebContents whose id is the given
+  // web_contents_id, and returns the WebContents index and the TabStripModel.
+  int FindTabStripModelById(int64_t target_web_contents_id,
+                            TabStripModel** model) const;
+
   // Called by WebContentsData whenever the discard state of a WebContents
   // changes, so that observers can be informed.
   void OnDiscardedStateChange(content::WebContents* contents,
@@ -258,38 +269,27 @@
   // that need to be run periodically (see comment in implementation).
   void UpdateTimerCallback();
 
-  // Initially PurgeAndSuspendState is RUNNING.
-  // RUNNING => SUSPENDED
-  // - A tab has been backgrounded for more than purge-and-suspend-time
-  //   seconds.
-  // SUSPENDED => RESUMED
-  // - A suspended tab is still suspended (i.e. last active time < last
-  //   purge-and-suspend modified time), and
-  // - kMaxTimeRendererAllowedToBeSuspendedBeforeResume time passes since
-  //   since the tab was suspended.
-  // RESUMED => SUSPENDED
-  // - A resumed tab is still backgrounded (i.e. last active time < last
-  //   purge-and-suspend modified time), and
-  // - kSuspendedRendererLengthOfResumption time passes since the tab was
-  //   resumed.
-  // SUSPENDED, RESUMED, RUNNING => RUNNING
-  // - When ActiveTabChaged, the newly activated tab's state will be RUNNING.
-  enum PurgeAndSuspendState {
-    RUNNING,
-    RESUMED,
-    SUSPENDED,
-  };
   // Returns WebContents whose contents id matches the given tab_contents_id.
   content::WebContents* GetWebContentsById(int64_t tab_contents_id) const;
 
-  // Returns the next state of the purge and suspend.
-  PurgeAndSuspendState GetNextPurgeAndSuspendState(
-      content::WebContents* content,
-      base::TimeTicks current_time,
-      const base::TimeDelta& time_to_first_suspension) const;
+  // Returns a random time-to-purge whose min value is min_time_to_purge and max
+  // value is min_time_to_purge * kMinMaxTimeToPurgeRatio.
+  base::TimeDelta GetTimeToPurge(base::TimeDelta min_time_to_purge) const;
 
-  // Purges and suspends renderers in backgrounded tabs.
-  void PurgeAndSuspendBackgroundedTabs();
+  // Returns true if the tab specified by |content| is now eligible to have
+  // its memory purged.
+  bool ShouldPurgeNow(content::WebContents* content) const;
+
+  // Purges renderers in backgrounded tabs if the following conditions are
+  // satisfied:
+  // - the renderers are not purged yet,
+  // - the renderers are not playing media,
+  //   (CanPurgeBackgroundedRenderer returns true)
+  // - the renderers are left inactive and background for time-to-purge.
+  // If renderers are purged, their internal states become 'purged'.
+  // The state is reset to be 'not purged' only when they are activated
+  // (=ActiveTabChanged is invoked).
+  void PurgeBackgroundedTabsIfNeeded();
 
   // Does the actual discard by destroying the WebContents in |model| at |index|
   // and replacing it by an empty one. Returns the new WebContents or NULL if
@@ -372,8 +372,8 @@
   // backgrounded.
   base::TimeDelta minimum_protection_time_;
 
-  // A backgrounded renderer will be suspended when this time passes.
-  base::TimeDelta time_to_first_suspension_;
+  // A backgrounded renderer will be purged when this time passes.
+  base::TimeDelta min_time_to_purge_;
 
 #if defined(OS_CHROMEOS)
   std::unique_ptr<TabManagerDelegate> delegate_;
diff --git a/chrome/browser/memory/tab_manager_browsertest.cc b/chrome/browser/memory/tab_manager_browsertest.cc
index 6f62268..a5bad6b 100644
--- a/chrome/browser/memory/tab_manager_browsertest.cc
+++ b/chrome/browser/memory/tab_manager_browsertest.cc
@@ -481,6 +481,107 @@
   EXPECT_TRUE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(0)));
 }
 
+IN_PROC_BROWSER_TEST_F(TabManagerTest, PurgeBackgroundRenderer) {
+  TabManager* tab_manager = g_browser_process->GetTabManager();
+
+  base::SimpleTestTickClock test_clock_;
+  tab_manager->set_test_tick_clock(&test_clock_);
+
+  // Get three tabs open.
+  content::WindowedNotificationObserver load1(
+      content::NOTIFICATION_NAV_ENTRY_COMMITTED,
+      content::NotificationService::AllSources());
+  OpenURLParams open1(GURL(chrome::kChromeUIAboutURL), content::Referrer(),
+                      WindowOpenDisposition::CURRENT_TAB,
+                      ui::PAGE_TRANSITION_TYPED, false);
+  browser()->OpenURL(open1);
+  load1.Wait();
+
+  content::WindowedNotificationObserver load2(
+      content::NOTIFICATION_NAV_ENTRY_COMMITTED,
+      content::NotificationService::AllSources());
+  OpenURLParams open2(GURL(chrome::kChromeUICreditsURL), content::Referrer(),
+                      WindowOpenDisposition::NEW_FOREGROUND_TAB,
+                      ui::PAGE_TRANSITION_TYPED, false);
+  browser()->OpenURL(open2);
+  load2.Wait();
+
+  content::WindowedNotificationObserver load3(
+      content::NOTIFICATION_NAV_ENTRY_COMMITTED,
+      content::NotificationService::AllSources());
+  OpenURLParams open3(GURL(chrome::kChromeUITermsURL), content::Referrer(),
+                      WindowOpenDisposition::NEW_FOREGROUND_TAB,
+                      ui::PAGE_TRANSITION_TYPED, false);
+  browser()->OpenURL(open3);
+  load3.Wait();
+
+  auto* tsm = browser()->tab_strip_model();
+  TabManager::WebContentsData* tab1_contents_data =
+      tab_manager->GetWebContentsData(tsm->GetWebContentsAt(0));
+  TabManager::WebContentsData* tab2_contents_data =
+      tab_manager->GetWebContentsData(tsm->GetWebContentsAt(1));
+  TabManager::WebContentsData* tab3_contents_data =
+      tab_manager->GetWebContentsData(tsm->GetWebContentsAt(2));
+
+  // The time-to-purge initialized at ActiveTabChanged should be in the
+  // right default range.
+  EXPECT_GE(tab1_contents_data->time_to_purge(),
+            base::TimeDelta::FromMinutes(30));
+  EXPECT_LT(tab1_contents_data->time_to_purge(),
+            base::TimeDelta::FromMinutes(60));
+  EXPECT_GE(tab2_contents_data->time_to_purge(),
+            base::TimeDelta::FromMinutes(30));
+  EXPECT_LT(tab2_contents_data->time_to_purge(),
+            base::TimeDelta::FromMinutes(60));
+  EXPECT_GE(tab3_contents_data->time_to_purge(),
+            base::TimeDelta::FromMinutes(30));
+  EXPECT_LT(tab3_contents_data->time_to_purge(),
+            base::TimeDelta::FromMinutes(60));
+
+  // To make it easy to test, configure time-to-purge here.
+  base::TimeDelta time_to_purge1 = base::TimeDelta::FromMinutes(30);
+  base::TimeDelta time_to_purge2 = base::TimeDelta::FromMinutes(40);
+  tab1_contents_data->set_time_to_purge(time_to_purge1);
+  tab2_contents_data->set_time_to_purge(time_to_purge2);
+  tab3_contents_data->set_time_to_purge(time_to_purge1);
+
+  // No tabs are not purged yet.
+  ASSERT_FALSE(tab1_contents_data->is_purged());
+  ASSERT_FALSE(tab2_contents_data->is_purged());
+  ASSERT_FALSE(tab3_contents_data->is_purged());
+
+  // Advance the clock for time_to_purge1.
+  test_clock_.Advance(time_to_purge1);
+  tab_manager->PurgeBackgroundedTabsIfNeeded();
+
+  ASSERT_FALSE(tab1_contents_data->is_purged());
+  ASSERT_FALSE(tab2_contents_data->is_purged());
+  ASSERT_FALSE(tab3_contents_data->is_purged());
+
+  // Advance the clock for 1 minutes.
+  test_clock_.Advance(base::TimeDelta::FromMinutes(1));
+  tab_manager->PurgeBackgroundedTabsIfNeeded();
+
+  // Since tab1 is kept inactive and background for more than
+  // time_to_purge1, tab1 should be purged.
+  ASSERT_TRUE(tab1_contents_data->is_purged());
+  ASSERT_FALSE(tab2_contents_data->is_purged());
+  ASSERT_FALSE(tab3_contents_data->is_purged());
+
+  // Advance the clock.
+  test_clock_.Advance(time_to_purge2 - time_to_purge1);
+  tab_manager->PurgeBackgroundedTabsIfNeeded();
+
+  // Since tab2 is kept inactive and background for more than
+  // time_to_purge2, tab1 should be purged.
+  // Since tab3 is active, tab3 should not be purged.
+  ASSERT_TRUE(tab1_contents_data->is_purged());
+  ASSERT_TRUE(tab2_contents_data->is_purged());
+  ASSERT_FALSE(tab3_contents_data->is_purged());
+
+  tsm->CloseAllTabs();
+}
+
 }  // namespace memory
 
 #endif  // OS_WIN || OS_MAXOSX || OS_LINUX
diff --git a/chrome/browser/memory/tab_manager_unittest.cc b/chrome/browser/memory/tab_manager_unittest.cc
index c20c394..5fd7b77 100644
--- a/chrome/browser/memory/tab_manager_unittest.cc
+++ b/chrome/browser/memory/tab_manager_unittest.cc
@@ -27,7 +27,9 @@
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/variations/variations_associated_data.h"
+#include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/web_contents_tester.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -672,7 +674,15 @@
   ASSERT_TRUE(tabstrip.empty());
 }
 
-TEST_F(TabManagerTest, NextPurgeAndSuspendState) {
+TEST_F(TabManagerTest, DefaultTimeToPurgeInCorrectRange) {
+  TabManager tab_manager;
+  base::TimeDelta time_to_purge =
+      tab_manager.GetTimeToPurge(TabManager::kDefaultMinTimeToPurge);
+  EXPECT_GE(time_to_purge, base::TimeDelta::FromMinutes(30));
+  EXPECT_LT(time_to_purge, base::TimeDelta::FromMinutes(60));
+}
+
+TEST_F(TabManagerTest, ShouldPurgeAtDefaultTime) {
   TabManager tab_manager;
   TabStripDummyDelegate delegate;
   TabStripModel tabstrip(&delegate, profile());
@@ -681,102 +691,67 @@
   WebContents* test_contents = CreateWebContents();
   tabstrip.AppendWebContents(test_contents, false);
 
-  // Use default time-to-first-purge  defined in TabManager.
-  base::TimeDelta threshold = TabManager::kDefaultTimeToFirstPurge;
   base::SimpleTestTickClock test_clock;
+  tab_manager.set_test_tick_clock(&test_clock);
 
+  tab_manager.GetWebContentsData(test_contents)->set_is_purged(false);
   tab_manager.GetWebContentsData(test_contents)
-      ->SetPurgeAndSuspendState(TabManager::RUNNING);
+      ->SetLastInactiveTime(test_clock.NowTicks());
   tab_manager.GetWebContentsData(test_contents)
-      ->SetLastPurgeAndSuspendModifiedTimeForTesting(test_clock.NowTicks());
+      ->set_time_to_purge(base::TimeDelta::FromMinutes(1));
 
-  // Wait 30 minutes and verify that the tab is still RUNNING.
-  test_clock.Advance(base::TimeDelta::FromMinutes(30));
-  EXPECT_EQ(TabManager::RUNNING,
-            tab_manager.GetNextPurgeAndSuspendState(
-                test_contents, test_clock.NowTicks(), threshold));
+  // Wait 1 minute and verify that the tab is still not to be purged.
+  test_clock.Advance(base::TimeDelta::FromMinutes(1));
+  EXPECT_FALSE(tab_manager.ShouldPurgeNow(test_contents));
 
-  // Wait another second and verify that it is now SUSPENDED.
+  // Wait another 1 second and verify that it should be purged now .
   test_clock.Advance(base::TimeDelta::FromSeconds(1));
-  EXPECT_EQ(TabManager::SUSPENDED,
-            tab_manager.GetNextPurgeAndSuspendState(
-                test_contents, test_clock.NowTicks(), threshold));
+  EXPECT_TRUE(tab_manager.ShouldPurgeNow(test_contents));
 
+  tab_manager.GetWebContentsData(test_contents)->set_is_purged(true);
   tab_manager.GetWebContentsData(test_contents)
-      ->SetPurgeAndSuspendState(TabManager::SUSPENDED);
-  tab_manager.GetWebContentsData(test_contents)
-      ->SetLastPurgeAndSuspendModifiedTimeForTesting(test_clock.NowTicks());
+      ->SetLastInactiveTime(test_clock.NowTicks());
 
-  test_clock.Advance(base::TimeDelta::FromSeconds(1200));
-  EXPECT_EQ(TabManager::SUSPENDED,
-            tab_manager.GetNextPurgeAndSuspendState(
-                test_contents, test_clock.NowTicks(), threshold));
-
-  test_clock.Advance(base::TimeDelta::FromSeconds(1));
-  EXPECT_EQ(TabManager::RESUMED,
-            tab_manager.GetNextPurgeAndSuspendState(
-                test_contents, test_clock.NowTicks(), threshold));
-
-  tab_manager.GetWebContentsData(test_contents)
-      ->SetPurgeAndSuspendState(TabManager::RESUMED);
-  tab_manager.GetWebContentsData(test_contents)
-      ->SetLastPurgeAndSuspendModifiedTimeForTesting(test_clock.NowTicks());
-
-  test_clock.Advance(base::TimeDelta::FromSeconds(10));
-  EXPECT_EQ(TabManager::RESUMED,
-            tab_manager.GetNextPurgeAndSuspendState(
-                test_contents, test_clock.NowTicks(), threshold));
-
-  test_clock.Advance(base::TimeDelta::FromSeconds(1));
-  EXPECT_EQ(TabManager::SUSPENDED,
-            tab_manager.GetNextPurgeAndSuspendState(
-                test_contents, test_clock.NowTicks(), threshold));
-
-  // Clean up the tabstrip.
-  tabstrip.CloseAllTabs();
-  EXPECT_TRUE(tabstrip.empty());
+  // Wait 1 day and verify that the tab is still be purged.
+  test_clock.Advance(base::TimeDelta::FromHours(24));
+  EXPECT_FALSE(tab_manager.ShouldPurgeNow(test_contents));
 }
 
-TEST_F(TabManagerTest, ActivateTabResetPurgeAndSuspendState) {
+TEST_F(TabManagerTest, ActivateTabResetPurgeState) {
   TabManager tab_manager;
   TabStripDummyDelegate delegate;
   TabStripModel tabstrip(&delegate, profile());
   tabstrip.AddObserver(&tab_manager);
+  tab_manager.test_tab_strip_models_.push_back(
+      TabManager::TestTabStripModel(&tabstrip, false /* !is_app */));
+
+  base::SimpleTestTickClock test_clock;
+  tab_manager.set_test_tick_clock(&test_clock);
 
   WebContents* tab1 = CreateWebContents();
   WebContents* tab2 = CreateWebContents();
   tabstrip.AppendWebContents(tab1, true);
   tabstrip.AppendWebContents(tab2, false);
 
-  base::SimpleTestTickClock test_clock;
+  tab_manager.GetWebContentsData(tab2)->SetLastInactiveTime(
+      test_clock.NowTicks());
+  static_cast<content::MockRenderProcessHost*>(tab2->GetRenderProcessHost())
+      ->set_is_process_backgrounded(true);
+  EXPECT_TRUE(tab2->GetRenderProcessHost()->IsProcessBackgrounded());
 
-  // Initially PurgeAndSuspend state should be RUNNING.
-  EXPECT_EQ(TabManager::RUNNING,
-            tab_manager.GetWebContentsData(tab2)->GetPurgeAndSuspendState());
+  // Initially PurgeAndSuspend state should be NOT_PURGED.
+  EXPECT_FALSE(tab_manager.GetWebContentsData(tab2)->is_purged());
+  tab_manager.GetWebContentsData(tab2)->set_time_to_purge(
+      base::TimeDelta::FromMinutes(1));
+  test_clock.Advance(base::TimeDelta::FromMinutes(2));
+  tab_manager.PurgeBackgroundedTabsIfNeeded();
+  // Since tab2 is kept inactive and background for more than time-to-purge,
+  // tab2 should be purged.
+  EXPECT_TRUE(tab_manager.GetWebContentsData(tab2)->is_purged());
 
-  tab_manager.GetWebContentsData(tab2)->SetPurgeAndSuspendState(
-      TabManager::SUSPENDED);
-  tab_manager.GetWebContentsData(tab2)
-      ->SetLastPurgeAndSuspendModifiedTimeForTesting(test_clock.NowTicks());
-
-  // Activate tab2. Tab2's PurgeAndSuspend state should be RUNNING.
+  // Activate tab2. Tab2's PurgeAndSuspend state should be NOT_PURGED.
   tabstrip.ActivateTabAt(1, true /* user_gesture */);
-  EXPECT_EQ(TabManager::RUNNING,
-            tab_manager.GetWebContentsData(tab2)->GetPurgeAndSuspendState());
-
-  tab_manager.GetWebContentsData(tab1)->SetPurgeAndSuspendState(
-      TabManager::RESUMED);
-  tab_manager.GetWebContentsData(tab1)
-      ->SetLastPurgeAndSuspendModifiedTimeForTesting(test_clock.NowTicks());
-
-  // Activate tab1. Tab1's PurgeAndSuspend state should be RUNNING.
-  tabstrip.ActivateTabAt(0, true /* user_gesture */);
-  EXPECT_EQ(TabManager::RUNNING,
-            tab_manager.GetWebContentsData(tab1)->GetPurgeAndSuspendState());
-
-  // Clean up the tabstrip.
-  tabstrip.CloseAllTabs();
-  EXPECT_TRUE(tabstrip.empty());
+  EXPECT_FALSE(tab_manager.GetWebContentsData(tab2)->is_purged());
 }
 
 }  // namespace memory
diff --git a/chrome/browser/memory/tab_manager_web_contents_data.cc b/chrome/browser/memory/tab_manager_web_contents_data.cc
index 92ebc88..b585167 100644
--- a/chrome/browser/memory/tab_manager_web_contents_data.cc
+++ b/chrome/browser/memory/tab_manager_web_contents_data.cc
@@ -22,9 +22,8 @@
 TabManager::WebContentsData::WebContentsData(content::WebContents* web_contents)
     : WebContentsObserver(web_contents),
       test_tick_clock_(nullptr),
-      last_purge_and_suspend_modified_time_(
-          base::TimeTicks::FromInternalValue(0)),
-      purge_and_suspend_state_(RUNNING) {}
+      time_to_purge_(base::TimeDelta::FromMinutes(30)),
+      is_purged_(false) {}
 
 TabManager::WebContentsData::~WebContentsData() {}
 
@@ -202,25 +201,4 @@
   return tab_data_.is_auto_discardable;
 }
 
-void TabManager::WebContentsData::SetPurgeAndSuspendState(
-    PurgeAndSuspendState state) {
-  last_purge_and_suspend_modified_time_ = NowTicks();
-  purge_and_suspend_state_ = state;
-}
-
-base::TimeTicks TabManager::WebContentsData::LastPurgeAndSuspendModifiedTime()
-    const {
-  return last_purge_and_suspend_modified_time_;
-}
-
-void TabManager::WebContentsData::SetLastPurgeAndSuspendModifiedTimeForTesting(
-    base::TimeTicks timestamp) {
-  last_purge_and_suspend_modified_time_ = timestamp;
-}
-
-TabManager::PurgeAndSuspendState
-TabManager::WebContentsData::GetPurgeAndSuspendState() const {
-  return purge_and_suspend_state_;
-}
-
 }  // namespace memory
diff --git a/chrome/browser/memory/tab_manager_web_contents_data.h b/chrome/browser/memory/tab_manager_web_contents_data.h
index 8f68c12..3e50914 100644
--- a/chrome/browser/memory/tab_manager_web_contents_data.h
+++ b/chrome/browser/memory/tab_manager_web_contents_data.h
@@ -79,25 +79,23 @@
   // Sets/clears the auto-discardable state of the tab.
   void SetAutoDiscardableState(bool state);
 
-  // Sets the current purge-and-suspend state, and update the timestamp,
-  // i.e. the last purge-and-suspend modified time.
-  // TODO(tasak): remove this after PurgeAndSuspend code is moved into
+  // Sets the current purge state.
+  // TODO(tasak): remove this after the logic is moved into
   // MemoryCoordinator.
-  void SetPurgeAndSuspendState(TabManager::PurgeAndSuspendState state);
+  void set_is_purged(bool state) { is_purged_ = state; }
 
-  // Returns the last purge-and-suspend modified time.
-  // TODO(tasak): remove this after PurgeAndSuspend code is moved into
+  // Returns the current state of purge.
+  // TODO(tasak): remove this after the logic is moved into
   // MemoryCoordinator.
-  base::TimeTicks LastPurgeAndSuspendModifiedTime() const;
+  bool is_purged() const { return is_purged_; }
 
-  // Sets the timestamp of the last modified time the purge-and-suspend state
-  // of the tab was changed for testing.
-  void SetLastPurgeAndSuspendModifiedTimeForTesting(base::TimeTicks timestamp);
+  // Sets the time to purge after the tab is backgrounded.
+  void set_time_to_purge(const base::TimeDelta& time_to_purge) {
+    time_to_purge_ = time_to_purge;
+  }
 
-  // Returns the current state of purge-and-suspend.
-  // TODO(tasak): remove this after PurgeAndSuspend code is moved into
-  // MemoryCoordinator.
-  TabManager::PurgeAndSuspendState GetPurgeAndSuspendState() const;
+  // Returns the time to first purge after the tab is backgrounded.
+  base::TimeDelta time_to_purge() const { return time_to_purge_; }
 
  private:
   // Needed to access tab_data_.
@@ -141,11 +139,11 @@
   // this test clock. Otherwise it returns the system clock's value.
   base::TickClock* test_tick_clock_;
 
-  // The last time purge-and-suspend state was modified.
-  base::TimeTicks last_purge_and_suspend_modified_time_;
+  // The time to purge after the tab is backgrounded.
+  base::TimeDelta time_to_purge_;
 
-  // The current state of purge-and-suspend.
-  PurgeAndSuspendState purge_and_suspend_state_;
+  // True if the tab has been purged.
+  bool is_purged_;
 
   DISALLOW_COPY_AND_ASSIGN(WebContentsData);
 };
diff --git a/chrome/browser/memory/tab_manager_web_contents_data_unittest.cc b/chrome/browser/memory/tab_manager_web_contents_data_unittest.cc
index d98afa8..e8adeefb 100644
--- a/chrome/browser/memory/tab_manager_web_contents_data_unittest.cc
+++ b/chrome/browser/memory/tab_manager_web_contents_data_unittest.cc
@@ -204,25 +204,4 @@
   histograms.ExpectBucketCount(kHistogramName, 12000, 1);
 }
 
-TEST_F(TabManagerWebContentsDataTest, PurgeAndSuspendState) {
-  EXPECT_EQ(TabManager::RUNNING, tab_data()->GetPurgeAndSuspendState());
-  base::TimeTicks last_modified = tab_data()->LastPurgeAndSuspendModifiedTime();
-  test_clock().Advance(base::TimeDelta::FromSeconds(5));
-  tab_data()->SetPurgeAndSuspendState(TabManager::SUSPENDED);
-  EXPECT_EQ(TabManager::SUSPENDED, tab_data()->GetPurgeAndSuspendState());
-  EXPECT_GT(tab_data()->LastPurgeAndSuspendModifiedTime(), last_modified);
-
-  last_modified = tab_data()->LastPurgeAndSuspendModifiedTime();
-  test_clock().Advance(base::TimeDelta::FromSeconds(5));
-  tab_data()->SetPurgeAndSuspendState(TabManager::RESUMED);
-  EXPECT_EQ(TabManager::RESUMED, tab_data()->GetPurgeAndSuspendState());
-  EXPECT_GT(tab_data()->LastPurgeAndSuspendModifiedTime(), last_modified);
-
-  last_modified = tab_data()->LastPurgeAndSuspendModifiedTime();
-  test_clock().Advance(base::TimeDelta::FromSeconds(5));
-  tab_data()->SetPurgeAndSuspendState(TabManager::RUNNING);
-  EXPECT_EQ(TabManager::RUNNING, tab_data()->GetPurgeAndSuspendState());
-  EXPECT_GT(tab_data()->LastPurgeAndSuspendModifiedTime(), last_modified);
-}
-
 }  // namespace memory
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 7c1d1be..9719d1d3 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1775,13 +1775,13 @@
       "../browser/ui/location_bar/location_bar_browsertest.cc",
       "../browser/ui/login/login_handler_browsertest.cc",
       "../browser/ui/native_window_tracker_browsertest.cc",
+      "../browser/ui/passwords/manage_passwords_test.cc",
       "../browser/ui/permission_bubble/mock_permission_prompt.cc",
       "../browser/ui/permission_bubble/mock_permission_prompt.h",
       "../browser/ui/permission_bubble/mock_permission_prompt_factory.cc",
       "../browser/ui/permission_bubble/mock_permission_prompt_factory.h",
       "../browser/ui/permission_bubble/permission_bubble_browser_test_util.cc",
       "../browser/ui/permission_bubble/permission_bubble_browser_test_util.h",
-      "../browser/ui/passwords/manage_passwords_test.cc",
       "../browser/ui/prefs/prefs_tab_helper_browsertest.cc",
       "../browser/ui/profile_error_browsertest.cc",
       "../browser/ui/search/new_tab_page_interceptor_browsertest.cc",
@@ -3373,13 +3373,13 @@
     "../browser/ui/find_bar/find_backend_unittest.cc",
     "../browser/ui/login/login_handler_unittest.cc",
     "../browser/ui/page_info/website_settings_unittest.cc",
+    "../browser/ui/passwords/manage_passwords_state_unittest.cc",
+    "../browser/ui/passwords/manage_passwords_view_utils_unittest.cc",
+    "../browser/ui/passwords/password_manager_presenter_unittest.cc",
     "../browser/ui/permission_bubble/mock_permission_prompt.cc",
     "../browser/ui/permission_bubble/mock_permission_prompt.h",
     "../browser/ui/permission_bubble/mock_permission_prompt_factory.cc",
     "../browser/ui/permission_bubble/mock_permission_prompt_factory.h",
-    "../browser/ui/passwords/manage_passwords_state_unittest.cc",
-    "../browser/ui/passwords/manage_passwords_view_utils_unittest.cc",
-    "../browser/ui/passwords/password_manager_presenter_unittest.cc",
     "../browser/ui/search_engines/keyword_editor_controller_unittest.cc",
     "../browser/ui/sync/profile_signin_confirmation_helper_unittest.cc",
     "../browser/ui/sync/sync_promo_ui_unittest.cc",
@@ -4127,6 +4127,7 @@
     deps += [
       "//chrome/common/extensions/api",
       "//extensions:extensions_resources",
+      "//extensions/common",
       "//extensions/strings",
       "//media/cast:test_support",
 
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc
index 6523e80..06c9bf6 100644
--- a/chrome/utility/chrome_content_utility_client.cc
+++ b/chrome/utility/chrome_content_utility_client.cc
@@ -273,6 +273,8 @@
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::ExtensionsHandler::ExposeInterfacesToBrowser(
       registry, utility_process_running_elevated_);
+  extensions::UtilityHandler::ExposeInterfacesToBrowser(
+      registry, utility_process_running_elevated_);
 #endif
   // If our process runs with elevated privileges, only add elevated Mojo
   // services to the interface registry.
diff --git a/chrome/utility/extensions/extensions_handler.cc b/chrome/utility/extensions/extensions_handler.cc
index 4cb3fa8..5c93970 100644
--- a/chrome/utility/extensions/extensions_handler.cc
+++ b/chrome/utility/extensions/extensions_handler.cc
@@ -176,8 +176,7 @@
   ExtensionsClient::Set(ChromeExtensionsClient::GetInstance());
 }
 
-ExtensionsHandler::~ExtensionsHandler() {
-}
+ExtensionsHandler::~ExtensionsHandler() = default;
 
 // static
 void ExtensionsHandler::PreSandboxStartup() {
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn
index d32de66..6141d10a 100644
--- a/components/arc/BUILD.gn
+++ b/components/arc/BUILD.gn
@@ -154,6 +154,7 @@
     "common/scale_factor.mojom",
     "common/screen_rect.mojom",
     "common/storage_manager.mojom",
+    "common/tracing.mojom",
     "common/tts.mojom",
     "common/video.mojom",
     "common/video_accelerator.mojom",
diff --git a/components/arc/arc_bridge_host_impl.cc b/components/arc/arc_bridge_host_impl.cc
index 437aa1a4..521f1179 100644
--- a/components/arc/arc_bridge_host_impl.cc
+++ b/components/arc/arc_bridge_host_impl.cc
@@ -198,6 +198,11 @@
                   std::move(storage_manager_ptr));
 }
 
+void ArcBridgeHostImpl::OnTracingInstanceReady(
+    mojom::TracingInstancePtr tracing_ptr) {
+  OnInstanceReady(arc_bridge_service_->tracing(), std::move(tracing_ptr));
+}
+
 void ArcBridgeHostImpl::OnTtsInstanceReady(mojom::TtsInstancePtr tts_ptr) {
   OnInstanceReady(arc_bridge_service_->tts(), std::move(tts_ptr));
 }
diff --git a/components/arc/arc_bridge_host_impl.h b/components/arc/arc_bridge_host_impl.h
index 9ee3664..e72bbb7c 100644
--- a/components/arc/arc_bridge_host_impl.h
+++ b/components/arc/arc_bridge_host_impl.h
@@ -71,6 +71,7 @@
   void OnProcessInstanceReady(mojom::ProcessInstancePtr process_ptr) override;
   void OnStorageManagerInstanceReady(
       mojom::StorageManagerInstancePtr storage_manager_ptr) override;
+  void OnTracingInstanceReady(mojom::TracingInstancePtr trace_ptr) override;
   void OnTtsInstanceReady(mojom::TtsInstancePtr tts_ptr) override;
   void OnVideoInstanceReady(mojom::VideoInstancePtr video_ptr) override;
   void OnWallpaperInstanceReady(
diff --git a/components/arc/arc_bridge_service.h b/components/arc/arc_bridge_service.h
index 60abe9d5..a7377f95 100644
--- a/components/arc/arc_bridge_service.h
+++ b/components/arc/arc_bridge_service.h
@@ -36,6 +36,7 @@
 class PrintInstance;
 class ProcessInstance;
 class StorageManagerInstance;
+class TracingInstance;
 class TtsInstance;
 class VideoInstance;
 class WallpaperInstance;
@@ -89,6 +90,7 @@
   InstanceHolder<mojom::StorageManagerInstance>* storage_manager() {
     return &storage_manager_;
   }
+  InstanceHolder<mojom::TracingInstance>* tracing() { return &tracing_; }
   InstanceHolder<mojom::TtsInstance>* tts() { return &tts_; }
   InstanceHolder<mojom::VideoInstance>* video() { return &video_; }
   InstanceHolder<mojom::WallpaperInstance>* wallpaper() { return &wallpaper_; }
@@ -116,6 +118,7 @@
   InstanceHolder<mojom::PrintInstance> print_;
   InstanceHolder<mojom::ProcessInstance> process_;
   InstanceHolder<mojom::StorageManagerInstance> storage_manager_;
+  InstanceHolder<mojom::TracingInstance> tracing_;
   InstanceHolder<mojom::TtsInstance> tts_;
   InstanceHolder<mojom::VideoInstance> video_;
   InstanceHolder<mojom::WallpaperInstance> wallpaper_;
diff --git a/components/arc/common/arc_bridge.mojom b/components/arc/common/arc_bridge.mojom
index 7b8a9c52..1ad0fe37 100644
--- a/components/arc/common/arc_bridge.mojom
+++ b/components/arc/common/arc_bridge.mojom
@@ -26,13 +26,14 @@
 import "print.mojom";
 import "process.mojom";
 import "storage_manager.mojom";
+import "tracing.mojom";
 import "tts.mojom";
 import "video.mojom";
 import "wallpaper.mojom";
 
-// Next MinVersion: 22
+// Next MinVersion: 23
 // Deprecated method IDs: 101, 105
-// Next method ID: 128
+// Next method ID: 129
 interface ArcBridgeHost {
   // Keep the entries alphabetical. In order to do so without breaking
   // compatibility with the ARC instance, explicitly assign each interface a
@@ -110,6 +111,9 @@
   // Notifies Chrome that the StorageManagerInstance interface is ready.
   [MinVersion=12] OnStorageManagerInstanceReady@118(StorageManagerInstance instance_ptr);
 
+  // Notifies Chrome that the TracingInstance interface is ready.
+  [MinVersion=22] OnTracingInstanceReady@128(TracingInstance instance_ptr);
+
   // Notifies Chrome that the TtsInstance interface is ready.
   [MinVersion=17] OnTtsInstanceReady@123(TtsInstance instance_ptr);
 
diff --git a/components/arc/common/tracing.mojom b/components/arc/common/tracing.mojom
new file mode 100644
index 0000000..e27d89b
--- /dev/null
+++ b/components/arc/common/tracing.mojom
@@ -0,0 +1,18 @@
+// 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.
+
+// Next MinVersion: 1
+
+module arc.mojom;
+
+interface TracingInstance {
+  // Queries available tracing categories in the container.
+  QueryAvailableCategories@0() => (array<string> categories);
+
+  // Starts tracing in the container with the given categories.
+  StartTracing@1(array<string> categories) => (bool success);
+
+  // Stops tracing in the container.
+  StopTracing@2() => (bool success);
+};
diff --git a/content/app/content_main_runner.cc b/content/app/content_main_runner.cc
index aab03f3..5e082ed 100644
--- a/content/app/content_main_runner.cc
+++ b/content/app/content_main_runner.cc
@@ -27,7 +27,6 @@
 #include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_base.h"
 #include "base/metrics/statistics_recorder.h"
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 3bd6eb0..673a49c2 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1379,6 +1379,8 @@
     "streams/stream_write_observer.h",
     "theme_helper_mac.h",
     "theme_helper_mac.mm",
+    "tracing/arc_tracing_agent.cc",
+    "tracing/arc_tracing_agent.h",
     "tracing/background_tracing_config_impl.cc",
     "tracing/background_tracing_config_impl.h",
     "tracing/background_tracing_manager_impl.cc",
diff --git a/content/browser/loader/reload_cache_control_browsertest.cc b/content/browser/loader/reload_cache_control_browsertest.cc
index 05e8481..411f7658 100644
--- a/content/browser/loader/reload_cache_control_browsertest.cc
+++ b/content/browser/loader/reload_cache_control_browsertest.cc
@@ -157,6 +157,12 @@
 
 // Test if bypassing reload issues requests with proper cache control flags.
 IN_PROC_BROWSER_TEST_F(ReloadCacheControlBrowserTest, BypassingReload) {
+  // TODO(crbug.com/671545): This test gets to be unstable if browser-side
+  // navigation is enabled. This is because we can not ensure which of frame and
+  // image requests hits the network first.
+  if (IsBrowserSideNavigationEnabled())
+    return;
+
   GURL url(embedded_test_server()->GetURL(kReloadTestPath));
 
   EXPECT_TRUE(NavigateToURL(shell(), url));
diff --git a/content/browser/media/android/browser_media_player_manager.cc b/content/browser/media/android/browser_media_player_manager.cc
index 1ea8bf1..c890820 100644
--- a/content/browser/media/android/browser_media_player_manager.cc
+++ b/content/browser/media/android/browser_media_player_manager.cc
@@ -88,18 +88,16 @@
     case MEDIA_PLAYER_TYPE_REMOTE_ONLY:
     case MEDIA_PLAYER_TYPE_URL: {
       const std::string user_agent = GetContentClient()->GetUserAgent();
-      std::unique_ptr<MediaPlayerAndroid> media_player_bridge(
-          new MediaPlayerBridge(
-              media_player_params.player_id, media_player_params.url,
-              media_player_params.first_party_for_cookies, user_agent,
-              hide_url_log, this,
-              base::Bind(&BrowserMediaPlayerManager::OnDecoderResourcesReleased,
-                         weak_ptr_factory_.GetWeakPtr()),
-              media_player_params.frame_url,
-              media_player_params.allow_credentials));
+      auto media_player_bridge = base::MakeUnique<MediaPlayerBridge>(
+          media_player_params.player_id, media_player_params.url,
+          media_player_params.first_party_for_cookies, user_agent, hide_url_log,
+          this,
+          base::Bind(&BrowserMediaPlayerManager::OnDecoderResourcesReleased,
+                     weak_ptr_factory_.GetWeakPtr()),
+          media_player_params.frame_url, media_player_params.allow_credentials);
 
       if (media_player_params.type == MEDIA_PLAYER_TYPE_REMOTE_ONLY)
-        return media_player_bridge;
+        return std::move(media_player_bridge);
 
       bool should_block = false;
       bool extract_metadata =
@@ -122,10 +120,9 @@
         OnMediaMetadataChanged(media_player_params.player_id, base::TimeDelta(),
                                0, 0, false);
       } else if (!should_block) {
-        static_cast<MediaPlayerBridge*>(media_player_bridge.get())
-            ->Initialize();
+        media_player_bridge->Initialize();
       }
-      return media_player_bridge;
+      return std::move(media_player_bridge);
     }
   }
 
diff --git a/content/browser/renderer_host/ime_adapter_android.cc b/content/browser/renderer_host/ime_adapter_android.cc
index 3721d364..bb8a938 100644
--- a/content/browser/renderer_host/ime_adapter_android.cc
+++ b/content/browser/renderer_host/ime_adapter_android.cc
@@ -46,7 +46,7 @@
     const base::android::JavaRef<jobject>& java_key_event,
     int type,
     int modifiers,
-    long time_ms,
+    jlong time_ms,
     int key_code,
     int scan_code,
     bool is_system_key,
@@ -123,14 +123,14 @@
     const JavaParamRef<jobject>& original_key_event,
     int type,
     int modifiers,
-    long time_ms,
+    jlong time_ms,
     int key_code,
     int scan_code,
     bool is_system_key,
     int unicode_char) {
   NativeWebKeyboardEvent event = NativeWebKeyboardEventFromKeyEvent(
-          env, original_key_event, type, modifiers,
-          time_ms / 1000.0, key_code, scan_code, is_system_key, unicode_char);
+      env, original_key_event, type, modifiers, time_ms, key_code, scan_code,
+      is_system_key, unicode_char);
   rwhva_->SendKeyEvent(event);
   return true;
 }
diff --git a/content/browser/renderer_host/ime_adapter_android.h b/content/browser/renderer_host/ime_adapter_android.h
index 71ffef2..72ab6d3 100644
--- a/content/browser/renderer_host/ime_adapter_android.h
+++ b/content/browser/renderer_host/ime_adapter_android.h
@@ -43,7 +43,7 @@
       const base::android::JavaParamRef<jobject>& original_key_event,
       int type,
       int modifiers,
-      long event_time,
+      jlong time_ms,
       int key_code,
       int scan_code,
       bool is_system_key,
diff --git a/content/browser/tracing/arc_tracing_agent.cc b/content/browser/tracing/arc_tracing_agent.cc
new file mode 100644
index 0000000..d71cd647
--- /dev/null
+++ b/content/browser/tracing/arc_tracing_agent.cc
@@ -0,0 +1,100 @@
+// 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 "content/browser/tracing/arc_tracing_agent.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/threading/thread_checker.h"
+#include "base/threading/thread_task_runner_handle.h"
+
+namespace content {
+
+namespace {
+
+constexpr char kArcTracingAgentName[] = "arc";
+constexpr char kArcTraceLabel[] = "ArcTraceEvents";
+
+void OnStopTracing(bool success) {
+  DLOG_IF(WARNING, !success) << "Failed to stop ARC tracing.";
+}
+
+class ArcTracingAgentImpl : public ArcTracingAgent {
+ public:
+  // base::trace_event::TracingAgent overrides:
+  std::string GetTracingAgentName() override { return kArcTracingAgentName; }
+
+  std::string GetTraceEventLabel() override { return kArcTraceLabel; }
+
+  void StartAgentTracing(const base::trace_event::TraceConfig& trace_config,
+                         const StartAgentTracingCallback& callback) override {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    // delegate_ may be nullptr if ARC is not enabled on the system. In such
+    // case, simply do nothing.
+    if (!delegate_) {
+      // Use PostTask as the convention of TracingAgent. The caller expects
+      // callback to be called after this function returns.
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::Bind(callback, GetTracingAgentName(), false));
+      return;
+    }
+
+    delegate_->StartTracing(trace_config,
+                            base::Bind(callback, GetTracingAgentName()));
+  }
+
+  void StopAgentTracing(const StopAgentTracingCallback& callback) override {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    if (delegate_)
+      delegate_->StopTracing(base::Bind(OnStopTracing));
+
+    // Trace data is collect via systrace (debugd) in dev-mode. Simply
+    // return empty data here.
+    std::string no_data;
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::Bind(callback, GetTracingAgentName(), GetTraceEventLabel(),
+                   base::RefCountedString::TakeString(&no_data)));
+  }
+
+  // ArcTracingAgent overrides:
+  void SetDelegate(Delegate* delegate) override {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    delegate_ = delegate;
+  }
+
+  static ArcTracingAgentImpl* GetInstance() {
+    return base::Singleton<ArcTracingAgentImpl>::get();
+  }
+
+ private:
+  // This allows constructor and destructor to be private and usable only
+  // by the Singleton class.
+  friend struct base::DefaultSingletonTraits<ArcTracingAgentImpl>;
+
+  ArcTracingAgentImpl() = default;
+  ~ArcTracingAgentImpl() override = default;
+
+  Delegate* delegate_ = nullptr;  // Owned by ArcServiceLauncher.
+  base::ThreadChecker thread_checker_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArcTracingAgentImpl);
+};
+
+}  // namespace
+
+// static
+ArcTracingAgent* ArcTracingAgent::GetInstance() {
+  return ArcTracingAgentImpl::GetInstance();
+}
+
+ArcTracingAgent::ArcTracingAgent() = default;
+ArcTracingAgent::~ArcTracingAgent() = default;
+
+ArcTracingAgent::Delegate::~Delegate() = default;
+
+}  // namespace content
diff --git a/content/browser/tracing/arc_tracing_agent.h b/content/browser/tracing/arc_tracing_agent.h
new file mode 100644
index 0000000..43cff41
--- /dev/null
+++ b/content/browser/tracing/arc_tracing_agent.h
@@ -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.
+
+#ifndef CONTENT_BROWSER_TRACING_ARC_TRACING_AGENT_H_
+#define CONTENT_BROWSER_TRACING_ARC_TRACING_AGENT_H_
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/tracing_agent.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+// A wrapper for controlling tracing in ARC container.
+// The overriden Stop/StartAgentTracing functions are implemented by the
+// Delegate object to interact with ARC bridge. All the functions are expected
+// to be called on the browser's UI thread.
+class CONTENT_EXPORT ArcTracingAgent : public base::trace_event::TracingAgent {
+ public:
+  class Delegate {
+   public:
+    // Callback for StartTracing, takes one boolean argument to indicate
+    // success or failure.
+    using StartTracingCallback = base::Callback<void(bool)>;
+    // Callback for StopTracing, takes one boolean argument to indicate
+    // success or failure.
+    using StopTracingCallback = base::Callback<void(bool)>;
+
+    virtual ~Delegate();
+
+    // Starts tracing in ARC container.
+    virtual void StartTracing(
+        const base::trace_event::TraceConfig& trace_config,
+        const StartTracingCallback& callback) = 0;
+
+    // Stops tracing in ARC container.
+    virtual void StopTracing(const StopTracingCallback& callback) = 0;
+  };
+
+  ArcTracingAgent();
+  ~ArcTracingAgent() override;
+
+  static ArcTracingAgent* GetInstance();
+
+  // Sets Delegate instance, which should implement the communication
+  // using ARC bridge. If set to nullptr, calling of Start/StopAgentTracing
+  // does nothing. Ownership of |delegate| remains with the caller.
+  virtual void SetDelegate(Delegate* delegate) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ArcTracingAgent);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_TRACING_ARC_TRACING_AGENT_H_
diff --git a/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc
index 5fc480346..7115cc5 100644
--- a/content/browser/tracing/tracing_controller_impl.cc
+++ b/content/browser/tracing/tracing_controller_impl.cc
@@ -3,6 +3,10 @@
 // found in the LICENSE file.
 #include "content/browser/tracing/tracing_controller_impl.h"
 
+#include <algorithm>
+#include <memory>
+#include <utility>
+
 #include "base/bind.h"
 #include "base/cpu.h"
 #include "base/files/file_util.h"
@@ -45,6 +49,7 @@
 #if defined(OS_CHROMEOS)
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/debug_daemon_client.h"
+#include "content/browser/tracing/arc_tracing_agent.h"
 #endif
 
 #if defined(OS_WIN)
@@ -63,6 +68,7 @@
 
 const char kChromeTracingAgentName[] = "chrome";
 const char kETWTracingAgentName[] = "etw";
+const char kArcTracingAgentName[] = "arc";
 const char kChromeTraceLabel[] = "traceEvents";
 
 const int kStartTracingTimeoutSeconds = 30;
@@ -289,6 +295,12 @@
                      base::Unretained(this)));
       ++pending_start_tracing_ack_count_;
     }
+
+    ArcTracingAgent::GetInstance()->StartAgentTracing(
+        trace_config,
+        base::Bind(&TracingControllerImpl::OnStartAgentTracingAcked,
+                   base::Unretained(this)));
+    ++pending_start_tracing_ack_count_;
 #elif defined(OS_WIN)
     EtwTracingAgent::GetInstance()->StartAgentTracing(
         trace_config,
@@ -563,6 +575,11 @@
         BrowserThread::GetBlockingPool());
     return;
   }
+
+  if (agent_name == kArcTracingAgentName) {
+    additional_tracing_agents_.push_back(ArcTracingAgent::GetInstance());
+    return;
+  }
 #elif defined(OS_WIN)
   auto* etw_agent = EtwTracingAgent::GetInstance();
   if (agent_name == etw_agent->GetTracingAgentName()) {
@@ -667,15 +684,17 @@
 
   if (trace_data_sink_.get() && events_str_ptr &&
       !events_str_ptr->data().empty()) {
-    std::string json_string;
     if (agent_name == kETWTracingAgentName) {
       // The Windows kernel events are kept into a JSON format stored as string
       // and must not be escaped.
-      json_string = events_str_ptr->data();
-    } else {
-      json_string = base::GetQuotedJSONString(events_str_ptr->data());
+      trace_data_sink_->AddAgentTrace(events_label, events_str_ptr->data());
+    } else if (agent_name != kArcTracingAgentName) {
+      // ARC trace data is obtained via systrace. Ignore the empty data.
+      // Quote other trace data as JSON strings and merge them into
+      // |trace_data_sink_|.
+      trace_data_sink_->AddAgentTrace(
+          events_label, base::GetQuotedJSONString(events_str_ptr->data()));
     }
-    trace_data_sink_->AddAgentTrace(events_label, json_string);
   }
   std::vector<std::string> category_groups;
   OnStopTracingAcked(NULL, category_groups);
@@ -830,7 +849,7 @@
 
 void TracingControllerImpl::IssueClockSyncMarker() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(pending_clock_sync_ack_count_ == 0);
+  DCHECK_EQ(0, pending_clock_sync_ack_count_);
 
   for (auto* it : additional_tracing_agents_) {
     if (it->SupportsExplicitClockSync()) {
@@ -870,7 +889,7 @@
     return;
 
   // Stop tracing only if all agents report back.
-  if(--pending_clock_sync_ack_count_ == 0) {
+  if (--pending_clock_sync_ack_count_ == 0) {
     clock_sync_timer_.Stop();
     StopTracingAfterClockSync();
   }
diff --git a/content/child/blob_storage/blob_transport_controller.cc b/content/child/blob_storage/blob_transport_controller.cc
index 4f6d145..11b5a3e 100644
--- a/content/child/blob_storage/blob_transport_controller.cc
+++ b/content/child/blob_storage/blob_transport_controller.cc
@@ -16,7 +16,6 @@
 #include "base/lazy_instance.h"
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
-#include "base/memory/scoped_vector.h"
 #include "base/memory/shared_memory.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/numerics/safe_conversions.h"
@@ -210,8 +209,8 @@
 
   // Since we can be writing to the same shared memory handle from multiple
   // requests, we keep them in a vector and lazily create them.
-  ScopedVector<SharedMemory> opened_memory;
-  opened_memory.resize(memory_handles->size());
+  std::vector<std::unique_ptr<SharedMemory>> opened_memory(
+      memory_handles->size());
 
   // We need to calculate how much memory we expect to be writing to the memory
   // segments so we can correctly map it the first time.
@@ -252,13 +251,11 @@
       }
       case IPCBlobItemRequestStrategy::SHARED_MEMORY: {
         responses.push_back(BlobItemBytesResponse(request.request_number));
-        SharedMemory* memory = opened_memory[request.handle_index];
-        if (!memory) {
+        if (!opened_memory[request.handle_index]) {
           SharedMemoryHandle& handle = (*memory_handles)[request.handle_index];
           size_t size = shared_memory_sizes[request.handle_index];
           DCHECK(SharedMemory::IsHandleValid(handle));
-          std::unique_ptr<SharedMemory> shared_memory(
-              new SharedMemory(handle, false));
+          auto shared_memory = base::MakeUnique<SharedMemory>(handle, false);
 
           if (!shared_memory->Map(size)) {
             // This would happen if the renderer process doesn't have enough
@@ -269,14 +266,15 @@
                          << ".";
             return;
           }
-          memory = shared_memory.get();
-          opened_memory[request.handle_index] = shared_memory.release();
+          opened_memory[request.handle_index] = std::move(shared_memory);
         }
-        CHECK(memory->memory()) << "Couldn't map memory for blob transfer.";
+        CHECK(opened_memory[request.handle_index]->memory())
+            << "Couldn't map memory for blob transfer.";
         ReadStatus status = consolidation->ReadMemory(
             request.renderer_item_index, request.renderer_item_offset,
             request.size,
-            static_cast<char*>(memory->memory()) + request.handle_offset);
+            static_cast<char*>(opened_memory[request.handle_index]->memory()) +
+                request.handle_offset);
         DCHECK(status == ReadStatus::OK)
             << "Error reading from consolidated blob: "
             << static_cast<int>(status);
diff --git a/content/child/web_data_consumer_handle_impl.cc b/content/child/web_data_consumer_handle_impl.cc
index b40f479..2defcb6 100644
--- a/content/child/web_data_consumer_handle_impl.cc
+++ b/content/child/web_data_consumer_handle_impl.cc
@@ -62,20 +62,13 @@
     // Even if there is unread data available, mojo::ReadDataRaw() returns
     // FAILED_PRECONDITION when |size| is 0 and the producer handle was closed.
     // But in this case, WebDataConsumerHandle::Reader::read() must return Ok.
-    // So we use mojo::Wait() with 0 deadline to check whether readable or not.
-    MojoResult wait_result = mojo::Wait(
-        context_->handle().get(), MOJO_HANDLE_SIGNAL_READABLE, 0, nullptr);
-    switch (wait_result) {
-      case MOJO_RESULT_OK:
-        return Ok;
-      case MOJO_RESULT_FAILED_PRECONDITION:
-        return Done;
-      case MOJO_RESULT_DEADLINE_EXCEEDED:
-        return ShouldWait;
-      default:
-        NOTREACHED();
-        return UnexpectedError;
-    }
+    // So we query the signals state directly.
+    mojo::HandleSignalsState state = context_->handle()->QuerySignalsState();
+    if (state.readable())
+      return Ok;
+    if (state.never_readable())
+      return Done;
+    return ShouldWait;
   }
 
   uint32_t size_to_pass = size;
diff --git a/content/common/input/input_param_traits_unittest.cc b/content/common/input/input_param_traits_unittest.cc
index c5c70a3b..fa274b9 100644
--- a/content/common/input/input_param_traits_unittest.cc
+++ b/content/common/input/input_param_traits_unittest.cc
@@ -5,7 +5,10 @@
 #include "content/common/input/input_param_traits.h"
 
 #include <stddef.h>
+
+#include <memory>
 #include <utility>
+#include <vector>
 
 #include "base/memory/ptr_util.h"
 #include "content/common/input/input_event.h"
@@ -27,7 +30,7 @@
 namespace content {
 namespace {
 
-typedef ScopedVector<InputEvent> InputEvents;
+typedef std::vector<std::unique_ptr<InputEvent>> InputEvents;
 
 class InputParamTraitsTest : public testing::Test {
  protected:
@@ -44,7 +47,7 @@
 
   static void Compare(const InputEvents* a, const InputEvents* b) {
     for (size_t i = 0; i < a->size(); ++i)
-      Compare((*a)[i], (*b)[i]);
+      Compare((*a)[i].get(), (*b)[i].get());
   }
 
   static void Compare(const SyntheticSmoothScrollGestureParams* a,
@@ -201,35 +204,35 @@
                                     blink::WebInputEvent::NoModifiers,
                                     blink::WebInputEvent::TimeStampForTesting);
   key_event.nativeKeyCode = 5;
-  events.push_back(new InputEvent(key_event, latency));
+  events.push_back(base::MakeUnique<InputEvent>(key_event, latency));
 
   blink::WebMouseWheelEvent wheel_event(
       blink::WebInputEvent::MouseWheel, blink::WebInputEvent::NoModifiers,
       blink::WebInputEvent::TimeStampForTesting);
   wheel_event.deltaX = 10;
   latency.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, 1, 1);
-  events.push_back(new InputEvent(wheel_event, latency));
+  events.push_back(base::MakeUnique<InputEvent>(wheel_event, latency));
 
   blink::WebMouseEvent mouse_event(blink::WebInputEvent::MouseDown,
                                    blink::WebInputEvent::NoModifiers,
                                    blink::WebInputEvent::TimeStampForTesting);
   mouse_event.x = 10;
   latency.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_UI_COMPONENT, 2, 2);
-  events.push_back(new InputEvent(mouse_event, latency));
+  events.push_back(base::MakeUnique<InputEvent>(mouse_event, latency));
 
   blink::WebGestureEvent gesture_event(
       blink::WebInputEvent::GestureScrollBegin,
       blink::WebInputEvent::NoModifiers,
       blink::WebInputEvent::TimeStampForTesting);
   gesture_event.x = -1;
-  events.push_back(new InputEvent(gesture_event, latency));
+  events.push_back(base::MakeUnique<InputEvent>(gesture_event, latency));
 
   blink::WebTouchEvent touch_event(blink::WebInputEvent::TouchStart,
                                    blink::WebInputEvent::NoModifiers,
                                    blink::WebInputEvent::TimeStampForTesting);
   touch_event.touchesLength = 1;
   touch_event.touches[0].radiusX = 1;
-  events.push_back(new InputEvent(touch_event, latency));
+  events.push_back(base::MakeUnique<InputEvent>(touch_event, latency));
 
   Verify(events);
 }
diff --git a/content/public/browser/web_ui.h b/content/public/browser/web_ui.h
index 1c677877..f871576 100644
--- a/content/public/browser/web_ui.h
+++ b/content/public/browser/web_ui.h
@@ -8,7 +8,6 @@
 #include <vector>
 
 #include "base/callback.h"
-#include "base/memory/scoped_vector.h"
 #include "base/strings/string16.h"
 #include "content/common/content_export.h"
 #include "ui/base/page_transition_types.h"
diff --git a/content/public/test/mock_render_process_host.cc b/content/public/test/mock_render_process_host.cc
index 7ea1a745..a5eb8785 100644
--- a/content/public/test/mock_render_process_host.cc
+++ b/content/public/test/mock_render_process_host.cc
@@ -10,6 +10,7 @@
 
 #include "base/lazy_instance.h"
 #include "base/location.h"
+#include "base/memory/ptr_util.h"
 #include "base/process/process_handle.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -377,28 +378,24 @@
 MockRenderProcessHostFactory::~MockRenderProcessHostFactory() {
   // Detach this object from MockRenderProcesses to prevent STLDeleteElements()
   // from calling MockRenderProcessHostFactory::Remove().
-  for (ScopedVector<MockRenderProcessHost>::iterator it = processes_.begin();
-       it != processes_.end(); ++it) {
-    (*it)->SetFactory(NULL);
-  }
+  for (const auto& process : processes_)
+    process->SetFactory(nullptr);
 }
 
 RenderProcessHost* MockRenderProcessHostFactory::CreateRenderProcessHost(
     BrowserContext* browser_context,
     SiteInstance* site_instance) const {
-  MockRenderProcessHost* host = new MockRenderProcessHost(browser_context);
-  if (host) {
-    processes_.push_back(host);
-    host->SetFactory(this);
-  }
-  return host;
+  processes_.push_back(
+      base::MakeUnique<MockRenderProcessHost>(browser_context));
+  processes_.back()->SetFactory(this);
+  return processes_.back().get();
 }
 
 void MockRenderProcessHostFactory::Remove(MockRenderProcessHost* host) const {
-  for (ScopedVector<MockRenderProcessHost>::iterator it = processes_.begin();
-       it != processes_.end(); ++it) {
-    if (*it == host) {
-      processes_.weak_erase(it);
+  for (auto it = processes_.begin(); it != processes_.end(); ++it) {
+    if (it->get() == host) {
+      it->release();
+      processes_.erase(it);
       break;
     }
   }
diff --git a/content/public/test/mock_render_process_host.h b/content/public/test/mock_render_process_host.h
index 10ef44d0..8126ff8 100644
--- a/content/public/test/mock_render_process_host.h
+++ b/content/public/test/mock_render_process_host.h
@@ -9,9 +9,9 @@
 #include <stdint.h>
 #include <memory>
 #include <utility>
+#include <vector>
 
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "base/metrics/persistent_memory_allocator.h"
 #include "base/observer_list.h"
 #include "content/public/browser/render_process_host.h"
@@ -192,7 +192,7 @@
   // A list of MockRenderProcessHosts created by this object. This list is used
   // for deleting all MockRenderProcessHosts that have not deleted by a test in
   // the destructor and prevent them from being leaked.
-  mutable ScopedVector<MockRenderProcessHost> processes_;
+  mutable std::vector<std::unique_ptr<MockRenderProcessHost>> processes_;
 
   DISALLOW_COPY_AND_ASSIGN(MockRenderProcessHostFactory);
 };
diff --git a/content/public/test/test_file_system_context.h b/content/public/test/test_file_system_context.h
index a9c1b5f3..903ffc2 100644
--- a/content/public/test/test_file_system_context.h
+++ b/content/public/test/test_file_system_context.h
@@ -6,7 +6,6 @@
 #define CONTENT_PUBLIC_TEST_TEST_FILE_SYSTEM_CONTEXT_H_
 
 #include "base/files/file_path.h"
-#include "base/memory/scoped_vector.h"
 #include "storage/browser/fileapi/file_system_context.h"
 
 namespace storage {
diff --git a/content/public/test/test_web_contents_factory.h b/content/public/test/test_web_contents_factory.h
index 55e01ede..c2a71af7 100644
--- a/content/public/test/test_web_contents_factory.h
+++ b/content/public/test/test_web_contents_factory.h
@@ -6,9 +6,9 @@
 #define CONTENT_PUBLIC_TEST_TEST_WEB_CONTENTS_FACTORY_H_
 
 #include <memory>
+#include <vector>
 
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 
 namespace content {
 class BrowserContext;
@@ -41,7 +41,7 @@
   std::unique_ptr<RenderViewHostTestEnabler> rvh_enabler_;
 
   // The vector of web contents that this class created.
-  ScopedVector<WebContents> web_contents_;
+  std::vector<std::unique_ptr<WebContents>> web_contents_;
 
   DISALLOW_COPY_AND_ASSIGN(TestWebContentsFactory);
 };
diff --git a/content/test/test_web_contents_factory.cc b/content/test/test_web_contents_factory.cc
index 0cbbbe8..d27e6a8 100644
--- a/content/test/test_web_contents_factory.cc
+++ b/content/test/test_web_contents_factory.cc
@@ -4,6 +4,7 @@
 
 #include "content/public/test/test_web_contents_factory.h"
 
+#include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/test_renderer_host.h"
@@ -29,10 +30,10 @@
 
 WebContents* TestWebContentsFactory::CreateWebContents(
     BrowserContext* context) {
-  web_contents_.push_back(
-      WebContentsTester::CreateTestWebContents(context, nullptr));
+  web_contents_.push_back(base::WrapUnique(
+      WebContentsTester::CreateTestWebContents(context, nullptr)));
   DCHECK(web_contents_.back());
-  return web_contents_.back();
+  return web_contents_.back().get();
 }
 
 }  // namespace content
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn
index 4e85c5b..ea676f6 100644
--- a/extensions/BUILD.gn
+++ b/extensions/BUILD.gn
@@ -117,6 +117,8 @@
     "test/extension_test_notification_observer.h",
     "test/result_catcher.cc",
     "test/result_catcher.h",
+    "test/test_content_browser_client.cc",
+    "test/test_content_browser_client.h",
     "test/test_content_utility_client.cc",
     "test/test_content_utility_client.h",
     "test/test_extensions_client.cc",
diff --git a/extensions/DEPS b/extensions/DEPS
index f79d2406..390904a 100644
--- a/extensions/DEPS
+++ b/extensions/DEPS
@@ -16,6 +16,7 @@
   "+extensions/grit/extensions_resources.h",
   "+extensions/test",
   "+mojo/public",
+  "+services/service_manager/public",
   "+testing",
   "+third_party/skia/include",
 
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index 350e845..b037388 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -295,7 +295,7 @@
       "//components/variations",
       "//crypto:platform",
       "//extensions:extensions_browser_resources",
-      "//extensions/common:mojo",
+      "//extensions/common",
       "//services/service_manager/public/cpp",
     ]
   }
diff --git a/extensions/browser/extensions_test.cc b/extensions/browser/extensions_test.cc
index 4d1ccf30..f9f88a10 100644
--- a/extensions/browser/extensions_test.cc
+++ b/extensions/browser/extensions_test.cc
@@ -9,6 +9,7 @@
 #include "content/public/common/content_client.h"
 #include "content/public/test/test_browser_context.h"
 #include "extensions/browser/test_extensions_browser_client.h"
+#include "extensions/test/test_content_browser_client.h"
 #include "extensions/test/test_content_utility_client.h"
 
 namespace extensions {
@@ -22,10 +23,12 @@
 //   }
 // };
 ExtensionsTest::ExtensionsTest()
-    : content_utility_client_(new TestContentUtilityClient),
+    : content_browser_client_(new TestContentBrowserClient),
+      content_utility_client_(new TestContentUtilityClient),
       browser_context_(new content::TestBrowserContext),
       extensions_browser_client_(
           new TestExtensionsBrowserClient(browser_context_.get())) {
+  content::SetBrowserClientForTesting(content_browser_client_.get());
   content::SetUtilityClientForTesting(content_utility_client_.get());
   ExtensionsBrowserClient::Set(extensions_browser_client_.get());
   extensions_browser_client_->set_extension_system_factory(
diff --git a/extensions/browser/extensions_test.h b/extensions/browser/extensions_test.h
index 1c2bf61..22a9c06 100644
--- a/extensions/browser/extensions_test.h
+++ b/extensions/browser/extensions_test.h
@@ -16,6 +16,7 @@
 
 namespace content {
 class BrowserContext;
+class ContentBrowserClient;
 class ContentUtilityClient;
 class RenderViewHostTestEnabler;
 }
@@ -52,6 +53,7 @@
 
  private:
   content::TestContentClientInitializer content_client_initializer_;
+  std::unique_ptr<content::ContentBrowserClient> content_browser_client_;
   std::unique_ptr<content::ContentUtilityClient> content_utility_client_;
   std::unique_ptr<content::BrowserContext> browser_context_;
   std::unique_ptr<TestExtensionsBrowserClient> extensions_browser_client_;
diff --git a/extensions/browser/sandboxed_unpacker.cc b/extensions/browser/sandboxed_unpacker.cc
index 043dcd6..5f36c51e4 100644
--- a/extensions/browser/sandboxed_unpacker.cc
+++ b/extensions/browser/sandboxed_unpacker.cc
@@ -14,23 +14,19 @@
 #include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/json/json_string_value_serializer.h"
-#include "base/message_loop/message_loop.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/numerics/safe_conversions.h"
 #include "base/path_service.h"
 #include "base/sequenced_task_runner.h"
-#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/sequenced_worker_pool.h"
 #include "build/build_config.h"
 #include "components/crx_file/crx_file.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/utility_process_host.h"
-#include "content/public/common/common_param_traits.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_l10n_util.h"
-#include "extensions/common/extension_utility_messages.h"
+#include "extensions/common/extension_unpacker.mojom.h"
+#include "extensions/common/extension_utility_types.h"
 #include "extensions/common/extensions_client.h"
 #include "extensions/common/file_util.h"
 #include "extensions/common/manifest_constants.h"
@@ -43,7 +39,6 @@
 
 using base::ASCIIToUTF16;
 using content::BrowserThread;
-using content::UtilityProcessHost;
 using crx_file::CrxFile;
 
 // The following macro makes histograms that record the length of paths
@@ -129,21 +124,25 @@
     LOG(ERROR) << temp_dir->value() << " is not writable";
     return false;
   }
+
   // NormalizeFilePath requires a non-empty file, so write some data.
   // If you change the exit points of this function please make sure all
   // exit points delete this temp file!
-  if (base::WriteFile(temp_file, ".", 1) != 1)
+  if (base::WriteFile(temp_file, ".", 1) != 1) {
+    base::DeleteFile(temp_file, false);
     return false;
+  }
 
   base::FilePath normalized_temp_file;
   bool normalized = base::NormalizeFilePath(temp_file, &normalized_temp_file);
   if (!normalized) {
-    // If |temp_file| contains a link, the sandbox will block al file system
-    // operations, and the install will fail.
+    // If |temp_file| contains a link, the sandbox will block all file
+    // system operations, and the install will fail.
     LOG(ERROR) << temp_dir->value() << " seem to be on remote drive.";
   } else {
     *temp_dir = normalized_temp_file.DirName();
   }
+
   // Clean up the temp file.
   base::DeleteFile(temp_file, false);
 
@@ -224,11 +223,9 @@
     SandboxedUnpackerClient* client)
     : client_(client),
       extensions_dir_(extensions_dir),
-      got_response_(false),
       location_(location),
       creation_flags_(creation_flags),
-      unpacker_io_task_runner_(unpacker_io_task_runner),
-      utility_wrapper_(new UtilityHostWrapper) {
+      unpacker_io_task_runner_(unpacker_io_task_runner) {
   // Tracking for crbug.com/692069. The location must be valid. If it's invalid,
   // the utility process kills itself for a bad IPC.
   CHECK_GT(location, Manifest::INVALID_LOCATION);
@@ -259,8 +256,8 @@
 }
 
 void SandboxedUnpacker::StartWithCrx(const CRXFileInfo& crx_info) {
-  // We assume that we are started on the thread that the client wants us to do
-  // file IO on.
+  // We assume that we are started on the thread that the client wants us
+  // to do file IO on.
   CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
 
   crx_unpack_start_time_ = base::TimeTicks::Now();
@@ -314,12 +311,13 @@
                   l10n_util::GetStringUTF16(IDS_EXTENSION_UNPACK_FAILED));
     return;
   }
+
   PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackLinkFreeCrxPathLength",
                         link_free_crx_path);
 
-  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
-                          base::Bind(&SandboxedUnpacker::StartUnzipOnIOThread,
-                                     this, link_free_crx_path));
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::Bind(&SandboxedUnpacker::Unzip, this, link_free_crx_path));
 }
 
 void SandboxedUnpacker::StartWithDirectory(const std::string& extension_id,
@@ -342,9 +340,9 @@
     return;
   }
 
-  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
-                          base::Bind(&SandboxedUnpacker::StartUnpackOnIOThread,
-                                     this, extension_root_));
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::Bind(&SandboxedUnpacker::Unpack, this, extension_root_));
 }
 
 SandboxedUnpacker::~SandboxedUnpacker() {
@@ -353,28 +351,28 @@
   temp_dir_.Take();
 }
 
-bool SandboxedUnpacker::OnMessageReceived(const IPC::Message& message) {
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(SandboxedUnpacker, message)
-    IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnzipToDir_Succeeded,
-                        OnUnzipToDirSucceeded)
-    IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnzipToDir_Failed,
-                        OnUnzipToDirFailed)
-    IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnpackExtension_Succeeded,
-                        OnUnpackExtensionSucceeded)
-    IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnpackExtension_Failed,
-                        OnUnpackExtensionFailed)
-    IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-  return handled;
-}
+void SandboxedUnpacker::StartUtilityProcessIfNeeded() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
-void SandboxedUnpacker::OnProcessCrashed(int exit_code) {
-  // Don't report crashes if they happen after we got a response.
-  if (got_response_)
+  if (utility_process_mojo_client_)
     return;
 
-  // Utility process crashed while trying to install.
+  utility_process_mojo_client_ = base::MakeUnique<
+      content::UtilityProcessMojoClient<mojom::ExtensionUnpacker>>(
+      l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_EXTENSION_UNPACKER_NAME));
+  utility_process_mojo_client_->set_error_callback(
+      base::Bind(&SandboxedUnpacker::UtilityProcessCrashed, this));
+
+  utility_process_mojo_client_->set_exposed_directory(temp_dir_.GetPath());
+
+  utility_process_mojo_client_->Start();
+}
+
+void SandboxedUnpacker::UtilityProcessCrashed() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  utility_process_mojo_client_.reset();
+
   ReportFailure(
       UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL,
       l10n_util::GetStringFUTF16(
@@ -384,60 +382,73 @@
           l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_PROCESS_CRASHED));
 }
 
-void SandboxedUnpacker::StartUnzipOnIOThread(const base::FilePath& crx_path) {
-  if (!utility_wrapper_->StartIfNeeded(temp_dir_.GetPath(), this,
-                                       unpacker_io_task_runner_)) {
-    ReportFailure(
-        COULD_NOT_START_UTILITY_PROCESS,
-        l10n_util::GetStringFUTF16(
-            IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
-            FailureReasonToString16(COULD_NOT_START_UTILITY_PROCESS)));
-    return;
-  }
+void SandboxedUnpacker::Unzip(const base::FilePath& crx_path) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  StartUtilityProcessIfNeeded();
+
   DCHECK(crx_path.DirName() == temp_dir_.GetPath());
   base::FilePath unzipped_dir =
       crx_path.DirName().AppendASCII(kTempExtensionName);
-  utility_wrapper_->host()->Send(
-      new ExtensionUtilityMsg_UnzipToDir(crx_path, unzipped_dir));
+
+  utility_process_mojo_client_->service()->Unzip(
+      crx_path, unzipped_dir,
+      base::Bind(&SandboxedUnpacker::UnzipDone, this, unzipped_dir));
 }
 
-void SandboxedUnpacker::StartUnpackOnIOThread(
-    const base::FilePath& directory_path) {
-  if (!utility_wrapper_->StartIfNeeded(temp_dir_.GetPath(), this,
-                                       unpacker_io_task_runner_)) {
-    ReportFailure(
-        COULD_NOT_START_UTILITY_PROCESS,
-        l10n_util::GetStringFUTF16(
-            IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
-            FailureReasonToString16(COULD_NOT_START_UTILITY_PROCESS)));
+void SandboxedUnpacker::UnzipDone(const base::FilePath& directory,
+                                  bool success) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  if (!success) {
+    utility_process_mojo_client_.reset();
+    ReportFailure(UNZIP_FAILED,
+                  l10n_util::GetStringUTF16(IDS_EXTENSION_PACKAGE_UNZIP_ERROR));
     return;
   }
-  DCHECK(directory_path.DirName() == temp_dir_.GetPath());
-  utility_wrapper_->host()->Send(new ExtensionUtilityMsg_UnpackExtension(
-      directory_path, extension_id_, location_, creation_flags_));
-}
 
-void SandboxedUnpacker::OnUnzipToDirSucceeded(const base::FilePath& directory) {
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
-      base::Bind(&SandboxedUnpacker::StartUnpackOnIOThread, this, directory));
+      base::Bind(&SandboxedUnpacker::Unpack, this, directory));
 }
 
-void SandboxedUnpacker::OnUnzipToDirFailed(const std::string& error) {
-  got_response_ = true;
-  utility_wrapper_ = nullptr;
-  ReportFailure(UNZIP_FAILED,
-                l10n_util::GetStringUTF16(IDS_EXTENSION_PACKAGE_UNZIP_ERROR));
+void SandboxedUnpacker::Unpack(const base::FilePath& directory) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  StartUtilityProcessIfNeeded();
+
+  DCHECK(directory.DirName() == temp_dir_.GetPath());
+
+  utility_process_mojo_client_->service()->Unpack(
+      directory, extension_id_, location_, creation_flags_,
+      base::Bind(&SandboxedUnpacker::UnpackDone, this));
 }
 
-void SandboxedUnpacker::OnUnpackExtensionSucceeded(
-    const base::DictionaryValue& manifest) {
+void SandboxedUnpacker::UnpackDone(
+    const base::string16& error,
+    std::unique_ptr<base::DictionaryValue> manifest) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  utility_process_mojo_client_.reset();
+
+  if (!error.empty()) {
+    unpacker_io_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&SandboxedUnpacker::UnpackExtensionFailed, this, error));
+    return;
+  }
+
+  unpacker_io_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&SandboxedUnpacker::UnpackExtensionSucceeded, this,
+                            base::Passed(&manifest)));
+}
+
+void SandboxedUnpacker::UnpackExtensionSucceeded(
+    std::unique_ptr<base::DictionaryValue> manifest) {
   CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
-  got_response_ = true;
-  utility_wrapper_ = nullptr;
 
   std::unique_ptr<base::DictionaryValue> final_manifest(
-      RewriteManifestFile(manifest));
+      RewriteManifestFile(*manifest));
   if (!final_manifest)
     return;
 
@@ -477,13 +488,12 @@
   if (!RewriteCatalogFiles())
     return;
 
-  ReportSuccess(manifest, install_icon);
+  ReportSuccess(std::move(manifest), install_icon);
 }
 
-void SandboxedUnpacker::OnUnpackExtensionFailed(const base::string16& error) {
+void SandboxedUnpacker::UnpackExtensionFailed(const base::string16& error) {
   CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
-  got_response_ = true;
-  utility_wrapper_ = nullptr;
+
   ReportFailure(
       UNPACKER_CLIENT_FAILED,
       l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_MESSAGE, error));
@@ -572,13 +582,12 @@
       return ASCIIToUTF16("UNZIP_FAILED");
     case DIRECTORY_MOVE_FAILED:
       return ASCIIToUTF16("DIRECTORY_MOVE_FAILED");
-    case COULD_NOT_START_UTILITY_PROCESS:
-      return ASCIIToUTF16("COULD_NOT_START_UTILITY_PROCESS");
 
     case NUM_FAILURE_REASONS:
       NOTREACHED();
       return base::string16();
   }
+
   NOTREACHED();
   return base::string16();
 }
@@ -643,12 +652,12 @@
       FailWithPackageError(CRX_HASH_VERIFICATION_FAILED);
       break;
   }
+
   return false;
 }
 
 void SandboxedUnpacker::ReportFailure(FailureReason reason,
                                       const base::string16& error) {
-  utility_wrapper_ = nullptr;
   UMA_HISTOGRAM_ENUMERATION("Extensions.SandboxUnpackFailureReason", reason,
                             NUM_FAILURE_REASONS);
   if (!crx_unpack_start_time_.is_null())
@@ -665,9 +674,8 @@
 }
 
 void SandboxedUnpacker::ReportSuccess(
-    const base::DictionaryValue& original_manifest,
+    std::unique_ptr<base::DictionaryValue> original_manifest,
     const SkBitmap& install_icon) {
-  utility_wrapper_ = nullptr;
   UMA_HISTOGRAM_COUNTS("Extensions.SandboxUnpackSuccess", 1);
 
   if (!crx_unpack_start_time_.is_null())
@@ -677,8 +685,11 @@
   DCHECK(!temp_dir_.GetPath().empty());
 
   // Client takes ownership of temporary directory and extension.
+  // TODO(https://crbug.com/699528): we should consider transferring the
+  // ownership of original_manifest to the client as well.
   client_->OnUnpackSuccess(temp_dir_.Take(), extension_root_,
-                           &original_manifest, extension_.get(), install_icon);
+                           original_manifest.get(), extension_.get(),
+                           install_icon);
   extension_ = NULL;
 }
 
@@ -719,6 +730,7 @@
 
 bool SandboxedUnpacker::RewriteImageFiles(SkBitmap* install_icon) {
   DCHECK(!temp_dir_.GetPath().empty());
+
   DecodedImages images;
   if (!ReadImagesFromFile(temp_dir_.GetPath(), &images)) {
     // Couldn't read image data from disk.
@@ -794,6 +806,7 @@
                         ASCIIToUTF16("INVALID_PATH_FOR_BITMAP_IMAGE")));
       return false;
     }
+
     base::FilePath path = extension_root_.Append(path_suffix);
 
     std::vector<unsigned char> image_data;
@@ -860,6 +873,7 @@
                                      ASCIIToUTF16("INVALID_PATH_FOR_CATALOG")));
       return false;
     }
+
     base::FilePath path = extension_root_.Append(relative_path);
 
     std::string catalog_json;
@@ -898,42 +912,4 @@
   }
 }
 
-SandboxedUnpacker::UtilityHostWrapper::UtilityHostWrapper() {}
-
-bool SandboxedUnpacker::UtilityHostWrapper::StartIfNeeded(
-    const base::FilePath& exposed_dir,
-    const scoped_refptr<UtilityProcessHostClient>& client,
-    const scoped_refptr<base::SequencedTaskRunner>& client_task_runner) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (!utility_host_) {
-    utility_host_ =
-        UtilityProcessHost::Create(client, client_task_runner)->AsWeakPtr();
-    utility_host_->SetName(
-        l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_EXTENSION_UNPACKER_NAME));
-
-    // Grant the subprocess access to our temp dir so it can write out files.
-    DCHECK(!exposed_dir.empty());
-    utility_host_->SetExposedDir(exposed_dir);
-    if (!utility_host_->StartBatchMode()) {
-      utility_host_.reset();
-      return false;
-    }
-  }
-  return true;
-}
-
-content::UtilityProcessHost* SandboxedUnpacker::UtilityHostWrapper::host()
-    const {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  return utility_host_.get();
-}
-
-SandboxedUnpacker::UtilityHostWrapper::~UtilityHostWrapper() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (utility_host_) {
-    utility_host_->EndBatchMode();
-    utility_host_.reset();
-  }
-}
-
 }  // namespace extensions
diff --git a/extensions/browser/sandboxed_unpacker.h b/extensions/browser/sandboxed_unpacker.h
index 47d07b27..9007a47 100644
--- a/extensions/browser/sandboxed_unpacker.h
+++ b/extensions/browser/sandboxed_unpacker.h
@@ -13,8 +13,7 @@
 #include "base/memory/ref_counted_delete_on_sequence.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/utility_process_host_client.h"
+#include "content/public/browser/utility_process_mojo_client.h"
 #include "extensions/browser/crx_file_info.h"
 #include "extensions/browser/install/crx_install_error.h"
 #include "extensions/common/manifest.h"
@@ -26,13 +25,13 @@
 class SequencedTaskRunner;
 }
 
-namespace content {
-class UtilityProcessHost;
-}
-
 namespace extensions {
 class Extension;
 
+namespace mojom {
+class ExtensionUnpacker;
+}
+
 class SandboxedUnpackerClient
     : public base::RefCountedDeleteOnSequence<SandboxedUnpackerClient> {
  public:
@@ -63,32 +62,31 @@
   friend class base::RefCountedDeleteOnSequence<SandboxedUnpackerClient>;
   friend class base::DeleteHelper<SandboxedUnpackerClient>;
 
-  virtual ~SandboxedUnpackerClient() {}
+  virtual ~SandboxedUnpackerClient() = default;
 };
 
 // SandboxedUnpacker does work to optionally unpack and then validate/sanitize
-// an extension, either starting from a crx file or an already unzipped
-// directory (eg from differential update). This is done in a sandboxed
-// subprocess to protect the browser process from parsing complex formats like
-// JPEG or JSON from untrusted sources.
+// an extension, either starting from a crx file, or else an already unzipped
+// directory (eg., from a differential update). This is done in a sandboxed
+// subprocess to protect the browser process from parsing complex data formats
+// like JPEG or JSON from untrusted sources.
 //
-// Unpacking an extension using this class makes minor changes to its source,
-// such as transcoding all images to PNG, parsing all message catalogs
-// and rewriting the manifest JSON. As such, it should not be used when the
-// output is not intended to be given back to the author.
-//
+// Unpacking an extension using this class makes changes to its source, such as
+// transcoding all images to PNG, parsing all message catalogs, and rewriting
+// the manifest JSON. As such, it should not be used when the output is not
+// intended to be given back to the author.
 //
 // Lifetime management:
 //
 // This class is ref-counted by each call it makes to itself on another thread,
-// and by UtilityProcessHost.
+// and by UtilityProcessMojoClient.
 //
-// Additionally, we hold a reference to our own client so that it lives at least
+// Additionally, we hold a reference to our own client so that the client lives
 // long enough to receive the result of unpacking.
 //
+// NOTE: This class should only be used on the FILE thread.
 //
-// NOTE: This class should only be used on the file thread.
-class SandboxedUnpacker : public content::UtilityProcessHostClient {
+class SandboxedUnpacker : public base::RefCountedThreadSafe<SandboxedUnpacker> {
  public:
   // Creates a SanboxedUnpacker that will do work to unpack an extension,
   // passing the |location| and |creation_flags| to Extension::Create. The
@@ -111,7 +109,7 @@
                           const base::FilePath& directory);
 
  private:
-  class ProcessHostClient;
+  friend class base::RefCountedThreadSafe<SandboxedUnpacker>;
 
   // Enumerate all the ways unpacking can fail.  Calls to ReportFailure()
   // take a failure reason as an argument, and put it in histogram
@@ -125,14 +123,14 @@
     FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY,
     COULD_NOT_GET_SANDBOX_FRIENDLY_PATH,
 
-    // SandboxedUnpacker::OnUnpackExtensionSucceeded()
+    // SandboxedUnpacker::UnpackExtensionSucceeded()
     COULD_NOT_LOCALIZE_EXTENSION,
     INVALID_MANIFEST,
 
-    // SandboxedUnpacker::OnUnpackExtensionFailed()
+    // SandboxedUnpacker::UnpackExtensionFailed()
     UNPACKER_CLIENT_FAILED,
 
-    // SandboxedUnpacker::OnProcessCrashed()
+    // SandboxedUnpacker::UtilityProcessCrashed()
     UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL,
 
     // SandboxedUnpacker::ValidateSignature()
@@ -174,45 +172,48 @@
 
     UNZIP_FAILED,
     DIRECTORY_MOVE_FAILED,
-    COULD_NOT_START_UTILITY_PROCESS,
 
     NUM_FAILURE_REASONS
   };
 
-  friend class ProcessHostClient;
   friend class SandboxedUnpackerTest;
 
-  ~SandboxedUnpacker() override;
+  ~SandboxedUnpacker();
 
-  // Set |temp_dir_| as a temporary directory to unpack the extension in.
-  // Return true on success.
-  virtual bool CreateTempDirectory();
+  // Create |temp_dir_| used to unzip or unpack the extension in.
+  bool CreateTempDirectory();
 
-  // Helper functions to simplify calls to ReportFailure.
+  // Helper functions to simplify calling ReportFailure.
   base::string16 FailureReasonToString16(FailureReason reason);
   void FailWithPackageError(FailureReason reason);
 
   // Validates the signature of the extension and extract the key to
-  // |public_key_|. Returns true if the signature validates, false otherwise.
+  // |public_key_|. True if the signature validates, false otherwise.
   bool ValidateSignature(const base::FilePath& crx_path,
                          const std::string& expected_hash);
 
-  void StartUnzipOnIOThread(const base::FilePath& crx_path);
-  void StartUnpackOnIOThread(const base::FilePath& directory_path);
+  // Ensures the utility process is created.
+  void StartUtilityProcessIfNeeded();
 
-  // UtilityProcessHostClient
-  bool OnMessageReceived(const IPC::Message& message) override;
-  void OnProcessCrashed(int exit_code) override;
+  // Utility process crashed or failed while trying to install.
+  void UtilityProcessCrashed();
 
-  // IPC message handlers.
-  void OnUnzipToDirSucceeded(const base::FilePath& directory);
-  void OnUnzipToDirFailed(const std::string& error);
-  void OnUnpackExtensionSucceeded(const base::DictionaryValue& manifest);
-  void OnUnpackExtensionFailed(const base::string16& error_message);
+  // Unzips the extension into directory.
+  void Unzip(const base::FilePath& crx_path);
+  void UnzipDone(const base::FilePath& directory, bool success);
 
-  void ReportFailure(FailureReason reason, const base::string16& message);
-  void ReportSuccess(const base::DictionaryValue& original_manifest,
+  // Unpacks the extension in directory and returns the manifest.
+  void Unpack(const base::FilePath& directory);
+  void UnpackDone(const base::string16& error,
+                  std::unique_ptr<base::DictionaryValue> manifest);
+  void UnpackExtensionSucceeded(
+      std::unique_ptr<base::DictionaryValue> manifest);
+  void UnpackExtensionFailed(const base::string16& error);
+
+  // Reports unpack success or failure, or unzip failure.
+  void ReportSuccess(std::unique_ptr<base::DictionaryValue> original_manifest,
                      const SkBitmap& install_icon);
+  void ReportFailure(FailureReason reason, const base::string16& error);
 
   // Overwrites original manifest with safe result from utility process.
   // Returns NULL on error. Caller owns the returned object.
@@ -227,83 +228,49 @@
   // Cleans up temp directory artifacts.
   void Cleanup();
 
-  // This is a helper class to make it easier to keep track of the lifecycle of
-  // a UtilityProcessHost, including automatic begin and end of batch mode.
-  class UtilityHostWrapper : public base::RefCountedThreadSafe<
-                                 UtilityHostWrapper,
-                                 content::BrowserThread::DeleteOnIOThread> {
-   public:
-    UtilityHostWrapper();
-
-    // Start up the utility process if it is not already started, putting it
-    // into batch mode and giving it access to |exposed_dir|. This should only
-    // be called on the IO thread. Returns false if there was an error starting
-    // the utility process or putting it into batch mode.
-    bool StartIfNeeded(
-        const base::FilePath& exposed_dir,
-        const scoped_refptr<UtilityProcessHostClient>& client,
-        const scoped_refptr<base::SequencedTaskRunner>& client_task_runner);
-
-    // This should only be called on the IO thread.
-    content::UtilityProcessHost* host() const;
-
-   private:
-    friend struct content::BrowserThread::DeleteOnThread<
-        content::BrowserThread::IO>;
-    friend class base::DeleteHelper<UtilityHostWrapper>;
-    ~UtilityHostWrapper();
-
-    // Should only be used on the IO thread.
-    base::WeakPtr<content::UtilityProcessHost> utility_host_;
-
-    DISALLOW_COPY_AND_ASSIGN(UtilityHostWrapper);
-  };
-
-  // If we unpacked a crx file, we hold on to the path for use in various
-  // histograms.
+  // If we unpacked a CRX file, we hold on to the path name for use
+  // in various histograms.
   base::FilePath crx_path_for_histograms_;
 
-  // Our client.
+  // Our unpacker client.
   scoped_refptr<SandboxedUnpackerClient> client_;
 
   // The Extensions directory inside the profile.
   base::FilePath extensions_dir_;
 
-  // A temporary directory to use for unpacking.
+  // Temporary directory to use for unpacking.
   base::ScopedTempDir temp_dir_;
 
-  // The root directory of the unpacked extension. This is a child of temp_dir_.
+  // Root directory of the unpacked extension (a child of temp_dir_).
   base::FilePath extension_root_;
 
   // Represents the extension we're unpacking.
   scoped_refptr<Extension> extension_;
 
-  // Whether we've received a response from the utility process yet.
-  bool got_response_;
-
   // The public key that was extracted from the CRX header.
   std::string public_key_;
 
-  // The extension's ID. This will be calculated from the public key in the crx
-  // header.
+  // The extension's ID. This will be calculated from the public key
+  // in the CRX header.
   std::string extension_id_;
 
-  // If we unpacked a .crx file, the time at which unpacking started. Used to
-  // compute the time unpacking takes.
+  // If we unpacked a CRX file, the time at which unpacking started.
+  // Used to compute the time unpacking takes.
   base::TimeTicks crx_unpack_start_time_;
 
   // Location to use for the unpacked extension.
   Manifest::Location location_;
 
-  // Creation flags to use for the extension.  These flags will be used
-  // when calling Extenion::Create() by the crx installer.
+  // Creation flags to use for the extension. These flags will be used
+  // when calling Extenion::Create() by the CRX installer.
   int creation_flags_;
 
-  // Sequenced task runner where file I/O operations will be performed at.
+  // Sequenced task runner where file I/O operations will be performed.
   scoped_refptr<base::SequencedTaskRunner> unpacker_io_task_runner_;
 
-  // Used for sending tasks to the utility process.
-  scoped_refptr<UtilityHostWrapper> utility_wrapper_;
+  // Utility client used for sending tasks to the utility process.
+  std::unique_ptr<content::UtilityProcessMojoClient<mojom::ExtensionUnpacker>>
+      utility_process_mojo_client_;
 
   DISALLOW_COPY_AND_ASSIGN(SandboxedUnpacker);
 };
diff --git a/extensions/browser/sandboxed_unpacker_unittest.cc b/extensions/browser/sandboxed_unpacker_unittest.cc
index ebd01aa..e39f7b2 100644
--- a/extensions/browser/sandboxed_unpacker_unittest.cc
+++ b/extensions/browser/sandboxed_unpacker_unittest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "extensions/browser/sandboxed_unpacker.h"
+
 #include "base/base64.h"
 #include "base/bind.h"
 #include "base/command_line.h"
@@ -16,7 +18,6 @@
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/extensions_test.h"
-#include "extensions/browser/sandboxed_unpacker.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_paths.h"
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn
index 97f392b9..48ac33a8 100644
--- a/extensions/common/BUILD.gn
+++ b/extensions/common/BUILD.gn
@@ -19,12 +19,18 @@
 if (enable_extensions) {
   mojom("mojo") {
     sources = [
+      "extension_unpacker.mojom",
       "mojo/app_window.mojom",
       "mojo/keep_alive.mojom",
     ]
+
     if (proprietary_codecs && enable_wifi_display) {
       sources += [ "mojo/wifi_display_session_service.mojom" ]
     }
+
+    public_deps = [
+      "//mojo/common:common_custom_types",
+    ]
   }
 
   # This must be a static library because extensions common depends on
@@ -93,6 +99,7 @@
       "extension_urls.cc",
       "extension_urls.h",
       "extension_utility_messages.h",
+      "extension_utility_types.h",
       "extensions_aliases.cc",
       "extensions_aliases.h",
       "extensions_client.cc",
@@ -181,6 +188,8 @@
       "manifest_handlers/web_accessible_resources_info.h",
       "manifest_handlers/webview_info.cc",
       "manifest_handlers/webview_info.h",
+      "manifest_location_param_traits.cc",
+      "manifest_location_param_traits.h",
       "manifest_url_handlers.cc",
       "manifest_url_handlers.h",
       "message_bundle.cc",
diff --git a/extensions/common/OWNERS b/extensions/common/OWNERS
index f1f3a15..7327a7b 100644
--- a/extensions/common/OWNERS
+++ b/extensions/common/OWNERS
@@ -2,3 +2,12 @@
 per-file *_messages*.h=file://ipc/SECURITY_OWNERS
 per-file *_messages.cc=set noparent
 per-file *_messages.cc=file://ipc/SECURITY_OWNERS
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
+per-file *_param_traits*.*=set noparent
+per-file *_param_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *.typemap=set noparent
+per-file *.typemap=file://ipc/SECURITY_OWNERS
+per-file *_struct_traits*.*=set noparent
+per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
+
diff --git a/extensions/common/extension_messages.cc b/extensions/common/extension_messages.cc
index 34b15ed..8e56f09 100644
--- a/extensions/common/extension_messages.cc
+++ b/extensions/common/extension_messages.cc
@@ -13,6 +13,7 @@
 #include "extensions/common/extension.h"
 #include "extensions/common/manifest.h"
 #include "extensions/common/manifest_handler.h"
+#include "extensions/common/manifest_location_param_traits.h"
 #include "extensions/common/permissions/permissions_data.h"
 #include "extensions/common/permissions/permissions_info.h"
 
@@ -101,29 +102,6 @@
 
 namespace IPC {
 
-template <>
-struct ParamTraits<Manifest::Location> {
-  typedef Manifest::Location param_type;
-  static void Write(base::Pickle* m, const param_type& p) {
-    int val = static_cast<int>(p);
-    WriteParam(m, val);
-  }
-  static bool Read(const base::Pickle* m,
-                   base::PickleIterator* iter,
-                   param_type* p) {
-    int val = 0;
-    if (!ReadParam(m, iter, &val) ||
-        val < Manifest::INVALID_LOCATION ||
-        val >= Manifest::NUM_LOCATIONS)
-      return false;
-    *p = static_cast<param_type>(val);
-    return true;
-  }
-  static void Log(const param_type& p, std::string* l) {
-    ParamTraits<int>::Log(static_cast<int>(p), l);
-  }
-};
-
 void ParamTraits<URLPattern>::GetSize(base::PickleSizer* s,
                                       const param_type& p) {
   GetParamSize(s, p.valid_schemes());
diff --git a/extensions/common/extension_unpacker.mojom b/extensions/common/extension_unpacker.mojom
new file mode 100644
index 0000000..c2321c54
--- /dev/null
+++ b/extensions/common/extension_unpacker.mojom
@@ -0,0 +1,36 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Safe chrome extension unpacker service provided by the utility process
+// and exposed by mojo policy to the chrome browser process.
+
+module extensions.mojom;
+
+import "mojo/common/file_path.mojom";
+import "mojo/common/string16.mojom";
+import "mojo/common/values.mojom";
+
+interface ExtensionUnpacker {
+  // Unzip |file| into the directory |path|.
+  Unzip(mojo.common.mojom.FilePath file,
+        mojo.common.mojom.FilePath path) => (bool success);
+
+  // Unpack and sanitize the extension in directory |path|, and return its
+  // parsed manifest.json file in |manifest|. The supplied |location|, and
+  // and the |creation_flags| defined by Extension::InitFromValueFlags are
+  // passed into Extension::Create() when unpacking the extension. Decoded
+  // image and message catalog data from the extension is written to files
+  // kDecodedImagesFilename and kDecodedMessageCatalogsFilename in |path|.
+  // If Unpack() fails for any reason, |error| contains a user-displayable
+  // explanation of what went wrong.
+  Unpack(mojo.common.mojom.FilePath path,
+         string extension_id,
+         ManifestLocation location,
+         int32 creation_flags)
+      => (mojo.common.mojom.String16 error,
+          mojo.common.mojom.DictionaryValue? manifest);
+};
+
+[Native]
+enum ManifestLocation;
diff --git a/extensions/common/extension_utility_messages.h b/extensions/common/extension_utility_messages.h
index 363392f..5ce4033 100644
--- a/extensions/common/extension_utility_messages.h
+++ b/extensions/common/extension_utility_messages.h
@@ -5,23 +5,13 @@
 // Multiply-included message file, so no include guard.
 
 #include <string>
-#include <tuple>
 
 #include "extensions/common/update_manifest.h"
 #include "ipc/ipc_message_macros.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/gfx/ipc/skia/gfx_skia_param_traits.h"
 #include "url/ipc/url_param_traits.h"
 
 #define IPC_MESSAGE_START ExtensionUtilityMsgStart
 
-#ifndef EXTENSIONS_COMMON_EXTENSION_UTILITY_MESSAGES_H_
-#define EXTENSIONS_COMMON_EXTENSION_UTILITY_MESSAGES_H_
-
-using DecodedImages = std::vector<std::tuple<SkBitmap, base::FilePath>>;
-
-#endif  //  EXTENSIONS_COMMON_EXTENSION_UTILITY_MESSAGES_H_
-
 IPC_STRUCT_TRAITS_BEGIN(UpdateManifest::Result)
   IPC_STRUCT_TRAITS_MEMBER(extension_id)
   IPC_STRUCT_TRAITS_MEMBER(version)
@@ -43,20 +33,6 @@
 IPC_MESSAGE_CONTROL1(ExtensionUtilityMsg_ParseUpdateManifest,
                      std::string /* xml document contents */)
 
-// Tell the utility process to unzip the zipfile at a given path into a
-// directory at the second given path.
-IPC_MESSAGE_CONTROL2(ExtensionUtilityMsg_UnzipToDir,
-                     base::FilePath /* zip_file */,
-                     base::FilePath /* dir */)
-
-// Tells the utility process to validate and sanitize the extension in a
-// directory.
-IPC_MESSAGE_CONTROL4(ExtensionUtilityMsg_UnpackExtension,
-                     base::FilePath /* directory_path */,
-                     std::string /* extension_id */,
-                     int /* Manifest::Location */,
-                     int /* InitFromValue flags */)
-
 //------------------------------------------------------------------------------
 // Utility process host messages:
 // These are messages from the utility process to the browser.
@@ -70,28 +46,3 @@
 // is a description of what went wrong suitable for logging.
 IPC_MESSAGE_CONTROL1(ExtensionUtilityHostMsg_ParseUpdateManifest_Failed,
                      std::string /* error_message, if any */)
-
-// Reply when the utility process is done unzipping a file. |unpacked_path|
-// is the directory which contains the unzipped contents.
-IPC_MESSAGE_CONTROL1(ExtensionUtilityHostMsg_UnzipToDir_Succeeded,
-                     base::FilePath /* unpacked_path */)
-
-// Reply when the utility process failed to unzip a file. |error| contains
-// an error string to be reported to the user.
-IPC_MESSAGE_CONTROL1(ExtensionUtilityHostMsg_UnzipToDir_Failed,
-                     std::string /* error */)
-
-// Reply when the utility process is done unpacking an extension.  |manifest|
-// is the parsed manifest.json file.
-// The unpacker should also have written out files containing the decoded
-// images and message catalogs from the extension. The data is written into a
-// DecodedImages struct into a file named kDecodedImagesFilename in the
-// directory that was passed in. This is done because the data is too large to
-// pass over IPC.
-IPC_MESSAGE_CONTROL1(ExtensionUtilityHostMsg_UnpackExtension_Succeeded,
-                     base::DictionaryValue /* manifest */)
-
-// Reply when the utility process has failed while unpacking an extension.
-// |error_message| is a user-displayable explanation of what went wrong.
-IPC_MESSAGE_CONTROL1(ExtensionUtilityHostMsg_UnpackExtension_Failed,
-                     base::string16 /* error_message, if any */)
diff --git a/extensions/common/extension_utility_types.h b/extensions/common/extension_utility_types.h
new file mode 100644
index 0000000..941a338
--- /dev/null
+++ b/extensions/common/extension_utility_types.h
@@ -0,0 +1,25 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_COMMON_EXTENSION_UTILITY_TYPES_H_
+#define EXTENSIONS_COMMON_EXTENSION_UTILITY_TYPES_H_
+
+#include <tuple>
+#include <vector>
+
+#include "ui/gfx/ipc/skia/gfx_skia_param_traits.h"
+
+class SkBitmap;
+
+namespace base {
+class FilePath;
+}
+
+namespace extensions {
+
+using DecodedImages = std::vector<std::tuple<SkBitmap, base::FilePath>>;
+
+}  // namespace extensions
+
+#endif  // EXTENSIONS_COMMON_EXTENSION_UTILITY_TYPES_H_
diff --git a/extensions/common/manifest_location.typemap b/extensions/common/manifest_location.typemap
new file mode 100644
index 0000000..b4c1da53
--- /dev/null
+++ b/extensions/common/manifest_location.typemap
@@ -0,0 +1,12 @@
+# 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.
+
+mojom = "//extensions/common/extension_unpacker.mojom"
+
+public_headers = [ "//extensions/common/manifest.h" ]
+
+traits_headers = [ "//extensions/common/manifest_location_param_traits.h" ]
+
+type_mappings =
+    [ "extensions.mojom.ManifestLocation=extensions::Manifest::Location" ]
diff --git a/extensions/common/manifest_location_param_traits.cc b/extensions/common/manifest_location_param_traits.cc
new file mode 100644
index 0000000..047461e
--- /dev/null
+++ b/extensions/common/manifest_location_param_traits.cc
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/common/manifest_location_param_traits.h"
+
+// Auto-generate IPC::ParamTraits<> code specializations for sending and
+// receiving enum extensions::Manifest::Location over IPC.
+
+#include "ipc/param_traits_size_macros.h"
+namespace IPC {
+#undef EXTENSIONS_COMMON_MANIFEST_LOCATION_PARAM_TRAITS_H_
+#include "extensions/common/manifest_location_param_traits.h"
+}  // namespace IPC
+
+#include "ipc/param_traits_write_macros.h"
+namespace IPC {
+#undef EXTENSIONS_COMMON_MANIFEST_LOCATION_PARAM_TRAITS_H_
+#include "extensions/common/manifest_location_param_traits.h"
+}  // namespace IPC
+
+#include "ipc/param_traits_read_macros.h"
+namespace IPC {
+#undef EXTENSIONS_COMMON_MANIFEST_LOCATION_PARAM_TRAITS_H_
+#include "extensions/common/manifest_location_param_traits.h"
+}  // namespace IPC
+
+#include "ipc/param_traits_log_macros.h"
+namespace IPC {
+#undef EXTENSIONS_COMMON_MANIFEST_LOCATION_PARAM_TRAITS_H_
+#include "extensions/common/manifest_location_param_traits.h"
+}  // namespace IPC
diff --git a/extensions/common/manifest_location_param_traits.h b/extensions/common/manifest_location_param_traits.h
new file mode 100644
index 0000000..15f7dde
--- /dev/null
+++ b/extensions/common/manifest_location_param_traits.h
@@ -0,0 +1,16 @@
+// 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 EXTENSIONS_COMMON_MANIFEST_LOCATION_PARAM_TRAITS_H_
+#define EXTENSIONS_COMMON_MANIFEST_LOCATION_PARAM_TRAITS_H_
+
+#include "extensions/common/manifest.h"
+#include "ipc/ipc_message_macros.h"
+#include "ipc/param_traits_macros.h"
+
+IPC_ENUM_TRAITS_MIN_MAX_VALUE(extensions::Manifest::Location,
+                              extensions::Manifest::INVALID_LOCATION,
+                              extensions::Manifest::NUM_LOCATIONS - 1)
+
+#endif  // EXTENSIONS_COMMON_MANIFEST_LOCATION_PARAM_TRAITS_H_
diff --git a/extensions/common/typemaps.gni b/extensions/common/typemaps.gni
new file mode 100644
index 0000000..3ba2210
--- /dev/null
+++ b/extensions/common/typemaps.gni
@@ -0,0 +1,5 @@
+# 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.
+
+typemaps = [ "//extensions/common/manifest_location.typemap" ]
diff --git a/extensions/test/data/extensions_test_content_utility_manifest_overlay.json b/extensions/test/data/extensions_test_content_utility_manifest_overlay.json
new file mode 100644
index 0000000..7614da85
--- /dev/null
+++ b/extensions/test/data/extensions_test_content_utility_manifest_overlay.json
@@ -0,0 +1,12 @@
+{
+  "name": "content_utility",
+  "interface_provider_specs": {
+    "service_manager:connector": {
+      "provides": {
+        "browser": [
+          "extensions::mojom::ExtensionUnpacker"
+        ]
+      }
+    }
+  }
+}
diff --git a/extensions/test/test_content_browser_client.cc b/extensions/test/test_content_browser_client.cc
new file mode 100644
index 0000000..4b83f11
--- /dev/null
+++ b/extensions/test/test_content_browser_client.cc
@@ -0,0 +1,37 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/test/test_content_browser_client.h"
+
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/path_service.h"
+#include "base/values.h"
+#include "content/public/common/service_names.mojom.h"
+#include "extensions/common/extension_paths.h"
+
+namespace extensions {
+
+TestContentBrowserClient::TestContentBrowserClient() = default;
+
+TestContentBrowserClient::~TestContentBrowserClient() = default;
+
+std::unique_ptr<base::Value>
+TestContentBrowserClient::GetServiceManifestOverlay(base::StringPiece name) {
+  if (name != content::mojom::kUtilityServiceName)
+    return nullptr;
+
+  base::FilePath extensions_test_data_dir;
+  CHECK(base::PathService::Get(DIR_TEST_DATA, &extensions_test_data_dir));
+
+  std::string contents;
+  CHECK(base::ReadFileToString(
+      extensions_test_data_dir.AppendASCII(
+          "extensions_test_content_utility_manifest_overlay.json"),
+      &contents));
+
+  return base::JSONReader::Read(contents);
+}
+
+}  // namespace extensions
diff --git a/extensions/test/test_content_browser_client.h b/extensions/test/test_content_browser_client.h
new file mode 100644
index 0000000..9ae3f01
--- /dev/null
+++ b/extensions/test/test_content_browser_client.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 EXTENSIONS_TEST_TEST_CONTENT_BROWSER_CLIENT_H_
+#define EXTENSIONS_TEST_TEST_CONTENT_BROWSER_CLIENT_H_
+
+#include <memory>
+
+#include "base/strings/string_piece.h"
+#include "content/public/browser/content_browser_client.h"
+
+namespace base {
+class Value;
+}
+
+namespace extensions {
+
+class TestContentBrowserClient : public content::ContentBrowserClient {
+ public:
+  TestContentBrowserClient();
+  ~TestContentBrowserClient() override;
+
+  // content::ContentBrowserClient:
+  std::unique_ptr<base::Value> GetServiceManifestOverlay(
+      base::StringPiece name) override;
+};
+
+}  // namespace extensions
+
+#endif  // EXTENSIONS_TEST_TEST_CONTENT_BROWSER_CLIENT_H_
diff --git a/extensions/test/test_content_utility_client.cc b/extensions/test/test_content_utility_client.cc
index 93f907e6..56aee9fc 100644
--- a/extensions/test/test_content_utility_client.cc
+++ b/extensions/test/test_content_utility_client.cc
@@ -6,19 +6,21 @@
 
 namespace extensions {
 
-TestContentUtilityClient::TestContentUtilityClient() {
-}
+TestContentUtilityClient::TestContentUtilityClient() = default;
 
-TestContentUtilityClient::~TestContentUtilityClient() {
-}
+TestContentUtilityClient::~TestContentUtilityClient() = default;
 
 void TestContentUtilityClient::UtilityThreadStarted() {
   UtilityHandler::UtilityThreadStarted();
 }
 
+void TestContentUtilityClient::ExposeInterfacesToBrowser(
+    service_manager::InterfaceRegistry* registry) {
+  UtilityHandler::ExposeInterfacesToBrowser(registry, false);
+}
+
 bool TestContentUtilityClient::OnMessageReceived(const IPC::Message& message) {
   return utility_handler_.OnMessageReceived(message);
 }
 
-
 }  // namespace extensions
diff --git a/extensions/test/test_content_utility_client.h b/extensions/test/test_content_utility_client.h
index 518806a6..813569f 100644
--- a/extensions/test/test_content_utility_client.h
+++ b/extensions/test/test_content_utility_client.h
@@ -15,8 +15,10 @@
   TestContentUtilityClient();
   ~TestContentUtilityClient() override;
 
-  // ContentUtilityClient implementation
+  // content::ContentUtilityClient:
   void UtilityThreadStarted() override;
+  void ExposeInterfacesToBrowser(
+      service_manager::InterfaceRegistry* registry) override;
   bool OnMessageReceived(const IPC::Message& message) override;
 
  private:
diff --git a/extensions/utility/unpacker.cc b/extensions/utility/unpacker.cc
index bab2f98..e946013 100644
--- a/extensions/utility/unpacker.cc
+++ b/extensions/utility/unpacker.cc
@@ -25,7 +25,7 @@
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_l10n_util.h"
-#include "extensions/common/extension_utility_messages.h"
+#include "extensions/common/extension_utility_types.h"
 #include "extensions/common/extensions_client.h"
 #include "extensions/common/file_util.h"
 #include "extensions/common/manifest.h"
diff --git a/extensions/utility/unpacker.h b/extensions/utility/unpacker.h
index fa77b69f..63b20be 100644
--- a/extensions/utility/unpacker.h
+++ b/extensions/utility/unpacker.h
@@ -53,6 +53,10 @@
   base::DictionaryValue* parsed_manifest() { return parsed_manifest_.get(); }
   base::DictionaryValue* parsed_catalogs() { return parsed_catalogs_.get(); }
 
+  std::unique_ptr<base::DictionaryValue> TakeParsedManifest() {
+    return std::move(parsed_manifest_);
+  }
+
  private:
   // Write the decoded images to kDecodedImagesFilename.  We do this instead
   // of sending them over IPC, since they are so large.  Returns true on
diff --git a/extensions/utility/utility_handler.cc b/extensions/utility/utility_handler.cc
index 59fcdfe..96a4445 100644
--- a/extensions/utility/utility_handler.cc
+++ b/extensions/utility/utility_handler.cc
@@ -6,12 +6,10 @@
 
 #include "base/command_line.h"
 #include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/i18n/rtl.h"
 #include "content/public/utility/utility_thread.h"
 #include "extensions/common/constants.h"
-#include "extensions/common/extension.h"
 #include "extensions/common/extension_l10n_util.h"
+#include "extensions/common/extension_unpacker.mojom.h"
 #include "extensions/common/extension_utility_messages.h"
 #include "extensions/common/extensions_client.h"
 #include "extensions/common/manifest.h"
@@ -20,6 +18,8 @@
 #include "extensions/utility/unpacker.h"
 #include "ipc/ipc_message.h"
 #include "ipc/ipc_message_macros.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "services/service_manager/public/cpp/interface_registry.h"
 #include "third_party/zlib/google/zip.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/ui_base_switches.h"
@@ -28,24 +28,84 @@
 
 namespace {
 
-bool Send(IPC::Message* message) {
-  return content::UtilityThread::Get()->Send(message);
-}
+class ExtensionUnpackerImpl : public extensions::mojom::ExtensionUnpacker {
+ public:
+  ExtensionUnpackerImpl() = default;
+  ~ExtensionUnpackerImpl() override = default;
 
-void ReleaseProcessIfNeeded() {
-  content::UtilityThread::Get()->ReleaseProcessIfNeeded();
-}
+  static void Create(extensions::mojom::ExtensionUnpackerRequest request) {
+    mojo::MakeStrongBinding(base::MakeUnique<ExtensionUnpackerImpl>(),
+                            std::move(request));
+  }
 
-const char kExtensionHandlerUnzipError[] =
-    "Could not unzip extension for install.";
+ private:
+  // extensions::mojom::ExtensionUnpacker:
+  void Unzip(const base::FilePath& file,
+             const base::FilePath& path,
+             const UnzipCallback& callback) override {
+    std::unique_ptr<base::DictionaryValue> manifest;
+    if (UnzipFileManifestIntoPath(file, path, &manifest)) {
+      callback.Run(UnzipFileIntoPath(file, path, std::move(manifest)));
+    } else {
+      callback.Run(false);
+    }
+  }
+
+  void Unpack(const base::FilePath& path,
+              const std::string& extension_id,
+              Manifest::Location location,
+              int32_t creation_flags,
+              const UnpackCallback& callback) override {
+    CHECK_GT(location, Manifest::INVALID_LOCATION);
+    CHECK_LT(location, Manifest::NUM_LOCATIONS);
+    DCHECK(ExtensionsClient::Get());
+
+    content::UtilityThread::Get()->EnsureBlinkInitialized();
+
+    Unpacker unpacker(path.DirName(), path, extension_id, location,
+                      creation_flags);
+    if (unpacker.Run()) {
+      callback.Run(base::string16(), unpacker.TakeParsedManifest());
+    } else {
+      callback.Run(unpacker.error_message(), nullptr);
+    }
+  }
+
+  static bool UnzipFileManifestIntoPath(
+      const base::FilePath& file,
+      const base::FilePath& path,
+      std::unique_ptr<base::DictionaryValue>* manifest) {
+    if (zip::UnzipWithFilterCallback(
+            file, path, base::Bind(&Unpacker::IsManifestFile), false)) {
+      std::string error;
+      *manifest = Unpacker::ReadManifest(path, &error);
+      return error.empty() && manifest->get();
+    }
+
+    return false;
+  }
+
+  static bool UnzipFileIntoPath(
+      const base::FilePath& file,
+      const base::FilePath& path,
+      std::unique_ptr<base::DictionaryValue> manifest) {
+    Manifest internal(Manifest::INTERNAL, std::move(manifest));
+    // TODO(crbug.com/645263): This silently ignores blocked file types.
+    //                         Add install warnings.
+    return zip::UnzipWithFilterCallback(
+        file, path,
+        base::Bind(&Unpacker::ShouldExtractFile, internal.is_theme()),
+        true /* log_skipped_files */);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(ExtensionUnpackerImpl);
+};
 
 }  // namespace
 
-UtilityHandler::UtilityHandler() {
-}
+UtilityHandler::UtilityHandler() = default;
 
-UtilityHandler::~UtilityHandler() {
-}
+UtilityHandler::~UtilityHandler() = default;
 
 // static
 void UtilityHandler::UtilityThreadStarted() {
@@ -55,13 +115,23 @@
     extension_l10n_util::SetProcessLocale(lang);
 }
 
+// static
+void UtilityHandler::ExposeInterfacesToBrowser(
+    service_manager::InterfaceRegistry* registry,
+    bool running_elevated) {
+  // If our process runs with elevated privileges, only add elevated Mojo
+  // services to the interface registry.
+  if (running_elevated)
+    return;
+
+  registry->AddInterface(base::Bind(&ExtensionUnpackerImpl::Create));
+}
+
 bool UtilityHandler::OnMessageReceived(const IPC::Message& message) {
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(UtilityHandler, message)
     IPC_MESSAGE_HANDLER(ExtensionUtilityMsg_ParseUpdateManifest,
                         OnParseUpdateManifest)
-    IPC_MESSAGE_HANDLER(ExtensionUtilityMsg_UnzipToDir, OnUnzipToDir)
-    IPC_MESSAGE_HANDLER(ExtensionUtilityMsg_UnpackExtension, OnUnpackExtension)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
   return handled;
@@ -70,73 +140,15 @@
 void UtilityHandler::OnParseUpdateManifest(const std::string& xml) {
   UpdateManifest manifest;
   if (!manifest.Parse(xml)) {
-    Send(new ExtensionUtilityHostMsg_ParseUpdateManifest_Failed(
-        manifest.errors()));
+    content::UtilityThread::Get()->Send(
+        new ExtensionUtilityHostMsg_ParseUpdateManifest_Failed(
+            manifest.errors()));
   } else {
-    Send(new ExtensionUtilityHostMsg_ParseUpdateManifest_Succeeded(
-        manifest.results()));
+    content::UtilityThread::Get()->Send(
+        new ExtensionUtilityHostMsg_ParseUpdateManifest_Succeeded(
+            manifest.results()));
   }
-  ReleaseProcessIfNeeded();
+  content::UtilityThread::Get()->ReleaseProcessIfNeeded();
 }
 
-void UtilityHandler::OnUnzipToDir(const base::FilePath& zip_path,
-                                  const base::FilePath& dir) {
-  // First extract only the manifest to determine the extension type.
-  if (!zip::UnzipWithFilterCallback(zip_path, dir,
-                                    base::Bind(&Unpacker::IsManifestFile),
-                                    false /* log_skipped_files */)) {
-    Send(new ExtensionUtilityHostMsg_UnzipToDir_Failed(
-        std::string(kExtensionHandlerUnzipError)));
-    ReleaseProcessIfNeeded();
-    return;
-  }
-
-  // Load the manifest.
-  std::string error;
-  std::unique_ptr<base::DictionaryValue> dict =
-      Unpacker::ReadManifest(dir, &error);
-  if (!dict.get()) {
-    Send(new ExtensionUtilityHostMsg_UnzipToDir_Failed(
-        std::string(kExtensionHandlerUnzipError)));
-    ReleaseProcessIfNeeded();
-    return;
-  }
-
-  Manifest manifest(Manifest::INTERNAL, std::move(dict));
-  base::Callback<bool(const base::FilePath&)> filetype_filter_cb =
-      base::Bind(&Unpacker::ShouldExtractFile, manifest.is_theme());
-
-  // TODO(crbug.com/645263): This silently ignores blocked file types.
-  //                         Add install warnings.
-  if (!zip::UnzipWithFilterCallback(zip_path, dir, filetype_filter_cb,
-                                    true /* log_skipped_files */)) {
-    Send(new ExtensionUtilityHostMsg_UnzipToDir_Failed(
-        std::string(kExtensionHandlerUnzipError)));
-  } else {
-    Send(new ExtensionUtilityHostMsg_UnzipToDir_Succeeded(dir));
-  }
-  ReleaseProcessIfNeeded();
-}
-
-void UtilityHandler::OnUnpackExtension(const base::FilePath& directory_path,
-                                       const std::string& extension_id,
-                                       int location,
-                                       int creation_flags) {
-  CHECK_GT(location, Manifest::INVALID_LOCATION);
-  CHECK_LT(location, Manifest::NUM_LOCATIONS);
-  DCHECK(ExtensionsClient::Get());
-  content::UtilityThread::Get()->EnsureBlinkInitialized();
-  Unpacker unpacker(directory_path.DirName(), directory_path, extension_id,
-                    static_cast<Manifest::Location>(location), creation_flags);
-  if (unpacker.Run()) {
-    Send(new ExtensionUtilityHostMsg_UnpackExtension_Succeeded(
-        *unpacker.parsed_manifest()));
-  } else {
-    Send(new ExtensionUtilityHostMsg_UnpackExtension_Failed(
-        unpacker.error_message()));
-  }
-  ReleaseProcessIfNeeded();
-}
-
-
 }  // namespace extensions
diff --git a/extensions/utility/utility_handler.h b/extensions/utility/utility_handler.h
index 2bc12f4..2378dcd 100644
--- a/extensions/utility/utility_handler.h
+++ b/extensions/utility/utility_handler.h
@@ -7,17 +7,16 @@
 
 #include <string>
 
-#include "base/callback.h"
 #include "base/macros.h"
 
-namespace base {
-class FilePath;
-}
-
 namespace IPC {
 class Message;
 }
 
+namespace service_manager {
+class InterfaceRegistry;
+}
+
 namespace extensions {
 
 // A handler for extensions-related IPC from within utility processes.
@@ -28,16 +27,15 @@
 
   static void UtilityThreadStarted();
 
+  static void ExposeInterfacesToBrowser(
+      service_manager::InterfaceRegistry* registry,
+      bool running_elevated);
+
   bool OnMessageReceived(const IPC::Message& message);
 
  private:
   // IPC message handlers.
   void OnParseUpdateManifest(const std::string& xml);
-  void OnUnzipToDir(const base::FilePath& zip_path, const base::FilePath& dir);
-  void OnUnpackExtension(const base::FilePath& directory_path,
-                         const std::string& extension_id,
-                         int location,
-                         int creation_flags);
 
   DISALLOW_COPY_AND_ASSIGN(UtilityHandler);
 };
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.mm b/ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.mm
index 45ddedc..a7fff71 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.mm
@@ -120,7 +120,7 @@
 // Stops observing the ReadingListModel.
 - (void)stopObservingReadingListModel;
 // Returns the ReadingListEntry associated with the |item|. If there is not such
-// an entry, returns nullptr and reloads the UI.
+// an entry, returns nullptr.
 - (const ReadingListEntry*)readingListEntryForItem:
     (ReadingListCollectionViewItem*)item;
 // Updates the toolbar state according to the selected items.
@@ -619,9 +619,12 @@
       attributesProvider:self.attributesProvider
                      url:url
        distillationState:entry.DistilledState()];
-  NSString* urlString = base::SysUTF16ToNSString(url_formatter::FormatUrl(url));
+  NSString* fullUrlString =
+      base::SysUTF16ToNSString(url_formatter::FormatUrl(url));
+  NSString* urlString =
+      base::SysUTF16ToNSString(url_formatter::FormatUrl(url.GetOrigin()));
   NSString* title = base::SysUTF8ToNSString(entry.Title());
-  item.text = [title length] ? title : urlString;
+  item.text = [title length] ? title : fullUrlString;
   item.detailText = urlString;
   item.faviconPageURL =
       entry.DistilledURL().is_valid() ? entry.DistilledURL() : url;
@@ -695,12 +698,6 @@
   const ReadingListEntry* readingListEntry =
       self.readingListModel->GetEntryByURL(item.url);
 
-  if (!readingListEntry) {
-    // The entry has been removed from the model, reload all data to synchronize
-    // the UI with the model.
-    [self reloadData];
-  }
-
   return readingListEntry;
 }
 
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
index 02f700d9d..46eb0e4 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
@@ -13,6 +13,7 @@
 #import "ios/chrome/browser/ui/commands/generic_chrome_command.h"
 #include "ios/chrome/browser/ui/commands/ios_command_ids.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_collection_view_item.h"
+#import "ios/chrome/browser/ui/reading_list/reading_list_empty_collection_background.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ios/chrome/grit/ios_theme_resources.h"
@@ -195,6 +196,12 @@
   }
   return size;
 }
+
+// Returns a match for the Reading List Empty Collection Background.
+id<GREYMatcher> EmptyBackground() {
+  return grey_accessibilityID(
+      [ReadingListEmptyCollectionBackground accessibilityIdentifier]);
+}
 }  // namespace
 
 // Test class for the Reading List menu.
@@ -528,4 +535,29 @@
                  GetReadingListModel()->unread_size());
 }
 
+// Tests that you can delete multiple read items in the Reading List without
+// creating a crash (crbug.com/701956).
+- (void)testDeleteMultipleItems {
+  // Add entries.
+  ReadingListModel* model = GetReadingListModel();
+  for (int i = 0; i < 11; i++) {
+    std::string increment = std::to_string(i);
+    model->AddEntry(GURL(kReadURL + increment),
+                    std::string(kReadTitle + increment),
+                    reading_list::ADDED_VIA_CURRENT_APP);
+    model->SetReadStatus(GURL(kReadURL + increment), true);
+  }
+
+  // Delete them from the Reading List view.
+  OpenReadingList();
+  [[EarlGrey selectElementWithMatcher:EmptyBackground()]
+      assertWithMatcher:grey_nil()];
+  TapButtonWithID(IDS_IOS_READING_LIST_EDIT_BUTTON);
+  TapButtonWithID(IDS_IOS_READING_LIST_DELETE_ALL_READ_BUTTON);
+
+  // Verify the background string is displayed.
+  [[EarlGrey selectElementWithMatcher:EmptyBackground()]
+      assertWithMatcher:grey_notNil()];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_empty_collection_background.h b/ios/chrome/browser/ui/reading_list/reading_list_empty_collection_background.h
index 4ce426d7..edd28f6 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_empty_collection_background.h
+++ b/ios/chrome/browser/ui/reading_list/reading_list_empty_collection_background.h
@@ -14,6 +14,8 @@
 - (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE;
 - (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
 
++ (NSString*)accessibilityIdentifier;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_EMPTY_COLLECTION_BACKGROUND_H_
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_empty_collection_background.mm b/ios/chrome/browser/ui/reading_list/reading_list_empty_collection_background.mm
index 4c8ac93..7b7e141 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_empty_collection_background.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_empty_collection_background.mm
@@ -64,6 +64,8 @@
 
 @implementation ReadingListEmptyCollectionBackground
 
+#pragma mark - Public
+
 - (instancetype)init {
   self = [super initWithFrame:CGRectZero];
   if (self) {
@@ -172,6 +174,8 @@
     label.numberOfLines = 0;
     label.textAlignment = NSTextAlignmentCenter;
     label.accessibilityLabel = accessibilityLabel;
+    label.accessibilityIdentifier =
+        [ReadingListEmptyCollectionBackground accessibilityIdentifier];
     [label setTranslatesAutoresizingMaskIntoConstraints:NO];
     [self addSubview:label];
 
@@ -185,6 +189,12 @@
   return self;
 }
 
++ (NSString*)accessibilityIdentifier {
+  return @"ReadingListBackgroundViewIdentifier";
+}
+
+#pragma mark - Private
+
 - (void)attachIconNamed:(NSString*)iconName
                toString:(NSMutableAttributedString*)instructionString
               withCaret:(NSMutableAttributedString*)caret
diff --git a/mojo/android/javatests/src/org/chromium/mojo/HandleMock.java b/mojo/android/javatests/src/org/chromium/mojo/HandleMock.java
index 6783c0903..1f8de94 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/HandleMock.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/HandleMock.java
@@ -5,7 +5,7 @@
 package org.chromium.mojo;
 
 import org.chromium.mojo.system.Core;
-import org.chromium.mojo.system.Core.WaitResult;
+import org.chromium.mojo.system.Core.HandleSignalsState;
 import org.chromium.mojo.system.DataPipe;
 import org.chromium.mojo.system.DataPipe.ConsumerHandle;
 import org.chromium.mojo.system.DataPipe.ProducerHandle;
@@ -35,14 +35,11 @@
     }
 
     /**
-     * @see Handle#wait(Core.HandleSignals, long)
+     * @see Handle#querySignalsState()
      */
     @Override
-    public WaitResult wait(Core.HandleSignals signals, long deadline) {
-        // Do nothing.
-        WaitResult result = new WaitResult();
-        result.setMojoResult(MojoResult.OK);
-        return result;
+    public HandleSignalsState querySignalsState() {
+        return null;
     }
 
     /**
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java
index 5affb8f..6aa1726 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java
@@ -12,7 +12,6 @@
 import org.chromium.mojo.bindings.BindingsTestUtils.RecordingMessageReceiverWithResponder;
 import org.chromium.mojo.system.Core;
 import org.chromium.mojo.system.Core.HandleSignals;
-import org.chromium.mojo.system.Core.WaitResult;
 import org.chromium.mojo.system.Handle;
 import org.chromium.mojo.system.MessagePipeHandle;
 import org.chromium.mojo.system.MojoResult;
@@ -227,8 +226,6 @@
 
         // Confirm that the pipe was closed on the Router side.
         HandleSignals closedFlag = HandleSignals.none().setPeerClosed(true);
-        WaitResult result = mHandle.wait(closedFlag, 0);
-        assertEquals(MojoResult.OK, result.getMojoResult());
-        assertEquals(closedFlag, result.getHandleSignalsState().getSatisfiedSignals());
+        assertEquals(closedFlag, mHandle.querySignalsState().getSatisfiedSignals());
     }
 }
diff --git a/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java b/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java
index 77a9bda..5120198 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java
@@ -9,9 +9,6 @@
 import org.chromium.mojo.MojoTestCase;
 import org.chromium.mojo.system.Core;
 import org.chromium.mojo.system.Core.HandleSignals;
-import org.chromium.mojo.system.Core.HandleSignalsState;
-import org.chromium.mojo.system.Core.WaitManyResult;
-import org.chromium.mojo.system.Core.WaitResult;
 import org.chromium.mojo.system.DataPipe;
 import org.chromium.mojo.system.Handle;
 import org.chromium.mojo.system.InvalidHandle;
@@ -30,7 +27,6 @@
 import java.util.Random;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Testing the core API.
@@ -76,22 +72,6 @@
         mHandlesToClose.add(handles.second);
     }
 
-    /**
-     * Runnable that will close the given handle.
-     */
-    private static class CloseHandle implements Runnable {
-        private Handle mHandle;
-
-        CloseHandle(Handle handle) {
-            mHandle = handle;
-        }
-
-        @Override
-        public void run() {
-            mHandle.close();
-        }
-    }
-
     private static void checkSendingMessage(MessagePipeHandle in, MessagePipeHandle out) {
         Random random = new Random();
 
@@ -184,46 +164,6 @@
     }
 
     /**
-     * Testing {@link Core#waitMany(List, long)}.
-     */
-    @SmallTest
-    public void testWaitMany() {
-        Core core = CoreImpl.getInstance();
-        Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
-        addHandlePairToClose(handles);
-
-        // Test waiting on handles of a newly created message pipe - each should be writable, but
-        // not readable.
-        List<Pair<Handle, Core.HandleSignals>> handlesToWaitOn =
-                new ArrayList<Pair<Handle, Core.HandleSignals>>();
-        handlesToWaitOn.add(
-                new Pair<Handle, Core.HandleSignals>(handles.second, Core.HandleSignals.READABLE));
-        handlesToWaitOn.add(
-                new Pair<Handle, Core.HandleSignals>(handles.first, Core.HandleSignals.WRITABLE));
-        WaitManyResult result = core.waitMany(handlesToWaitOn, 0);
-        assertEquals(MojoResult.OK, result.getMojoResult());
-        assertEquals(1, result.getHandleIndex());
-        for (HandleSignalsState state : result.getSignalStates()) {
-            assertEquals(HandleSignals.WRITABLE, state.getSatisfiedSignals());
-            assertEquals(ALL_SIGNALS, state.getSatisfiableSignals());
-        }
-
-        // Same test, but swap the handles around.
-        handlesToWaitOn.clear();
-        handlesToWaitOn.add(
-                new Pair<Handle, Core.HandleSignals>(handles.first, Core.HandleSignals.WRITABLE));
-        handlesToWaitOn.add(
-                new Pair<Handle, Core.HandleSignals>(handles.second, Core.HandleSignals.READABLE));
-        result = core.waitMany(handlesToWaitOn, 0);
-        assertEquals(MojoResult.OK, result.getMojoResult());
-        assertEquals(0, result.getHandleIndex());
-        for (HandleSignalsState state : result.getSignalStates()) {
-            assertEquals(HandleSignals.WRITABLE, state.getSatisfiedSignals());
-            assertEquals(ALL_SIGNALS, state.getSatisfiableSignals());
-        }
-    }
-
-    /**
      * Testing that Core can be retrieved from a handle.
      */
     @SmallTest
@@ -274,53 +214,14 @@
         Core core = CoreImpl.getInstance();
         Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
         addHandlePairToClose(handles);
-        // Test waiting on handles of a newly created message pipe.
-        WaitResult waitResult = handles.first.wait(
-                Core.HandleSignals.none().setReadable(true).setWritable(true), 0);
-        assertEquals(MojoResult.OK, waitResult.getMojoResult());
-        assertEquals(
-                HandleSignals.WRITABLE, waitResult.getHandleSignalsState().getSatisfiedSignals());
-        assertEquals(ALL_SIGNALS, waitResult.getHandleSignalsState().getSatisfiableSignals());
-
-        waitResult = handles.first.wait(Core.HandleSignals.WRITABLE, 0);
-        assertEquals(MojoResult.OK, waitResult.getMojoResult());
-        assertEquals(
-                HandleSignals.WRITABLE, waitResult.getHandleSignalsState().getSatisfiedSignals());
-        assertEquals(ALL_SIGNALS, waitResult.getHandleSignalsState().getSatisfiableSignals());
-
-        waitResult = handles.first.wait(Core.HandleSignals.READABLE, 0);
-        assertEquals(MojoResult.DEADLINE_EXCEEDED, waitResult.getMojoResult());
-        assertEquals(
-                HandleSignals.WRITABLE, waitResult.getHandleSignalsState().getSatisfiedSignals());
-        assertEquals(ALL_SIGNALS, waitResult.getHandleSignalsState().getSatisfiableSignals());
 
         // Testing read on an empty pipe.
         ResultAnd<MessagePipeHandle.ReadMessageResult> readResult =
                 handles.first.readMessage(null, 0, MessagePipeHandle.ReadFlags.NONE);
         assertEquals(MojoResult.SHOULD_WAIT, readResult.getMojoResult());
 
-        // Closing a pipe while waiting.
-        WORKER.schedule(new CloseHandle(handles.first), 10, TimeUnit.MILLISECONDS);
-        waitResult = handles.first.wait(Core.HandleSignals.READABLE, 1000000L);
-        assertEquals(MojoResult.CANCELLED, waitResult.getMojoResult());
-        assertEquals(
-                HandleSignals.none(), waitResult.getHandleSignalsState().getSatisfiedSignals());
-        assertEquals(
-                HandleSignals.none(), waitResult.getHandleSignalsState().getSatisfiableSignals());
-
-        handles = core.createMessagePipe(null);
-        addHandlePairToClose(handles);
-
-        // Closing the other pipe while waiting.
-        WORKER.schedule(new CloseHandle(handles.first), 10, TimeUnit.MILLISECONDS);
-        waitResult = handles.second.wait(Core.HandleSignals.READABLE, 1000000L);
-        assertEquals(MojoResult.FAILED_PRECONDITION, waitResult.getMojoResult());
-
-        // Waiting on a closed pipe.
-        waitResult = handles.second.wait(Core.HandleSignals.READABLE, 0);
-        assertEquals(MojoResult.FAILED_PRECONDITION, waitResult.getMojoResult());
-        waitResult = handles.second.wait(Core.HandleSignals.WRITABLE, 0);
-        assertEquals(MojoResult.FAILED_PRECONDITION, waitResult.getMojoResult());
+        handles.first.close();
+        handles.second.close();
     }
 
     /**
@@ -540,29 +441,6 @@
         Core core = CoreImpl.getInstance();
         Handle handle = InvalidHandle.INSTANCE;
 
-        // Checking wait.
-        boolean exception = false;
-        try {
-            core.wait(handle, Core.HandleSignals.WRITABLE, 0);
-        } catch (MojoException e) {
-            assertEquals(MojoResult.INVALID_ARGUMENT, e.getMojoResult());
-            exception = true;
-        }
-        assertTrue(exception);
-
-        // Checking waitMany.
-        exception = false;
-        try {
-            List<Pair<Handle, Core.HandleSignals>> handles =
-                    new ArrayList<Pair<Handle, Core.HandleSignals>>();
-            handles.add(Pair.create(handle, Core.HandleSignals.WRITABLE));
-            core.waitMany(handles, 0);
-        } catch (MojoException e) {
-            assertEquals(MojoResult.INVALID_ARGUMENT, e.getMojoResult());
-            exception = true;
-        }
-        assertTrue(exception);
-
         // Checking sending an invalid handle.
         // Until the behavior is changed on the C++ side, handle gracefully 2 different use case:
         // - Receive a INVALID_ARGUMENT exception
diff --git a/mojo/android/system/core_impl.cc b/mojo/android/system/core_impl.cc
index 5cbd754..e53ed75c 100644
--- a/mojo/android/system/core_impl.cc
+++ b/mojo/android/system/core_impl.cc
@@ -26,41 +26,6 @@
   return MojoGetTimeTicksNow();
 }
 
-static jint WaitMany(JNIEnv* env,
-                     const JavaParamRef<jobject>& jcaller,
-                     const JavaParamRef<jobject>& buffer,
-                     jlong deadline) {
-  // |buffer| contains, in this order
-  // input: The array of N handles (MojoHandle, 4 bytes each)
-  // input: The array of N signals (MojoHandleSignals, 4 bytes each)
-  // space for output: The array of N handle states (MojoHandleSignalsState, 8
-  //                   bytes each)
-  // space for output: The result index (uint32_t, 4 bytes)
-  uint8_t* buffer_start =
-      static_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
-  DCHECK(buffer_start);
-  DCHECK_EQ(reinterpret_cast<uintptr_t>(buffer_start) % 8, 0u);
-  // Each handle of the input array contributes 4 (MojoHandle) + 4
-  // (MojoHandleSignals) + 8 (MojoHandleSignalsState) = 16 bytes to the size of
-  // the buffer.
-  const size_t size_per_handle = 16;
-  const size_t buffer_size = env->GetDirectBufferCapacity(buffer);
-  DCHECK_EQ((buffer_size - 4) % size_per_handle, 0u);
-
-  const size_t nb_handles = (buffer_size - 4) / size_per_handle;
-  const MojoHandle* handle_start =
-      reinterpret_cast<const MojoHandle*>(buffer_start);
-  const MojoHandleSignals* signals_start =
-      reinterpret_cast<const MojoHandleSignals*>(buffer_start + 4 * nb_handles);
-  MojoHandleSignalsState* states_start =
-      reinterpret_cast<MojoHandleSignalsState*>(buffer_start + 8 * nb_handles);
-  uint32_t* result_index =
-      reinterpret_cast<uint32_t*>(buffer_start + 16 * nb_handles);
-  *result_index = static_cast<uint32_t>(-1);
-  return MojoWaitMany(handle_start, signals_start, nb_handles, deadline,
-                      result_index, states_start);
-}
-
 static ScopedJavaLocalRef<jobject> CreateMessagePipe(
     JNIEnv* env,
     const JavaParamRef<jobject>& jcaller,
@@ -127,21 +92,16 @@
   return MojoClose(mojo_handle);
 }
 
-static jint Wait(JNIEnv* env,
-                 const JavaParamRef<jobject>& jcaller,
-                 const JavaParamRef<jobject>& buffer,
-                 jint mojo_handle,
-                 jint signals,
-                 jlong deadline) {
-  // Buffer contains space for the MojoHandleSignalsState
-  void* buffer_start = env->GetDirectBufferAddress(buffer);
-  DCHECK(buffer_start);
-  DCHECK_EQ(reinterpret_cast<const uintptr_t>(buffer_start) % 8, 0u);
-  DCHECK_EQ(sizeof(struct MojoHandleSignalsState),
+static jint QueryHandleSignalsState(JNIEnv* env,
+                                    const JavaParamRef<jobject>& jcaller,
+                                    jint mojo_handle,
+                                    const JavaParamRef<jobject>& buffer) {
+  MojoHandleSignalsState* signals_state =
+      static_cast<MojoHandleSignalsState*>(env->GetDirectBufferAddress(buffer));
+  DCHECK(signals_state);
+  DCHECK_EQ(sizeof(MojoHandleSignalsState),
             static_cast<size_t>(env->GetDirectBufferCapacity(buffer)));
-  struct MojoHandleSignalsState* signals_state =
-      static_cast<struct MojoHandleSignalsState*>(buffer_start);
-  return MojoWait(mojo_handle, signals, deadline, signals_state);
+  return MojoQueryHandleSignalsState(mojo_handle, signals_state);
 }
 
 static jint WriteMessage(JNIEnv* env,
diff --git a/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java b/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java
index 8330586..173f801 100644
--- a/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java
+++ b/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java
@@ -8,6 +8,7 @@
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.MainDex;
 import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.Core.HandleSignalsState;
 import org.chromium.mojo.system.DataPipe;
 import org.chromium.mojo.system.DataPipe.ConsumerHandle;
 import org.chromium.mojo.system.DataPipe.ProducerHandle;
@@ -27,7 +28,6 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -91,61 +91,6 @@
     }
 
     /**
-     * @see Core#waitMany(List, long)
-     */
-    @Override
-    public WaitManyResult waitMany(List<Pair<Handle, HandleSignals>> handles, long deadline) {
-        // Allocate a direct buffer to allow native code not to reach back to java. The buffer
-        // layout will be:
-        // input: The array of handles (int, 4 bytes each)
-        // input: The array of signals (int, 4 bytes each)
-        // space for output: The array of handle states (2 ints, 8 bytes each)
-        // Space for output: The result index (int, 4 bytes)
-        // The handles and signals will be filled before calling the native method. When the native
-        // method returns, the handle states and the index will have been set.
-        ByteBuffer buffer = allocateDirectBuffer(handles.size() * 16 + 4);
-        int index = 0;
-        for (Pair<Handle, HandleSignals> handle : handles) {
-            buffer.putInt(HANDLE_SIZE * index, getMojoHandle(handle.first));
-            buffer.putInt(
-                    HANDLE_SIZE * handles.size() + FLAG_SIZE * index, handle.second.getFlags());
-            index++;
-        }
-        int code = nativeWaitMany(buffer, deadline);
-        WaitManyResult result = new WaitManyResult();
-        result.setMojoResult(filterMojoResultForWait(code));
-        result.setHandleIndex(buffer.getInt(handles.size() * 16));
-        if (result.getMojoResult() != MojoResult.INVALID_ARGUMENT
-                && result.getMojoResult() != MojoResult.RESOURCE_EXHAUSTED) {
-            HandleSignalsState[] states = new HandleSignalsState[handles.size()];
-            for (int i = 0; i < handles.size(); ++i) {
-                states[i] = new HandleSignalsState(
-                        new HandleSignals(buffer.getInt(8 * (handles.size() + i))),
-                        new HandleSignals(buffer.getInt(8 * (handles.size() + i) + 4)));
-            }
-            result.setSignalStates(Arrays.asList(states));
-        }
-        return result;
-    }
-
-    /**
-     * @see Core#wait(Handle, HandleSignals, long)
-     */
-    @Override
-    public WaitResult wait(Handle handle, HandleSignals signals, long deadline) {
-        // Allocate a direct buffer to allow native code not to reach back to java. Buffer will
-        // contain spaces to write the handle state.
-        ByteBuffer buffer = allocateDirectBuffer(8);
-        WaitResult result = new WaitResult();
-        result.setMojoResult(filterMojoResultForWait(
-                nativeWait(buffer, getMojoHandle(handle), signals.getFlags(), deadline)));
-        HandleSignalsState signalsState = new HandleSignalsState(
-                new HandleSignals(buffer.getInt(0)), new HandleSignals(buffer.getInt(4)));
-        result.setHandleSignalsState(signalsState);
-        return result;
-    }
-
-    /**
      * @see Core#createMessagePipe(MessagePipeHandle.CreateOptions)
      */
     @Override
@@ -262,6 +207,14 @@
         }
     }
 
+    HandleSignalsState queryHandleSignalsState(int mojoHandle) {
+        ByteBuffer buffer = allocateDirectBuffer(8);
+        int result = nativeQueryHandleSignalsState(mojoHandle, buffer);
+        if (result != MojoResult.OK) throw new MojoException(result);
+        return new HandleSignalsState(
+                new HandleSignals(buffer.getInt(0)), new HandleSignals(buffer.getInt(4)));
+    }
+
     /**
      * @see MessagePipeHandle#writeMessage(ByteBuffer, List, MessagePipeHandle.WriteFlags)
      */
@@ -525,8 +478,6 @@
 
     private native long nativeGetTimeTicksNow();
 
-    private native int nativeWaitMany(ByteBuffer buffer, long deadline);
-
     private native ResultAnd<IntegerPair> nativeCreateMessagePipe(ByteBuffer optionsBuffer);
 
     private native ResultAnd<IntegerPair> nativeCreateDataPipe(ByteBuffer optionsBuffer);
@@ -536,7 +487,7 @@
 
     private native int nativeClose(int mojoHandle);
 
-    private native int nativeWait(ByteBuffer buffer, int mojoHandle, int signals, long deadline);
+    private native int nativeQueryHandleSignalsState(int mojoHandle, ByteBuffer signalsStateBuffer);
 
     private native int nativeWriteMessage(
             int mojoHandle, ByteBuffer bytes, int numBytes, ByteBuffer handlesBuffer, int flags);
diff --git a/mojo/android/system/src/org/chromium/mojo/system/impl/HandleBase.java b/mojo/android/system/src/org/chromium/mojo/system/impl/HandleBase.java
index a8870a8..4d149a4 100644
--- a/mojo/android/system/src/org/chromium/mojo/system/impl/HandleBase.java
+++ b/mojo/android/system/src/org/chromium/mojo/system/impl/HandleBase.java
@@ -7,8 +7,7 @@
 import android.util.Log;
 
 import org.chromium.mojo.system.Core;
-import org.chromium.mojo.system.Core.HandleSignals;
-import org.chromium.mojo.system.Core.WaitResult;
+import org.chromium.mojo.system.Core.HandleSignalsState;
 import org.chromium.mojo.system.Handle;
 import org.chromium.mojo.system.UntypedHandle;
 
@@ -63,11 +62,11 @@
     }
 
     /**
-     * @see org.chromium.mojo.system.Handle#wait(HandleSignals, long)
+     * @see org.chromium.mojo.system.Handle#querySignalsState()
      */
     @Override
-    public WaitResult wait(HandleSignals signals, long deadline) {
-        return mCore.wait(this, signals, deadline);
+    public HandleSignalsState querySignalsState() {
+        return mCore.queryHandleSignalsState(mMojoHandle);
     }
 
     /**
diff --git a/mojo/edk/embedder/entrypoints.cc b/mojo/edk/embedder/entrypoints.cc
index ecf1630..5c149d9 100644
--- a/mojo/edk/embedder/entrypoints.cc
+++ b/mojo/edk/embedder/entrypoints.cc
@@ -28,6 +28,12 @@
   return g_core->Close(handle);
 }
 
+MojoResult MojoQueryHandleSignalsStateImpl(
+    MojoHandle handle,
+    MojoHandleSignalsState* signals_state) {
+  return g_core->QueryHandleSignalsState(handle, signals_state);
+}
+
 MojoResult MojoWaitImpl(MojoHandle handle,
                         MojoHandleSignals signals,
                         MojoDeadline deadline,
@@ -281,6 +287,7 @@
   MojoSystemThunks system_thunks = {sizeof(MojoSystemThunks),
                                     MojoGetTimeTicksNowImpl,
                                     MojoCloseImpl,
+                                    MojoQueryHandleSignalsStateImpl,
                                     MojoWaitImpl,
                                     MojoWaitManyImpl,
                                     MojoCreateMessagePipeImpl,
diff --git a/mojo/edk/js/core.cc b/mojo/edk/js/core.cc
index f3eec8c..db9a395 100644
--- a/mojo/edk/js/core.cc
+++ b/mojo/edk/js/core.cc
@@ -35,6 +35,20 @@
   return MOJO_RESULT_OK;
 }
 
+gin::Dictionary QueryHandleSignalsState(const gin::Arguments& args,
+                                        mojo::Handle handle) {
+  gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
+  if (!handle.is_valid()) {
+    dictionary.Set("result", MOJO_RESULT_INVALID_ARGUMENT);
+  } else {
+    HandleSignalsState state = handle.QuerySignalsState();
+    dictionary.Set("result", MOJO_RESULT_OK);
+    dictionary.Set("satisfiedSignals", state.satisfied_signals);
+    dictionary.Set("satisfiableSignals", state.satisfiable_signals);
+  }
+  return dictionary;
+}
+
 gin::Dictionary WaitHandle(const gin::Arguments& args,
                            mojo::Handle handle,
                            MojoHandleSignals signals,
@@ -388,6 +402,7 @@
             // TODO(mpcomplete): Should these just be methods on the JS Handle
             // object?
             .SetMethod("close", CloseHandle)
+            .SetMethod("queryHandleSignalsState", QueryHandleSignalsState)
             .SetMethod("wait", WaitHandle)
             .SetMethod("waitMany", WaitMany)
             .SetMethod("createMessagePipe", CreateMessagePipe)
diff --git a/mojo/edk/system/BUILD.gn b/mojo/edk/system/BUILD.gn
index 3c94e153..6e3505dc 100644
--- a/mojo/edk/system/BUILD.gn
+++ b/mojo/edk/system/BUILD.gn
@@ -165,6 +165,7 @@
     "platform_handle_dispatcher_unittest.cc",
     "shared_buffer_dispatcher_unittest.cc",
     "shared_buffer_unittest.cc",
+    "signals_unittest.cc",
     "wait_set_dispatcher_unittest.cc",
     "waiter_test_utils.cc",
     "waiter_test_utils.h",
diff --git a/mojo/edk/system/awakable_list.cc b/mojo/edk/system/awakable_list.cc
index 429e691..f38d580 100644
--- a/mojo/edk/system/awakable_list.cc
+++ b/mojo/edk/system/awakable_list.cc
@@ -8,7 +8,6 @@
 
 #include "base/logging.h"
 #include "mojo/edk/system/awakable.h"
-#include "mojo/edk/system/handle_signals_state.h"
 
 namespace mojo {
 namespace edk {
diff --git a/mojo/edk/system/awakable_list.h b/mojo/edk/system/awakable_list.h
index 34d6b06..d400b387 100644
--- a/mojo/edk/system/awakable_list.h
+++ b/mojo/edk/system/awakable_list.h
@@ -13,12 +13,12 @@
 #include "base/macros.h"
 #include "mojo/edk/system/system_impl_export.h"
 #include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/handle_signals_state.h"
 
 namespace mojo {
 namespace edk {
 
 class Awakable;
-struct HandleSignalsState;
 
 // |AwakableList| tracks all the |Waiter|s that are waiting on a given
 // handle/|Dispatcher|. There should be a |AwakableList| for each handle that
diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc
index cfe01fa..263f9cd 100644
--- a/mojo/edk/system/core.cc
+++ b/mojo/edk/system/core.cc
@@ -378,6 +378,17 @@
   return MOJO_RESULT_OK;
 }
 
+MojoResult Core::QueryHandleSignalsState(
+    MojoHandle handle,
+    MojoHandleSignalsState* signals_state) {
+  RequestContext request_context;
+  scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handle);
+  if (!dispatcher || !signals_state)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  *signals_state = dispatcher->GetHandleSignalsState();
+  return MOJO_RESULT_OK;
+}
+
 MojoResult Core::Wait(MojoHandle handle,
                       MojoHandleSignals signals,
                       MojoDeadline deadline,
diff --git a/mojo/edk/system/core.h b/mojo/edk/system/core.h
index 2ef9b7f..ab94951b 100644
--- a/mojo/edk/system/core.h
+++ b/mojo/edk/system/core.h
@@ -136,6 +136,8 @@
   // "mojo/public/c/system/functions.h":
   MojoTimeTicks GetTimeTicksNow();
   MojoResult Close(MojoHandle handle);
+  MojoResult QueryHandleSignalsState(MojoHandle handle,
+                                     MojoHandleSignalsState* signals_state);
   MojoResult Wait(MojoHandle handle,
                   MojoHandleSignals signals,
                   MojoDeadline deadline,
diff --git a/mojo/edk/system/data_pipe_unittest.cc b/mojo/edk/system/data_pipe_unittest.cc
index 11474962..b04bebd 100644
--- a/mojo/edk/system/data_pipe_unittest.cc
+++ b/mojo/edk/system/data_pipe_unittest.cc
@@ -162,8 +162,7 @@
   // Now wait for the other side to become readable.
   MojoHandleSignalsState state;
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &state));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &state));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
             state.satisfied_signals);
 
@@ -249,8 +248,7 @@
 
   // Wait.
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
             hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
@@ -337,19 +335,12 @@
   Create(&options);
   MojoHandleSignalsState hss;
 
-  // Never readable.
-  hss = MojoHandleSignalsState();
-  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-            MojoWait(producer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  // Never readable. Already writable.
+  hss = GetSignalsState(producer_);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             hss.satisfiable_signals);
 
-  // Already writable.
-  hss = MojoHandleSignalsState();
-  ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
-
   // Write two elements.
   int32_t elements[2] = {123, 456};
   uint32_t num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
@@ -358,8 +349,7 @@
 
   // Wait for data to become available to the consumer.
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
             hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
@@ -419,11 +409,7 @@
   // It should now be never-writable.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(producer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
-  hss = MojoHandleSignalsState();
-  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-            MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
+            WaitForSignals(producer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss));
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
 }
@@ -444,8 +430,7 @@
   // It should be signaled.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(producer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(producer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss));
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
 }
@@ -466,8 +451,7 @@
   // It should be signaled.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss));
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
 }
@@ -485,8 +469,7 @@
   // Never writable.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_WRITABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_WRITABLE, &hss));
   EXPECT_EQ(0u, hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
                 MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
@@ -500,8 +483,7 @@
   // Wait for readability.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
             hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
@@ -516,8 +498,7 @@
   // Should still be readable.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
                 MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
@@ -534,8 +515,7 @@
   // Should still be readable.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
                 MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
@@ -559,8 +539,7 @@
   // Waiting should now succeed.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
             hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
@@ -573,8 +552,7 @@
   // Should still be readable.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   EXPECT_TRUE(hss.satisfied_signals & (MOJO_HANDLE_SIGNAL_READABLE |
                                        MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
@@ -584,8 +562,7 @@
   // Wait for the peer closed signal.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE |
                 MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             hss.satisfied_signals);
@@ -605,8 +582,7 @@
   // Should be never-readable.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
 }
@@ -625,11 +601,10 @@
   EXPECT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true));
 
   // The consumer handle should appear to be readable and have new data.
-  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                                     MOJO_DEADLINE_INDEFINITE, nullptr));
   EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, nullptr));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE));
+  EXPECT_TRUE(GetSignalsState(consumer_).satisfied_signals &
+              MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE);
 
   // Now try to read a minimum of 6 elements.
   int32_t read_elements[6];
@@ -638,35 +613,30 @@
             MojoReadData(consumer_, read_elements, &num_read_bytes,
                          MOJO_READ_DATA_FLAG_ALL_OR_NONE));
 
-  // The consumer should still appear to be readable, but not with new data.
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, nullptr));
-  EXPECT_EQ(
-      MOJO_RESULT_DEADLINE_EXCEEDED,
-      MojoWait(consumer_, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, 0, nullptr));
+  // The consumer should still appear to be readable but not with new data.
+  EXPECT_TRUE(GetSignalsState(consumer_).satisfied_signals &
+              MOJO_HANDLE_SIGNAL_READABLE);
+  EXPECT_FALSE(GetSignalsState(consumer_).satisfied_signals &
+               MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE);
 
   // Write four more elements.
   EXPECT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true));
   EXPECT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true));
 
-  // The consumer handle should once again appear to be readable with new data.
-  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                                     MOJO_DEADLINE_INDEFINITE, nullptr));
+  // The consumer handle should once again appear to be readable.
   EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, nullptr));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE));
 
-  // Read should succeed this time.
+  // Try again to read a minimum of 6 elements. Should succeed this time.
   EXPECT_EQ(MOJO_RESULT_OK,
             MojoReadData(consumer_, read_elements, &num_read_bytes,
                          MOJO_READ_DATA_FLAG_ALL_OR_NONE));
 
-  // And once again the consumer is unreadable.
-  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, nullptr));
-  EXPECT_EQ(
-      MOJO_RESULT_DEADLINE_EXCEEDED,
-      MojoWait(consumer_, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, 0, nullptr));
+  // And now the consumer is unreadable.
+  EXPECT_FALSE(GetSignalsState(consumer_).satisfied_signals &
+               MOJO_HANDLE_SIGNAL_READABLE);
+  EXPECT_FALSE(GetSignalsState(consumer_).satisfied_signals &
+               MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE);
 }
 
 // Test with two-phase APIs and also closing the producer with an active
@@ -697,8 +667,7 @@
   // Wait for readability.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
             hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
@@ -719,8 +688,7 @@
   // Should still be readable.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
                 MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
@@ -743,8 +711,7 @@
   // Should be never-readable.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
 }
@@ -761,9 +728,7 @@
   MojoHandleSignalsState hss;
 
   // It should be writable.
-  hss = MojoHandleSignalsState();
-  ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
+  hss = GetSignalsState(producer_);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             hss.satisfiable_signals);
@@ -775,17 +740,13 @@
   EXPECT_GE(num_bytes, static_cast<uint32_t>(1u * sizeof(int32_t)));
 
   // At this point, it shouldn't be writable.
-  hss = MojoHandleSignalsState();
-  ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
-            MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
+  hss = GetSignalsState(producer_);
   ASSERT_EQ(0u, hss.satisfied_signals);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             hss.satisfiable_signals);
 
   // It shouldn't be readable yet either (we'll wait later).
-  hss = MojoHandleSignalsState();
-  ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  hss = GetSignalsState(consumer_);
   ASSERT_EQ(0u, hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
                 MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
@@ -795,9 +756,7 @@
   ASSERT_EQ(MOJO_RESULT_OK, EndWriteData(1u * sizeof(int32_t)));
 
   // It should immediately be writable again.
-  hss = MojoHandleSignalsState();
-  ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
+  hss = GetSignalsState(producer_);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             hss.satisfiable_signals);
@@ -805,8 +764,7 @@
   // It should become readable.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
             hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
@@ -824,8 +782,7 @@
   // It should be readable.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
             hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
@@ -843,17 +800,13 @@
   ASSERT_EQ(static_cast<uint32_t>(1u * sizeof(int32_t)), num_bytes);
 
   // At this point, it should still be writable.
-  hss = MojoHandleSignalsState();
-  ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
+  hss = GetSignalsState(producer_);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             hss.satisfiable_signals);
 
   // But not readable.
-  hss = MojoHandleSignalsState();
-  ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  hss = GetSignalsState(consumer_);
   ASSERT_EQ(0u, hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
                 MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
@@ -863,9 +816,7 @@
   ASSERT_EQ(MOJO_RESULT_OK, EndReadData(0u));
 
   // It should be readable again.
-  hss = MojoHandleSignalsState();
-  ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  hss = GetSignalsState(consumer_);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
                 MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
@@ -911,8 +862,7 @@
   // of data to become available.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
             hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE |
@@ -1002,8 +952,7 @@
   // Wait.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
@@ -1067,8 +1016,7 @@
 
   // Wait for data.
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   EXPECT_TRUE(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
                 MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
@@ -1095,8 +1043,7 @@
   while (total_num_bytes < 90) {
     // Wait to write.
     ASSERT_EQ(MOJO_RESULT_OK,
-              MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE,
-                       MOJO_DEADLINE_INDEFINITE, &hss));
+              WaitForSignals(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, &hss));
     ASSERT_EQ(hss.satisfied_signals, MOJO_HANDLE_SIGNAL_WRITABLE);
     ASSERT_EQ(hss.satisfiable_signals,
               MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED);
@@ -1231,8 +1178,7 @@
   // TODO(vtl): (See corresponding TODO in AllOrNone.)
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
             hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
@@ -1252,8 +1198,7 @@
   // Wait for producer to know that the consumer is closed.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(producer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(producer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss));
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
 
@@ -1323,8 +1268,7 @@
   // must also know about all the data that was sent.)
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
                 MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
             hss.satisfied_signals);
@@ -1384,8 +1328,7 @@
   // Wait for the data.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
             hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
@@ -1411,8 +1354,7 @@
   // must also have received the extra data).
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
                 MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
@@ -1499,8 +1441,7 @@
   // TODO(vtl): (See corresponding TODO in AllOrNone.)
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
             hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
@@ -1569,8 +1510,7 @@
   // Wait for the data.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
             hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
@@ -1594,8 +1534,8 @@
             MojoWriteMessage(pipe0, nullptr, 0, &producer_, 1,
                              MOJO_WRITE_MESSAGE_FLAG_NONE));
   producer_ = MOJO_HANDLE_INVALID;
-  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1, MOJO_HANDLE_SIGNAL_READABLE,
-                                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(pipe1, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   uint32_t num_handles = 1;
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoReadMessage(pipe1, nullptr, 0, &producer_, &num_handles,
@@ -1612,8 +1552,7 @@
   // Wait for it.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
             hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
@@ -1653,8 +1592,7 @@
   // Now wait for the other side to become readable and to see the peer closed.
   MojoHandleSignalsState state;
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                     MOJO_DEADLINE_INDEFINITE, &state));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &state));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
                 MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
             state.satisfied_signals);
@@ -1671,8 +1609,8 @@
             MojoWriteMessage(pipe0, nullptr, 0, &consumer_, 1,
                              MOJO_WRITE_MESSAGE_FLAG_NONE));
   consumer_ = MOJO_HANDLE_INVALID;
-  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1, MOJO_HANDLE_SIGNAL_READABLE,
-                                     MOJO_DEADLINE_INDEFINITE, &state));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(pipe1, MOJO_HANDLE_SIGNAL_READABLE, &state));
   uint32_t num_handles = 1;
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoReadMessage(pipe1, nullptr, 0, &consumer_, &num_handles,
@@ -1680,8 +1618,7 @@
   ASSERT_EQ(num_handles, 1u);
 
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                     MOJO_DEADLINE_INDEFINITE, &state));
+            WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &state));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
                 MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
             state.satisfied_signals);
@@ -1716,8 +1653,8 @@
     }
 
     MojoHandleSignalsState hss = MojoHandleSignalsState();
-    EXPECT_EQ(MOJO_RESULT_OK, MojoWait(producer, MOJO_HANDLE_SIGNAL_WRITABLE,
-                                       MOJO_DEADLINE_INDEFINITE, &hss));
+    EXPECT_EQ(MOJO_RESULT_OK, test::MojoTestBase::WaitForSignals(
+                                  producer, MOJO_HANDLE_SIGNAL_WRITABLE, &hss));
     EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
     EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
               hss.satisfiable_signals);
@@ -1754,8 +1691,8 @@
     }
 
     MojoHandleSignalsState hss = MojoHandleSignalsState();
-    EXPECT_EQ(MOJO_RESULT_OK, MojoWait(consumer, MOJO_HANDLE_SIGNAL_READABLE,
-                                       MOJO_DEADLINE_INDEFINITE, &hss));
+    EXPECT_EQ(MOJO_RESULT_OK, test::MojoTestBase::WaitForSignals(
+                                  consumer, MOJO_HANDLE_SIGNAL_READABLE, &hss));
     // Peer could have become closed while we're still waiting for data.
     EXPECT_TRUE(MOJO_HANDLE_SIGNAL_READABLE & hss.satisfied_signals);
     EXPECT_TRUE(hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE);
@@ -1814,8 +1751,8 @@
     // Receive the consumer from the other side.
     producer_ = MOJO_HANDLE_INVALID;
     MojoHandleSignalsState hss = MojoHandleSignalsState();
-    ASSERT_EQ(MOJO_RESULT_OK, MojoWait(server_mp, MOJO_HANDLE_SIGNAL_READABLE,
-                                       MOJO_DEADLINE_INDEFINITE, &hss));
+    ASSERT_EQ(MOJO_RESULT_OK,
+              WaitForSignals(server_mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
     MojoHandle handles[2];
     uint32_t num_handles = arraysize(handles);
     ASSERT_EQ(MOJO_RESULT_OK,
@@ -1844,8 +1781,8 @@
   // Receive the data pipe from the other side.
   MojoHandle consumer = MOJO_HANDLE_INVALID;
   MojoHandleSignalsState hss = MojoHandleSignalsState();
-  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(client_mp, MOJO_HANDLE_SIGNAL_READABLE,
-                                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(client_mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   MojoHandle handles[2];
   uint32_t num_handles = arraysize(handles);
   ASSERT_EQ(MOJO_RESULT_OK,
@@ -1880,8 +1817,8 @@
   // Receive the producer from the other side.
   MojoHandle producer = MOJO_HANDLE_INVALID;
   hss = MojoHandleSignalsState();
-  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(client_mp, MOJO_HANDLE_SIGNAL_READABLE,
-                                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(client_mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   num_handles = arraysize(handles);
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoReadMessage(client_mp, nullptr, 0, handles, &num_handles,
@@ -1921,8 +1858,7 @@
   std::string expected_message = ReadMessageWithHandles(h, &c, 1);
 
   // Wait for the consumer to become readable.
-  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(c, MOJO_HANDLE_SIGNAL_READABLE,
-                                     MOJO_DEADLINE_INDEFINITE, nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(c, MOJO_HANDLE_SIGNAL_READABLE));
 
   // Drain the consumer and expect to find the given message.
   uint32_t num_bytes = static_cast<uint32_t>(expected_message.size());
@@ -1989,8 +1925,7 @@
     std::string expected_message = ReadMessageWithHandles(child, &c, 1);
 
     // Wait for the consumer to become readable.
-    EXPECT_EQ(MOJO_RESULT_OK, MojoWait(c, MOJO_HANDLE_SIGNAL_READABLE,
-                                       MOJO_DEADLINE_INDEFINITE, nullptr));
+    EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(c, MOJO_HANDLE_SIGNAL_READABLE));
 
     // Drain the consumer and expect to find the given message.
     uint32_t num_bytes = static_cast<uint32_t>(expected_message.size());
@@ -2017,15 +1952,13 @@
   MojoHandle* producers = &handles[0];
   MojoHandle* consumers = &handles[3];
 
-  // Wait on producer 0 using MojoWait.
+  // Wait on producer 0
   EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWait(producers[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                     MOJO_DEADLINE_INDEFINITE, nullptr));
+            WaitForSignals(producers[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED));
 
-  // Wait on consumer 0 using MojoWait.
+  // Wait on consumer 0
   EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumers[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                     MOJO_DEADLINE_INDEFINITE, nullptr));
+            WaitForSignals(consumers[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED));
 
   base::MessageLoop message_loop;
 
diff --git a/mojo/edk/system/handle_signals_state.h b/mojo/edk/system/handle_signals_state.h
index 1c47a28..f241278 100644
--- a/mojo/edk/system/handle_signals_state.h
+++ b/mojo/edk/system/handle_signals_state.h
@@ -5,45 +5,9 @@
 #ifndef MOJO_EDK_SYSTEM_HANDLE_SIGNALS_STATE_H_
 #define MOJO_EDK_SYSTEM_HANDLE_SIGNALS_STATE_H_
 
-#include "mojo/edk/system/system_impl_export.h"
-#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/handle_signals_state.h"
 
-namespace mojo {
-namespace edk {
-
-// Just "add" some constructors and methods to the C struct
-// |MojoHandleSignalsState| (for convenience). This should add no overhead.
-struct MOJO_SYSTEM_IMPL_EXPORT HandleSignalsState final
-    : public MojoHandleSignalsState {
-  HandleSignalsState() {
-    satisfied_signals = MOJO_HANDLE_SIGNAL_NONE;
-    satisfiable_signals = MOJO_HANDLE_SIGNAL_NONE;
-  }
-  HandleSignalsState(MojoHandleSignals satisfied,
-                     MojoHandleSignals satisfiable) {
-    satisfied_signals = satisfied;
-    satisfiable_signals = satisfiable;
-  }
-
-  bool equals(const HandleSignalsState& other) const {
-    return satisfied_signals == other.satisfied_signals &&
-           satisfiable_signals == other.satisfiable_signals;
-  }
-
-  bool satisfies(MojoHandleSignals signals) const {
-    return !!(satisfied_signals & signals);
-  }
-
-  bool can_satisfy(MojoHandleSignals signals) const {
-    return !!(satisfiable_signals & signals);
-  }
-
-  // (Copy and assignment allowed.)
-};
-static_assert(sizeof(HandleSignalsState) == sizeof(MojoHandleSignalsState),
-              "HandleSignalsState should add no overhead");
-
-}  // namespace edk
-}  // namespace mojo
+// TODO(rockot): Remove this header and use the C++ system library type
+// directly inside the EDK.
 
 #endif  // MOJO_EDK_SYSTEM_HANDLE_SIGNALS_STATE_H_
diff --git a/mojo/edk/system/message_pipe_perftest.cc b/mojo/edk/system/message_pipe_perftest.cc
index a6ce3709..9866c47 100644
--- a/mojo/edk/system/message_pipe_perftest.cc
+++ b/mojo/edk/system/message_pipe_perftest.cc
@@ -47,8 +47,7 @@
                               0, MOJO_WRITE_MESSAGE_FLAG_NONE),
              MOJO_RESULT_OK);
     HandleSignalsState hss;
-    CHECK_EQ(MojoWait(mp, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE,
-                      &hss),
+    CHECK_EQ(WaitForSignals(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss),
              MOJO_RESULT_OK);
     uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer_.size());
     CHECK_EQ(MojoReadMessage(mp, &read_buffer_[0], &read_buffer_size, nullptr,
@@ -98,9 +97,7 @@
     while (true) {
       // Wait for our end of the message pipe to be readable.
       HandleSignalsState hss;
-      MojoResult result =
-          MojoWait(mp, MOJO_HANDLE_SIGNAL_READABLE,
-                   MOJO_DEADLINE_INDEFINITE, &hss);
+      MojoResult result = WaitForSignals(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss);
       if (result != MOJO_RESULT_OK) {
         rv = result;
         break;
diff --git a/mojo/edk/system/message_pipe_unittest.cc b/mojo/edk/system/message_pipe_unittest.cc
index 8f48950..e6f1ff64 100644
--- a/mojo/edk/system/message_pipe_unittest.cc
+++ b/mojo/edk/system/message_pipe_unittest.cc
@@ -100,8 +100,8 @@
   ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0])));
 
   MojoHandleSignalsState state;
-  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE,
-                                     MOJO_DEADLINE_INDEFINITE, &state));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state));
 
   // Read from port 0.
   buffer[0] = 123;
@@ -124,8 +124,8 @@
   buffer[1] = 0;
   ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe0_, buffer, sizeof(buffer[0])));
 
-  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_READABLE,
-                                     MOJO_DEADLINE_INDEFINITE, &state));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, &state));
 
   // Read from port 1 with buffer size 0 (should get the size of next message).
   // Also test that giving a null buffer is okay when the buffer size is 0.
@@ -154,8 +154,8 @@
   ASSERT_EQ(123456789, buffer[0]);
   ASSERT_EQ(456, buffer[1]);
 
-  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_READABLE,
-                                     MOJO_DEADLINE_INDEFINITE, &state));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, &state));
 
   // Read again from port 1.
   buffer[0] = 123;
@@ -179,8 +179,8 @@
   MojoClose(pipe0_);
   pipe0_ = MOJO_HANDLE_INVALID;
 
-  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                                     MOJO_DEADLINE_INDEFINITE, &state));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &state));
 
   // Try to write from port 1 (to port 0).
   buffer[0] = 456789012;
@@ -215,8 +215,8 @@
   }
 
   MojoHandleSignalsState state;
-  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE,
-                                     MOJO_DEADLINE_INDEFINITE, &state));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state));
 
   // Port 0 shouldn't be empty.
   buffer_size = 0;
@@ -241,8 +241,8 @@
   ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0])));
 
   MojoHandleSignalsState state;
-  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE,
-                                     MOJO_DEADLINE_INDEFINITE, &state));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state));
 
   // Read/discard from port 0 (no buffer); get size.
   buffer_size = 0;
@@ -261,8 +261,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             WriteMessage(pipe1_, buffer, sizeof(buffer[0])));
 
-  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE,
-                                     MOJO_DEADLINE_INDEFINITE, &state));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state));
 
   // Read from port 0 (buffer big enough).
   buffer[0] = 123;
@@ -283,8 +283,8 @@
   buffer[1] = 0;
   ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0])));
 
-  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE,
-                                     MOJO_DEADLINE_INDEFINITE, &state));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state));
 
   // Read/discard from port 0 (buffer too small); get size.
   buffer_size = 1;
@@ -302,8 +302,8 @@
   buffer[1] = 0;
   ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0])));
 
-  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE,
-                                     MOJO_DEADLINE_INDEFINITE, &state));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state));
 
   // Discard from port 0.
   buffer_size = 1;
@@ -323,41 +323,27 @@
   const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
   uint32_t buffer_size;
 
-  // Always writable (until the other port is closed).
-  hss = MojoHandleSignalsState();
-  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_WRITABLE, 0,
-                                     &hss));
+  // Always writable (until the other port is closed). Not yet readable. Peer
+  // not closed.
+  hss = GetSignalsState(pipe0_);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
   ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
   hss = MojoHandleSignalsState();
 
-  // Not yet readable.
-  ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
-            MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
-  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
-  ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
-
-  // The peer is not closed.
-  hss = MojoHandleSignalsState();
-  ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
-            MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, 0, &hss));
-  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
-  ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
-
   // Write from port 0 (to port 1), to make port 1 readable.
   buffer[0] = 123456789;
   ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe0_, buffer, kBufferSize));
 
   // Port 1 should already be readable now.
-  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_READABLE,
-                                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
             hss.satisfied_signals);
   ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
   // ... and still writable.
   hss = MojoHandleSignalsState();
-  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_WRITABLE,
-                                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_WRITABLE, &hss));
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
             hss.satisfied_signals);
   ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
@@ -368,8 +354,8 @@
 
   // Port 1 should be signaled with peer closed.
   hss = MojoHandleSignalsState();
-  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss));
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             hss.satisfied_signals);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
@@ -379,8 +365,7 @@
   hss = MojoHandleSignalsState();
 
   ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-            MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_WRITABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_WRITABLE, &hss));
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             hss.satisfied_signals);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
@@ -388,8 +373,8 @@
 
   // But it should still be readable.
   hss = MojoHandleSignalsState();
-  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_READABLE,
-                                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             hss.satisfied_signals);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
@@ -404,8 +389,7 @@
   // Now port 1 should no longer be readable.
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-            MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, &hss));
+            WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, &hss));
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
 }
@@ -453,9 +437,7 @@
   EXPECT_EQ(MOJO_RESULT_OK,
             MojoWriteMessageNew(a, message, MOJO_WRITE_MESSAGE_FLAG_NONE));
 
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWait(b, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE,
-                     nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE));
   uint32_t num_bytes = 0;
   uint32_t num_handles = 0;
   EXPECT_EQ(MOJO_RESULT_OK,
@@ -489,8 +471,7 @@
     WriteMessageWithHandles(h, "", handles, kPingPongHandlesPerIteration);
   }
 
-  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
-                                     MOJO_DEADLINE_INDEFINITE, nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE));
   char msg[4];
   uint32_t num_bytes = 4;
   EXPECT_EQ(MOJO_RESULT_OK, ReadMessage(h, msg, &num_bytes));
@@ -675,9 +656,7 @@
   EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(b));
   EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c));
 
-  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(d, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                                     MOJO_DEADLINE_INDEFINITE, nullptr));
-
+  EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(d, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
   EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d));
 }
 
@@ -700,9 +679,7 @@
   EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c));
 
   EXPECT_EQ(kTestMessage, ReadMessage(d));
-  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(d, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                                     MOJO_DEADLINE_INDEFINITE, nullptr));
-
+  EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(d, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
   EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d));
 }
 
diff --git a/mojo/edk/system/multiprocess_message_pipe_unittest.cc b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
index f8e29d63..3b521ccf 100644
--- a/mojo/edk/system/multiprocess_message_pipe_unittest.cc
+++ b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
@@ -91,9 +91,7 @@
   for (;; rv = (rv + 1) % 100) {
     // Wait for our end of the message pipe to be readable.
     HandleSignalsState hss;
-    MojoResult result =
-        MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
-                 MOJO_DEADLINE_INDEFINITE, &hss);
+    MojoResult result = WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss);
     if (result != MOJO_RESULT_OK) {
       // It was closed, probably.
       CHECK_EQ(result, MOJO_RESULT_FAILED_PRECONDITION);
@@ -139,8 +137,7 @@
 
     HandleSignalsState hss;
     ASSERT_EQ(MOJO_RESULT_OK,
-              MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
-                       MOJO_DEADLINE_INDEFINITE, &hss));
+              WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss));
     // The child may or may not have closed its end of the message pipe and died
     // (and we may or may not know it yet), so our end may or may not appear as
     // writable.
@@ -179,8 +176,7 @@
     for (size_t i = 0; i < kNumMessages; i++) {
       HandleSignalsState hss;
       ASSERT_EQ(MOJO_RESULT_OK,
-                MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
-                         MOJO_DEADLINE_INDEFINITE, &hss));
+                WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss));
       // The child may or may not have closed its end of the message pipe and
       // died (and we may or may not know it yet), so our end may or may not
       // appear as writable.
@@ -208,8 +204,7 @@
     // "quitquitquit").
     HandleSignalsState hss;
     ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-              MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
-                       MOJO_DEADLINE_INDEFINITE, &hss));
+              WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss));
     ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
     ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
   END_CHILD_AND_EXPECT_EXIT_CODE(static_cast<int>(kNumMessages % 100));
@@ -219,8 +214,7 @@
                              h) {
   // Wait for the first message from our parent.
   HandleSignalsState hss;
-  CHECK_EQ(MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
-               MOJO_DEADLINE_INDEFINITE, &hss),
+  CHECK_EQ(WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss),
            MOJO_RESULT_OK);
   // In this test, the parent definitely doesn't close its end of the message
   // pipe before we do.
@@ -265,8 +259,7 @@
 
   // Now wait for our parent to send us a message.
   hss = HandleSignalsState();
-  CHECK_EQ(MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
-                    MOJO_DEADLINE_INDEFINITE, &hss),
+  CHECK_EQ(WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss),
            MOJO_RESULT_OK);
   CHECK_EQ(hss.satisfied_signals,
            MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
@@ -322,8 +315,7 @@
     // Wait for a message from the child.
     HandleSignalsState hss;
     ASSERT_EQ(MOJO_RESULT_OK,
-              MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
-                       MOJO_DEADLINE_INDEFINITE, &hss));
+              WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss));
     EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE));
     EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE));
 
@@ -359,8 +351,7 @@
     // Wait for |h| to become readable, which should fail.
     hss = HandleSignalsState();
     ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-              MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
-                       MOJO_DEADLINE_INDEFINITE, &hss));
+              WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss));
     ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
     ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
   END_CHILD()
@@ -369,8 +360,7 @@
 DEFINE_TEST_CLIENT_WITH_PIPE(CheckPlatformHandleFile,
                              MultiprocessMessagePipeTest, h) {
   HandleSignalsState hss;
-  CHECK_EQ(MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
-                    MOJO_DEADLINE_INDEFINITE, &hss),
+  CHECK_EQ(WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss),
            MOJO_RESULT_OK);
   CHECK_EQ(hss.satisfied_signals,
            MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
@@ -455,8 +445,7 @@
     // Wait for it to become readable, which should fail.
     HandleSignalsState hss;
     ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-              MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
-                       MOJO_DEADLINE_INDEFINITE, &hss));
+              WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss));
     ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
     ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
   END_CHILD()
@@ -474,8 +463,7 @@
 DEFINE_TEST_CLIENT_WITH_PIPE(CheckMessagePipe, MultiprocessMessagePipeTest, h) {
   // Wait for the first message from our parent.
   HandleSignalsState hss;
-  CHECK_EQ(MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
-                    MOJO_DEADLINE_INDEFINITE, &hss),
+  CHECK_EQ(WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss),
            MOJO_RESULT_OK);
   // In this test, the parent definitely doesn't close its end of the message
   // pipe before we do.
@@ -495,8 +483,7 @@
   CHECK_EQ(num_handlers, 1u);
 
   // Read data from the received message pipe.
-  CHECK_EQ(MojoWait(handles[0], MOJO_HANDLE_SIGNAL_READABLE,
-               MOJO_DEADLINE_INDEFINITE, &hss),
+  CHECK_EQ(WaitForSignals(handles[0], MOJO_HANDLE_SIGNAL_READABLE, &hss),
            MOJO_RESULT_OK);
   CHECK_EQ(hss.satisfied_signals,
            MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
@@ -547,8 +534,7 @@
     // Wait for a message from the child.
     HandleSignalsState hss;
     ASSERT_EQ(MOJO_RESULT_OK,
-              MojoWait(mp1, MOJO_HANDLE_SIGNAL_READABLE,
-                       MOJO_DEADLINE_INDEFINITE, &hss));
+              WaitForSignals(mp1, MOJO_HANDLE_SIGNAL_READABLE, &hss));
     EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE));
     EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE));
 
@@ -585,8 +571,7 @@
     // Wait for a message from the child.
     HandleSignalsState hss;
     ASSERT_EQ(MOJO_RESULT_OK,
-              MojoWait(mp1, MOJO_HANDLE_SIGNAL_READABLE,
-                       MOJO_DEADLINE_INDEFINITE, &hss));
+              WaitForSignals(mp1, MOJO_HANDLE_SIGNAL_READABLE, &hss));
     EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE));
     EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE));
 
@@ -604,8 +589,7 @@
 DEFINE_TEST_CLIENT_WITH_PIPE(DataPipeConsumer, MultiprocessMessagePipeTest, h) {
   // Wait for the first message from our parent.
   HandleSignalsState hss;
-  CHECK_EQ(MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
-                    MOJO_DEADLINE_INDEFINITE, &hss),
+  CHECK_EQ(WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss),
            MOJO_RESULT_OK);
   // In this test, the parent definitely doesn't close its end of the message
   // pipe before we do.
@@ -625,8 +609,7 @@
   CHECK_EQ(num_handlers, 1u);
 
   // Read data from the received message pipe.
-  CHECK_EQ(MojoWait(handles[0], MOJO_HANDLE_SIGNAL_READABLE,
-               MOJO_DEADLINE_INDEFINITE, &hss),
+  CHECK_EQ(WaitForSignals(handles[0], MOJO_HANDLE_SIGNAL_READABLE, &hss),
            MOJO_RESULT_OK);
   CHECK_EQ(hss.satisfied_signals,
            MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
@@ -677,8 +660,7 @@
     // Wait for a message from the child.
     HandleSignalsState hss;
     ASSERT_EQ(MOJO_RESULT_OK,
-              MojoWait(mp1, MOJO_HANDLE_SIGNAL_READABLE,
-                       MOJO_DEADLINE_INDEFINITE, &hss));
+              WaitForSignals(mp1, MOJO_HANDLE_SIGNAL_READABLE, &hss));
     EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE));
     EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE));
 
@@ -924,9 +906,7 @@
   EXPECT_EQ("bye", ReadMessage(p0));
 
   // We should still be able to observe peer closure from the other end.
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWait(p0, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                     MOJO_DEADLINE_INDEFINITE, nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(p0, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
 }
 
 // Parses commands from the parent pipe and does whatever it's asked to do.
@@ -1110,10 +1090,7 @@
                                   MultiprocessMessagePipeTest, h) {
   MojoHandle p;
   EXPECT_EQ("foo", ReadMessageWithHandles(h, &p, 1));
-
-  auto result = MojoWait(p, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                         MOJO_DEADLINE_INDEFINITE, nullptr);
-  EXPECT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(p, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
 }
 
 TEST_P(MultiprocessMessagePipeTestWithPeerSupport, SendPipeThenClosePeer) {
@@ -1159,9 +1136,8 @@
             ReadMessageWithHandles(application_client, &service_client, 1));
 
   // Wait for the service client to signal closure.
-  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(service_client,
-                                     MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                                     MOJO_DEADLINE_INDEFINITE, nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(service_client, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
 
   EXPECT_EQ(MOJO_RESULT_OK, MojoClose(service_client));
   EXPECT_EQ(MOJO_RESULT_OK, MojoClose(application_client));
@@ -1207,8 +1183,7 @@
   EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
 
   // We should be able to detect peer closure on |a|.
-  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(a, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                                     MOJO_DEADLINE_INDEFINITE, nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(a, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
 }
 
 DEFINE_TEST_CLIENT_TEST_WITH_PIPE(WriteCloseSendPeerClient,
@@ -1251,8 +1226,8 @@
     EXPECT_EQ("qux", ReadMessage(p));
 
     // Expect to have peer closure signaled.
-    EXPECT_EQ(MOJO_RESULT_OK, MojoWait(p, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                                       MOJO_DEADLINE_INDEFINITE, nullptr));
+    EXPECT_EQ(MOJO_RESULT_OK,
+              WaitForSignals(p, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
 
     WriteMessage(h, "quit");
   END_CHILD()
@@ -1265,9 +1240,8 @@
   MojoHandle handles[4];
   EXPECT_EQ("o_O", ReadMessageWithHandles(parent, handles, 4));
 
-  // Wait on handle 0 using MojoWait.
-  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(handles[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                                     MOJO_DEADLINE_INDEFINITE, nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(handles[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED));
 
   base::MessageLoop message_loop;
 
@@ -1349,8 +1323,7 @@
       WriteMessageWithHandles(child2, "hi", &d, 1);
 
       // Read a message from the pipe we sent to child1 and flag it as bad.
-      ASSERT_EQ(MOJO_RESULT_OK, MojoWait(a, MOJO_HANDLE_SIGNAL_READABLE,
-                                         MOJO_DEADLINE_INDEFINITE, nullptr));
+      ASSERT_EQ(MOJO_RESULT_OK, WaitForSignals(a, MOJO_HANDLE_SIGNAL_READABLE));
       uint32_t num_bytes = 0;
       MojoMessageHandle message;
       ASSERT_EQ(MOJO_RESULT_OK,
@@ -1362,8 +1335,7 @@
       EXPECT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message));
 
       // Read a message from the pipe we sent to child2 and flag it as bad.
-      ASSERT_EQ(MOJO_RESULT_OK, MojoWait(c, MOJO_HANDLE_SIGNAL_READABLE,
-                                         MOJO_DEADLINE_INDEFINITE, nullptr));
+      ASSERT_EQ(MOJO_RESULT_OK, WaitForSignals(c, MOJO_HANDLE_SIGNAL_READABLE));
       ASSERT_EQ(MOJO_RESULT_OK,
                 MojoReadMessageNew(c, &message, &num_bytes, nullptr, 0,
                                    MOJO_READ_MESSAGE_FLAG_NONE));
diff --git a/mojo/edk/system/signals_unittest.cc b/mojo/edk/system/signals_unittest.cc
new file mode 100644
index 0000000..e8b0cd1
--- /dev/null
+++ b/mojo/edk/system/signals_unittest.cc
@@ -0,0 +1,76 @@
+// 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 "mojo/edk/test/mojo_test_base.h"
+#include "mojo/public/c/system/buffer.h"
+#include "mojo/public/c/system/data_pipe.h"
+#include "mojo/public/c/system/functions.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace edk {
+namespace {
+
+using SignalsTest = test::MojoTestBase;
+
+TEST_F(SignalsTest, QueryInvalidArguments) {
+  MojoHandleSignalsState state = {0, 0};
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoQueryHandleSignalsState(MOJO_HANDLE_INVALID, &state));
+
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoQueryHandleSignalsState(a, nullptr));
+}
+
+TEST_F(SignalsTest, QueryMessagePipeSignals) {
+  MojoHandleSignalsState state = {0, 0};
+
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(a, &state));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+                MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            state.satisfiable_signals);
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &state));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+                MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            state.satisfiable_signals);
+
+  WriteMessage(a, "ok");
+  EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &state));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            state.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+                MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            state.satisfiable_signals);
+
+  EXPECT_EQ("ok", ReadMessage(b));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &state));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+                MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            state.satisfiable_signals);
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+
+  EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &state));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals);
+}
+
+}  // namespace
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/test/mojo_test_base.cc b/mojo/edk/test/mojo_test_base.cc
index f1032d7e..d7d3b2a0 100644
--- a/mojo/edk/test/mojo_test_base.cc
+++ b/mojo/edk/test/mojo_test_base.cc
@@ -5,13 +5,16 @@
 #include "mojo/edk/test/mojo_test_base.h"
 
 #include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/synchronization/waitable_event.h"
 #include "mojo/edk/embedder/embedder.h"
 #include "mojo/edk/system/handle_signals_state.h"
 #include "mojo/public/c/system/buffer.h"
 #include "mojo/public/c/system/data_pipe.h"
 #include "mojo/public/c/system/functions.h"
+#include "mojo/public/c/system/watcher.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if defined(OS_MACOSX) && !defined(OS_IOS)
@@ -22,6 +25,103 @@
 namespace edk {
 namespace test {
 
+namespace {
+
+class Waiter {
+ public:
+  Waiter() {}
+  ~Waiter() {}
+
+  MojoResult Wait(MojoHandle handle,
+                  MojoHandleSignals signals,
+                  MojoHandleSignalsState* state) {
+    MojoHandle watcher;
+    MojoCreateWatcher(&Context::OnNotification, &watcher);
+
+    context_ = new Context();
+
+    // Balanced by OnNotification in the |MOJO_RESULT_CANCELLED| case.
+    context_->AddRef();
+
+    MojoResult rv = MojoWatch(watcher, handle, signals,
+                              reinterpret_cast<uintptr_t>(context_.get()));
+    DCHECK_EQ(MOJO_RESULT_OK, rv);
+
+    uint32_t num_ready_contexts = 1;
+    uintptr_t ready_context;
+    MojoResult ready_result;
+    MojoHandleSignalsState ready_state;
+    rv = MojoArmWatcher(watcher, &num_ready_contexts, &ready_context,
+                        &ready_result, &ready_state);
+    if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
+      MojoClose(watcher);
+      DCHECK_EQ(1u, num_ready_contexts);
+      if (state)
+        *state = ready_state;
+      return ready_result;
+    }
+
+    // Wait for the first notification.
+    context_->event().Wait();
+
+    ready_result = context_->wait_result();
+    DCHECK_NE(MOJO_RESULT_UNKNOWN, ready_result);
+
+    if (state)
+      *state = context_->wait_state();
+
+    MojoClose(watcher);
+
+    return ready_result;
+  }
+
+ private:
+  class Context : public base::RefCountedThreadSafe<Context> {
+   public:
+    Context()
+        : event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                 base::WaitableEvent::InitialState::NOT_SIGNALED) {}
+
+    base::WaitableEvent& event() { return event_; }
+    MojoResult wait_result() const { return wait_result_; }
+    MojoHandleSignalsState wait_state() const { return wait_state_; }
+
+    static void OnNotification(uintptr_t context_value,
+                               MojoResult result,
+                               MojoHandleSignalsState state,
+                               MojoWatcherNotificationFlags flags) {
+      auto* context = reinterpret_cast<Context*>(context_value);
+      context->Notify(result, state);
+      if (result == MOJO_RESULT_CANCELLED)
+        context->Release();
+    }
+
+   private:
+    friend class base::RefCountedThreadSafe<Context>;
+
+    ~Context() {}
+
+    void Notify(MojoResult result, MojoHandleSignalsState state) {
+      if (wait_result_ == MOJO_RESULT_UNKNOWN) {
+        wait_result_ = result;
+        wait_state_ = state;
+      }
+      event_.Signal();
+    }
+
+    base::WaitableEvent event_;
+    MojoResult wait_result_ = MOJO_RESULT_UNKNOWN;
+    MojoHandleSignalsState wait_state_ = {0, 0};
+
+    DISALLOW_COPY_AND_ASSIGN(Context);
+  };
+
+  scoped_refptr<Context> context_;
+
+  DISALLOW_COPY_AND_ASSIGN(Waiter);
+};
+
+}  // namespace
 
 #if defined(OS_MACOSX) && !defined(OS_IOS)
 namespace {
@@ -130,9 +230,7 @@
     MojoHandle mp,
     MojoHandle* handles,
     uint32_t expected_num_handles) {
-  CHECK_EQ(MojoWait(mp, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE,
-                    nullptr),
-           MOJO_RESULT_OK);
+  CHECK_EQ(WaitForSignals(mp, MOJO_HANDLE_SIGNAL_READABLE), MOJO_RESULT_OK);
 
   uint32_t message_size = 0;
   uint32_t num_handles = 0;
@@ -154,9 +252,7 @@
 // static
 std::string MojoTestBase::ReadMessageWithOptionalHandle(MojoHandle mp,
                                                         MojoHandle* handle) {
-  CHECK_EQ(MojoWait(mp, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE,
-                    nullptr),
-           MOJO_RESULT_OK);
+  CHECK_EQ(WaitForSignals(mp, MOJO_HANDLE_SIGNAL_READABLE), MOJO_RESULT_OK);
 
   uint32_t message_size = 0;
   uint32_t num_handles = 0;
@@ -191,9 +287,7 @@
 void MojoTestBase::ReadMessage(MojoHandle mp,
                                char* data,
                                size_t num_bytes) {
-  CHECK_EQ(MojoWait(mp, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE,
-                    nullptr),
-           MOJO_RESULT_OK);
+  CHECK_EQ(WaitForSignals(mp, MOJO_HANDLE_SIGNAL_READABLE), MOJO_RESULT_OK);
 
   uint32_t message_size = 0;
   uint32_t num_handles = 0;
@@ -288,8 +382,7 @@
 
 // static
 void MojoTestBase::WriteData(MojoHandle producer, const std::string& data) {
-  CHECK_EQ(MojoWait(producer, MOJO_HANDLE_SIGNAL_WRITABLE,
-                    MOJO_DEADLINE_INDEFINITE, nullptr),
+  CHECK_EQ(WaitForSignals(producer, MOJO_HANDLE_SIGNAL_WRITABLE),
            MOJO_RESULT_OK);
   uint32_t num_bytes = static_cast<uint32_t>(data.size());
   CHECK_EQ(MojoWriteData(producer, data.data(), &num_bytes,
@@ -300,8 +393,7 @@
 
 // static
 std::string MojoTestBase::ReadData(MojoHandle consumer, size_t size) {
-  CHECK_EQ(MojoWait(consumer, MOJO_HANDLE_SIGNAL_READABLE,
-                    MOJO_DEADLINE_INDEFINITE, nullptr),
+  CHECK_EQ(WaitForSignals(consumer, MOJO_HANDLE_SIGNAL_READABLE),
            MOJO_RESULT_OK);
   std::vector<char> buffer(size);
   uint32_t num_bytes = static_cast<uint32_t>(size);
@@ -313,6 +405,21 @@
   return std::string(buffer.data(), buffer.size());
 }
 
+// static
+MojoHandleSignalsState MojoTestBase::GetSignalsState(MojoHandle handle) {
+  MojoHandleSignalsState signals_state;
+  CHECK_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(handle, &signals_state));
+  return signals_state;
+}
+
+// static
+MojoResult MojoTestBase::WaitForSignals(MojoHandle handle,
+                                        MojoHandleSignals signals,
+                                        MojoHandleSignalsState* state) {
+  Waiter waiter;
+  return waiter.Wait(handle, signals, state);
+}
+
 }  // namespace test
 }  // namespace edk
 }  // namespace mojo
diff --git a/mojo/edk/test/mojo_test_base.h b/mojo/edk/test/mojo_test_base.h
index fa5b64ce..35e2c2b1 100644
--- a/mojo/edk/test/mojo_test_base.h
+++ b/mojo/edk/test/mojo_test_base.h
@@ -30,8 +30,6 @@
   ~MojoTestBase() override;
 
   using LaunchType = MultiprocessTestHelper::LaunchType;
-
- protected:
   using HandlerCallback = base::Callback<void(ScopedMessagePipeHandle)>;
 
   class ClientController {
@@ -152,6 +150,14 @@
   // Reads data from a data pipe.
   static std::string ReadData(MojoHandle consumer, size_t size);
 
+  // Queries the signals state of |handle|.
+  static MojoHandleSignalsState GetSignalsState(MojoHandle handle);
+
+  // Helper to block the calling thread waiting for signals to be raised.
+  static MojoResult WaitForSignals(MojoHandle handle,
+                                   MojoHandleSignals signals,
+                                   MojoHandleSignalsState* state = nullptr);
+
   void set_launch_type(LaunchType launch_type) { launch_type_ = launch_type; }
 
  private:
diff --git a/mojo/public/c/system/functions.h b/mojo/public/c/system/functions.h
index 750a29ee..4dad70b 100644
--- a/mojo/public/c/system/functions.h
+++ b/mojo/public/c/system/functions.h
@@ -46,6 +46,21 @@
 // fail with |MOJO_RESULT_INVALID_ARGUMENT| if they happen after.
 MOJO_SYSTEM_EXPORT MojoResult MojoClose(MojoHandle handle);
 
+// Queries the last known signals state of a handle.
+//
+// Note that no guarantees can be made about the accuracy of the returned
+// signals state by the time this returns, as other threads in the system may
+// change the handle's state at any time. Use with appropriate discretion.
+//
+// Returns:
+//   |MOJO_RESULT_OK| on success. |*signals_state| is populated with the
+//       last known signals state of |handle|.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |handle| is not a valid handle or
+//       |signals_state| is null.
+MOJO_SYSTEM_EXPORT MojoResult
+MojoQueryHandleSignalsState(MojoHandle handle,
+                            struct MojoHandleSignalsState* signals_state);
+
 // Waits on the given handle until one of the following happens:
 //   - A signal indicated by |signals| is satisfied.
 //   - It becomes known that no signal indicated by |signals| will ever be
diff --git a/mojo/public/c/system/tests/core_unittest.cc b/mojo/public/c/system/tests/core_unittest.cc
index 8a54380..ada99d4b 100644
--- a/mojo/public/c/system/tests/core_unittest.cc
+++ b/mojo/public/c/system/tests/core_unittest.cc
@@ -41,9 +41,9 @@
   EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(MOJO_HANDLE_INVALID));
 
   // Wait:
-  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
-            MojoWait(MOJO_HANDLE_INVALID, ~MOJO_HANDLE_SIGNAL_NONE, 1000000,
-                     nullptr));
+  EXPECT_EQ(
+      MOJO_RESULT_INVALID_ARGUMENT,
+      MojoWait(MOJO_HANDLE_INVALID, ~MOJO_HANDLE_SIGNAL_NONE, 0, nullptr));
 
   h0 = MOJO_HANDLE_INVALID;
   sig = ~MOJO_HANDLE_SIGNAL_NONE;
@@ -98,16 +98,9 @@
   EXPECT_NE(h0, MOJO_HANDLE_INVALID);
   EXPECT_NE(h1, MOJO_HANDLE_INVALID);
 
-  // Shouldn't be readable, we haven't written anything.
+  // Shouldn't be readable, we haven't written anything. Should be writable.
   MojoHandleSignalsState state;
-  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
-            MojoWait(h0, MOJO_HANDLE_SIGNAL_READABLE, 0, &state));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
-  EXPECT_EQ(kSignalAll, state.satisfiable_signals);
-
-  // Should be writable.
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWait(h0, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &state));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(h0, &state));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
   EXPECT_EQ(kSignalAll, state.satisfiable_signals);
 
@@ -147,9 +140,7 @@
   EXPECT_STREQ(kHello, buffer);
 
   // |h0| should no longer be readable.
-  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
-            MojoWait(h0, MOJO_HANDLE_SIGNAL_READABLE, 10, &state));
-
+  EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(h0, &state));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
   EXPECT_EQ(kSignalAll, state.satisfiable_signals);
 
@@ -164,7 +155,7 @@
   EXPECT_EQ(
       MOJO_RESULT_FAILED_PRECONDITION,
       MojoWait(h1, MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
-               1000, &state));
+               MOJO_DEADLINE_INDEFINITE, &state));
 
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals);
@@ -188,18 +179,14 @@
 
   // The consumer |hc| shouldn't be readable.
   MojoHandleSignalsState state;
-  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
-            MojoWait(hc, MOJO_HANDLE_SIGNAL_READABLE, 0, &state));
-
+  EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(hc, &state));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_NONE, state.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
                 MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
             state.satisfiable_signals);
 
   // The producer |hp| should be writable.
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWait(hp, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &state));
-
+  EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(hp, &state));
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             state.satisfiable_signals);
@@ -276,7 +263,8 @@
 
   // |hc| should no longer be readable.
   EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-            MojoWait(hc, MOJO_HANDLE_SIGNAL_READABLE, 1000, &state));
+            MojoWait(hc, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE,
+                     &state));
 
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals);
diff --git a/mojo/public/c/system/tests/core_unittest_pure_c.c b/mojo/public/c/system/tests/core_unittest_pure_c.c
index fa3caa5..890bcfac 100644
--- a/mojo/public/c/system/tests/core_unittest_pure_c.c
+++ b/mojo/public/c/system/tests/core_unittest_pure_c.c
@@ -54,6 +54,9 @@
   EXPECT_NE(MOJO_RESULT_OK, MojoClose(handle0));
 
   EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoQueryHandleSignalsState(handle0, NULL));
+
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
             MojoWait(handle0, ~MOJO_HANDLE_SIGNAL_NONE,
                      MOJO_DEADLINE_INDEFINITE, NULL));
 
diff --git a/mojo/public/c/system/thunks.cc b/mojo/public/c/system/thunks.cc
index 1e92954..877ab8f 100644
--- a/mojo/public/c/system/thunks.cc
+++ b/mojo/public/c/system/thunks.cc
@@ -22,6 +22,13 @@
   return g_thunks.Close(handle);
 }
 
+MojoResult MojoQueryHandleSignalsState(
+    MojoHandle handle,
+    struct MojoHandleSignalsState* signals_state) {
+  assert(g_thunks.QueryHandleSignalsState);
+  return g_thunks.QueryHandleSignalsState(handle, signals_state);
+}
+
 MojoResult MojoWait(MojoHandle handle,
                     MojoHandleSignals signals,
                     MojoDeadline deadline,
diff --git a/mojo/public/c/system/thunks.h b/mojo/public/c/system/thunks.h
index 31d2289..70417b8 100644
--- a/mojo/public/c/system/thunks.h
+++ b/mojo/public/c/system/thunks.h
@@ -22,6 +22,9 @@
   size_t size;  // Should be set to sizeof(MojoSystemThunks).
   MojoTimeTicks (*GetTimeTicksNow)();
   MojoResult (*Close)(MojoHandle handle);
+  MojoResult (*QueryHandleSignalsState)(
+      MojoHandle handle,
+      struct MojoHandleSignalsState* signals_state);
   MojoResult (*Wait)(MojoHandle handle,
                      MojoHandleSignals signals,
                      MojoDeadline deadline,
diff --git a/mojo/public/cpp/system/BUILD.gn b/mojo/public/cpp/system/BUILD.gn
index 2fc9075..81654d0 100644
--- a/mojo/public/cpp/system/BUILD.gn
+++ b/mojo/public/cpp/system/BUILD.gn
@@ -29,6 +29,7 @@
     "data_pipe.h",
     "functions.h",
     "handle.h",
+    "handle_signals_state.h",
     "message.h",
     "message_pipe.h",
     "platform_handle.cc",
diff --git a/mojo/public/cpp/system/handle.h b/mojo/public/cpp/system/handle.h
index 5b2eb7bb..3f949e7 100644
--- a/mojo/public/cpp/system/handle.h
+++ b/mojo/public/cpp/system/handle.h
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "mojo/public/c/system/functions.h"
 #include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/handle_signals_state.h"
 
 namespace mojo {
 
@@ -170,6 +171,14 @@
     DCHECK_EQ(MOJO_RESULT_OK, result);
   }
 
+  HandleSignalsState QuerySignalsState() const {
+    HandleSignalsState signals_state;
+    MojoResult result = MojoQueryHandleSignalsState(
+        value_, static_cast<MojoHandleSignalsState*>(&signals_state));
+    DCHECK_EQ(MOJO_RESULT_OK, result);
+    return signals_state;
+  }
+
  private:
   MojoHandle value_;
 
diff --git a/mojo/public/cpp/system/handle_signals_state.h b/mojo/public/cpp/system/handle_signals_state.h
new file mode 100644
index 0000000..9e2430f
--- /dev/null
+++ b/mojo/public/cpp/system/handle_signals_state.h
@@ -0,0 +1,83 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_HANDLE_SIGNALS_STATE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_HANDLE_SIGNALS_STATE_H_
+
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/system_export.h"
+
+namespace mojo {
+
+// A convenience wrapper around the MojoHandleSignalsState struct.
+struct MOJO_CPP_SYSTEM_EXPORT HandleSignalsState final
+    : public MojoHandleSignalsState {
+  HandleSignalsState() {
+    satisfied_signals = MOJO_HANDLE_SIGNAL_NONE;
+    satisfiable_signals = MOJO_HANDLE_SIGNAL_NONE;
+  }
+
+  HandleSignalsState(MojoHandleSignals satisfied,
+                     MojoHandleSignals satisfiable) {
+    satisfied_signals = satisfied;
+    satisfiable_signals = satisfiable;
+  }
+
+  bool operator==(const HandleSignalsState& other) const {
+    return satisfied_signals == other.satisfied_signals &&
+           satisfiable_signals == other.satisfiable_signals;
+  }
+
+  // TODO(rockot): Remove this in favor of operator==.
+  bool equals(const HandleSignalsState& other) const {
+    return satisfied_signals == other.satisfied_signals &&
+           satisfiable_signals == other.satisfiable_signals;
+  }
+
+  bool satisfies(MojoHandleSignals signals) const {
+    return !!(satisfied_signals & signals);
+  }
+
+  bool can_satisfy(MojoHandleSignals signals) const {
+    return !!(satisfiable_signals & signals);
+  }
+
+  // The handle is currently readable. May apply to a message pipe handle or
+  // data pipe consumer handle.
+  bool readable() const { return satisfies(MOJO_HANDLE_SIGNAL_READABLE); }
+
+  // The handle is currently writable. May apply to a message pipe handle or
+  // data pipe producer handle.
+  bool writable() const { return satisfies(MOJO_HANDLE_SIGNAL_WRITABLE); }
+
+  // The handle's peer is closed. May apply to any message pipe or data pipe
+  // handle.
+  bool peer_closed() const { return satisfies(MOJO_HANDLE_SIGNAL_PEER_CLOSED); }
+
+  // The handle will never be |readable()| again.
+  bool never_readable() const {
+    return !can_satisfy(MOJO_HANDLE_SIGNAL_READABLE);
+  }
+
+  // The handle will never be |writable()| again.
+  bool never_writable() const {
+    return !can_satisfy(MOJO_HANDLE_SIGNAL_WRITABLE);
+  }
+
+  // The handle can never indicate |peer_closed()|. Never true for message pipe
+  // or data pipe handles (they can always signal peer closure), but always true
+  // for other types of handles (they have no peer.)
+  bool never_peer_closed() const {
+    return !can_satisfy(MOJO_HANDLE_SIGNAL_PEER_CLOSED);
+  }
+
+  // (Copy and assignment allowed.)
+};
+
+static_assert(sizeof(HandleSignalsState) == sizeof(MojoHandleSignalsState),
+              "HandleSignalsState should add no overhead");
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_SYSTEM_HANDLE_SIGNALS_STATE_H_
diff --git a/mojo/public/cpp/system/tests/BUILD.gn b/mojo/public/cpp/system/tests/BUILD.gn
index 3c3d410..c6e05e8 100644
--- a/mojo/public/cpp/system/tests/BUILD.gn
+++ b/mojo/public/cpp/system/tests/BUILD.gn
@@ -7,6 +7,7 @@
 
   sources = [
     "core_unittest.cc",
+    "handle_signals_state_unittest.cc",
     "simple_watcher_unittest.cc",
   ]
 
diff --git a/mojo/public/cpp/system/tests/handle_signals_state_unittest.cc b/mojo/public/cpp/system/tests/handle_signals_state_unittest.cc
new file mode 100644
index 0000000..82f538e1
--- /dev/null
+++ b/mojo/public/cpp/system/tests/handle_signals_state_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/system/handle_signals_state.h"
+
+#include "mojo/public/c/system/types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+using HandleSignalsStateTest = testing::Test;
+
+TEST_F(HandleSignalsStateTest, SanityCheck) {
+  // There's not much to test here. Just a quick sanity check to make sure the
+  // code compiles and the helper methods do what they're supposed to do.
+
+  HandleSignalsState empty_signals(MOJO_HANDLE_SIGNAL_NONE,
+                                   MOJO_HANDLE_SIGNAL_NONE);
+  EXPECT_FALSE(empty_signals.readable());
+  EXPECT_FALSE(empty_signals.writable());
+  EXPECT_FALSE(empty_signals.peer_closed());
+  EXPECT_TRUE(empty_signals.never_readable());
+  EXPECT_TRUE(empty_signals.never_writable());
+  EXPECT_TRUE(empty_signals.never_peer_closed());
+
+  HandleSignalsState all_signals(
+      MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+          MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+      MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+          MOJO_HANDLE_SIGNAL_PEER_CLOSED);
+  EXPECT_TRUE(all_signals.readable());
+  EXPECT_TRUE(all_signals.writable());
+  EXPECT_TRUE(all_signals.peer_closed());
+  EXPECT_FALSE(all_signals.never_readable());
+  EXPECT_FALSE(all_signals.never_writable());
+  EXPECT_FALSE(all_signals.never_peer_closed());
+}
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Core.java b/mojo/public/java/system/src/org/chromium/mojo/system/Core.java
index e5c6d08..40e4be36 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/Core.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Core.java
@@ -4,8 +4,6 @@
 
 package org.chromium.mojo.system;
 
-import java.util.List;
-
 /**
  * Core mojo interface giving access to the base operations. See |src/mojo/public/c/system/core.h|
  * for the underlying api.
@@ -129,142 +127,6 @@
     }
 
     /**
-     * Result for the |wait| method.
-     */
-    public static class WaitResult {
-        /**
-         * The result of the wait method.
-         * <p>
-         * |MojoResult.OK| if some signal in |signals| was satisfied (or is already satisfied).
-         * <p>
-         * |MojoResult.DEADLINE_EXCEEDED| if the deadline has passed without any of the signals
-         * being satisfied.
-         * <p>
-         * |MojoResult.CANCELLED| if |handle| is closed concurrently by another thread.
-         * <p>
-         * |MojoResult.FAILED_PRECONDITION| if it is or becomes impossible that any flag in
-         * |signals| will ever be satisfied (for example, if the other endpoint is closed).
-         */
-        private int mMojoResult;
-
-        /**
-         * The signaling state of handles.
-         */
-        private HandleSignalsState mHandleSignalsState;
-
-        /**
-         * Returns the mojoResult.
-         */
-        public int getMojoResult() {
-            return mMojoResult;
-        }
-
-        /**
-         * @param mojoResult the mojoResult to set
-         */
-        public void setMojoResult(int mojoResult) {
-            mMojoResult = mojoResult;
-        }
-
-        /**
-         * Returns the handleSignalsState.
-         */
-        public HandleSignalsState getHandleSignalsState() {
-            return mHandleSignalsState;
-        }
-
-        /**
-         * @param handleSignalsState the handleSignalsState to set
-         */
-        public void setHandleSignalsState(HandleSignalsState handleSignalsState) {
-            mHandleSignalsState = handleSignalsState;
-        }
-    }
-
-    /**
-     * Waits on the given |handle| until the state indicated by |signals| is satisfied or until
-     * |deadline| has passed.
-     *
-     * @return a |WaitResult|.
-     */
-    public WaitResult wait(Handle handle, HandleSignals signals, long deadline);
-
-    /**
-     * Result for the |waitMany| method.
-     */
-    public static class WaitManyResult {
-
-        /**
-         * See |wait| for the different possible values.
-         */
-        private int mMojoResult;
-
-        /**
-         * If |mojoResult| is |MojoResult.OK|, |handleIndex| is the index of the handle for which
-         * some flag was satisfied (or is already satisfied). If |mojoResult| is
-         * |MojoResult.CANCELLED| or |MojoResult.FAILED_PRECONDITION|, |handleIndex| is the index of
-         * the handle for which the issue occurred.
-         */
-        private int mHandleIndex;
-
-        /**
-         * The signaling state of handles. Will not be set if |mojoResult| is
-         * |MOJO_RESULT_INVALID_ARGUMENT| or |MOJO_RESULT_RESOURCE_EXHAUSTED|
-         */
-        private List<HandleSignalsState> mSignalStates;
-
-        /**
-         * Returns the mojoResult.
-         */
-        public int getMojoResult() {
-            return mMojoResult;
-        }
-
-        /**
-         * @param mojoResult the mojoResult to set
-         */
-        public void setMojoResult(int mojoResult) {
-            mMojoResult = mojoResult;
-        }
-
-        /**
-         * Returns the handleIndex.
-         */
-        public int getHandleIndex() {
-            return mHandleIndex;
-        }
-
-        /**
-         * @param handleIndex the handleIndex to set
-         */
-        public void setHandleIndex(int handleIndex) {
-            mHandleIndex = handleIndex;
-        }
-
-        /**
-         * Returns the signalStates.
-         */
-        public List<HandleSignalsState> getSignalStates() {
-            return mSignalStates;
-        }
-
-        /**
-         * @param signalStates the signalStates to set
-         */
-        public void setSignalStates(List<HandleSignalsState> signalStates) {
-            mSignalStates = signalStates;
-        }
-    }
-
-    /**
-     * Waits on handle in |handles| for at least one of them to satisfy the associated
-     * |HandleSignals|, or until |deadline| has passed.
-     *
-     * @returns a |WaitManyResult|.
-     */
-    public WaitManyResult waitMany(List<Pair<Handle, HandleSignals>> handles, long deadline);
-
-    /**
      * Creates a message pipe, which is a bidirectional communication channel for framed data (i.e.,
      * messages), with the given options. Messages can contain plain data and/or Mojo handles.
      *
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java b/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java
index 6181669d..903f36d 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java
@@ -4,7 +4,7 @@
 
 package org.chromium.mojo.system;
 
-import org.chromium.mojo.system.Core.WaitResult;
+import org.chromium.mojo.system.Core.HandleSignalsState;
 
 import java.io.Closeable;
 
@@ -25,9 +25,9 @@
     public void close();
 
     /**
-     * @see Core#wait(Handle, Core.HandleSignals, long)
+     * @return the last known signaling state of the handle.
      */
-    public WaitResult wait(Core.HandleSignals signals, long deadline);
+    public HandleSignalsState querySignalsState();
 
     /**
      * @return whether the handle is valid. A handle is valid until it has been explicitly closed or
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java
index 9c20fdd..f8b99c6d 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java
@@ -4,8 +4,7 @@
 
 package org.chromium.mojo.system;
 
-import org.chromium.mojo.system.Core.HandleSignals;
-import org.chromium.mojo.system.Core.WaitResult;
+import org.chromium.mojo.system.Core.HandleSignalsState;
 import org.chromium.mojo.system.DataPipe.ConsumerHandle;
 import org.chromium.mojo.system.DataPipe.ProducerHandle;
 
@@ -38,10 +37,10 @@
     }
 
     /**
-     * @see Handle#wait(Core.HandleSignals, long)
+     * @see Handle#querySignalsState()
      */
     @Override
-    public WaitResult wait(HandleSignals signals, long deadline) {
+    public HandleSignalsState querySignalsState() {
         throw new MojoException(MojoResult.INVALID_ARGUMENT);
     }
 
diff --git a/mojo/public/js/core.js b/mojo/public/js/core.js
index ef480ee..28fdcb77 100644
--- a/mojo/public/js/core.js
+++ b/mojo/public/js/core.js
@@ -142,6 +142,18 @@
 function close(handle) { [native code] }
 
 /**
+ * Queries the last known signaling state of |handle|.
+ *
+ * @param {MojoHandle} handle Handle to query.
+ * @return {object} An object of the form {
+ *     result,              // MOJO_RESULT_OK or MOJO_RESULT_INVALID_ARGUMENT
+ *     satisfiedSignals,    // MojoHandleSignals (see above)
+ *     satisfiableSignals,  // MojoHandleSignals
+ * }
+ */
+function queryHandleSignalsState(handle) { [native code] }
+
+/**
  * Waits on the given handle until a signal indicated by |signals| is
  * satisfied or until |deadline| is passed. See MojoWait for more information.
  *
diff --git a/mojo/public/js/tests/core_unittest.js b/mojo/public/js/tests/core_unittest.js
index 395ed05..7c77713 100644
--- a/mojo/public/js/tests/core_unittest.js
+++ b/mojo/public/js/tests/core_unittest.js
@@ -89,35 +89,15 @@
   }
 
   function testReadAndWriteMessage(pipe) {
-    var wait = core.waitMany([], [], 0);
-    expect(wait.result).toBe(core.RESULT_INVALID_ARGUMENT);
-    expect(wait.index).toBe(null);
-    expect(wait.signalsState).toBe(null);
+    var state0 = core.queryHandleSignalsState(pipe.handle0);
+    expect(state0.result).toBe(core.RESULT_OK);
+    expect(state0.satisfiedSignals).toBe(core.HANDLE_SIGNAL_WRITABLE);
+    expect(state0.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
 
-    wait = core.wait(pipe.handle0, core.HANDLE_SIGNAL_READABLE, 0);
-    expect(wait.result).toBe(core.RESULT_DEADLINE_EXCEEDED);
-    expect(wait.signalsState.satisfiedSignals).toBe(
-           core.HANDLE_SIGNAL_WRITABLE);
-    expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
-
-    wait = core.waitMany(
-                  [pipe.handle0, pipe.handle1],
-                  [core.HANDLE_SIGNAL_READABLE,core.HANDLE_SIGNAL_READABLE],
-                  0);
-    expect(wait.result).toBe(core.RESULT_DEADLINE_EXCEEDED);
-    expect(wait.index).toBe(null);
-    expect(wait.signalsState[0].satisfiedSignals).toBe(
-           core.HANDLE_SIGNAL_WRITABLE);
-    expect(wait.signalsState[0].satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
-    expect(wait.signalsState[1].satisfiedSignals).toBe(
-           core.HANDLE_SIGNAL_WRITABLE);
-    expect(wait.signalsState[1].satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
-
-    wait = core.wait(pipe.handle0, core.HANDLE_SIGNAL_WRITABLE, 0);
-    expect(wait.result).toBe(core.RESULT_OK);
-    expect(wait.signalsState.satisfiedSignals).toBe(
-           core.HANDLE_SIGNAL_WRITABLE);
-    expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
+    var state1 = core.queryHandleSignalsState(pipe.handle1);
+    expect(state1.result).toBe(core.RESULT_OK);
+    expect(state1.satisfiedSignals).toBe(core.HANDLE_SIGNAL_WRITABLE);
+    expect(state1.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
 
     var senderData = new Uint8Array(42);
     for (var i = 0; i < senderData.length; ++i) {
@@ -130,14 +110,13 @@
 
     expect(result).toBe(core.RESULT_OK);
 
-    wait = core.wait(pipe.handle0, core.HANDLE_SIGNAL_WRITABLE, 0);
-    expect(wait.result).toBe(core.RESULT_OK);
-    expect(wait.signalsState.satisfiedSignals).toBe(
-        core.HANDLE_SIGNAL_WRITABLE);
-    expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
+    state0 = core.queryHandleSignalsState(pipe.handle0);
+    expect(state0.result).toBe(core.RESULT_OK);
+    expect(state0.satisfiedSignals).toBe(core.HANDLE_SIGNAL_WRITABLE);
+    expect(state0.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
 
-    wait = core.wait(pipe.handle1, core.HANDLE_SIGNAL_READABLE,
-                     core.DEADLINE_INDEFINITE);
+    var wait = core.wait(pipe.handle1, core.HANDLE_SIGNAL_READABLE,
+                         core.DEADLINE_INDEFINITE);
     expect(wait.result).toBe(core.RESULT_OK);
     expect(wait.signalsState.satisfiedSignals).toBe(HANDLE_SIGNAL_READWRITABLE);
     expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
diff --git a/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
index 831157f..5a1cf2a 100644
--- a/mojo/public/tools/bindings/chromium_bindings_configuration.gni
+++ b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
@@ -21,6 +21,7 @@
   "//device/gamepad/public/interfaces/typemaps.gni",
   "//device/generic_sensor/public/interfaces/typemaps.gni",
   "//device/usb/public/interfaces/typemaps.gni",
+  "//extensions/common/typemaps.gni",
   "//gpu/ipc/common/typemaps.gni",
   "//media/capture/mojo/typemaps.gni",
   "//media/mojo/interfaces/typemaps.gni",
diff --git a/testing/libfuzzer/fuzzers/BUILD.gn b/testing/libfuzzer/fuzzers/BUILD.gn
index 291d7ec0..f550f52 100644
--- a/testing/libfuzzer/fuzzers/BUILD.gn
+++ b/testing/libfuzzer/fuzzers/BUILD.gn
@@ -19,16 +19,6 @@
   additional_configs = [ "//testing/libfuzzer:no_clusterfuzz" ]
 }
 
-fuzzer_test("brotli_fuzzer") {
-  sources = [
-    "brotli_fuzzer.cc",
-  ]
-  deps = [
-    "//third_party/brotli:dec",
-  ]
-  libfuzzer_options = [ "max_len=1280" ]
-}
-
 fuzzer_test("courgette_fuzzer") {
   sources = [
     "courgette_fuzzer.cc",
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked-in-new-window-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked-in-new-window-expected.txt
new file mode 100644
index 0000000..d1d429f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked-in-new-window-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL The form resubmission should be blocked after the redirect assert_unreached: The form submission wasn't blocked. Reached unreachable code
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked-in-new-window.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked-in-new-window.html
new file mode 100644
index 0000000..779db7c5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked-in-new-window.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<!--
+	TODO(mkwst, arthursonzogni). This test fails. See https://crbug.com/700964
+-->
+<html>
+<head>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <meta http-equiv="Content-Security-Policy" content="form-action 127.0.0.1:8000">
+</head>
+<body>
+  <form
+    action="/resources/redirection-response.php?host=localhost:8000&status=302&target=/security/resources/post-done-to-opener.html"
+    target="namedWindow"
+    method="post">
+    <input type='submit' id='submit'>
+  </form>
+
+  <script>
+    async_test(t => {
+      // #1 Open a new window with the name matching the form.target attribute
+      //    above.
+      var namedWindow = window.open('/security/resources/empty.html', 'namedWindow')
+
+      // #2 Wait the window to be loaded. It prevents the document url to still
+      //    be about:blank and to have inherited from its opener's CSP.
+      t.step_timeout(function() {
+        window.addEventListener('message', t.step_func(e => {
+          if (e.source == namedWindow && e.data == "done")
+            assert_unreached("The form submission wasn't blocked.");
+        }));
+
+        // The navigation should be blocked, either in the current window 1) or
+        // in the new window 2).
+
+        // 1) The navigation is blocked in the current window.
+        window.addEventListener('securitypolicyviolation', t.step_func(e => {
+          assert_equals(e.effectiveDirective, "form-action");
+          assert_equals(e.blockedURI, "localhost:8000/resources/post-done-to-opener.html");
+          namedWindow.close();
+          t.done();
+        }));
+
+        // 2) The navigation is blocked in the new window.
+        t.step_timeout(t.step_func(() => {
+          namedWindow.close();
+          t.done();
+        }), 1000);
+
+        // #3 Make a form submission with a redirect. It should be blocked by
+        //    the form-action directive after the redirect.
+        document.getElementById('submit').click();
+
+      }, 1000);
+    }, "The form resubmission should be blocked after the redirect");
+
+  </script>
+</body>
+</html>
diff --git a/third_party/WebKit/Source/bindings/scripts/code_generator_v8.py b/third_party/WebKit/Source/bindings/scripts/code_generator_v8.py
index 29feb03..477de5c 100644
--- a/third_party/WebKit/Source/bindings/scripts/code_generator_v8.py
+++ b/third_party/WebKit/Source/bindings/scripts/code_generator_v8.py
@@ -287,12 +287,20 @@
     def __init__(self, info_provider, cache_dir, output_dir, target_component):
         CodeGeneratorBase.__init__(self, MODULE_PYNAME, info_provider, cache_dir, output_dir)
         self.target_component = target_component
+        # The code below duplicates parts of TypedefResolver. We do not use it
+        # directly because IdlUnionType is not a type defined in
+        # idl_definitions.py. What we do instead is to resolve typedefs in
+        # _generate_container_code() whenever a new union file is generated.
+        self.typedefs = {}
+        for name, typedef in self.info_provider.typedefs.iteritems():
+            self.typedefs[name] = typedef.idl_type
 
     def _generate_container_code(self, union_type):
+        union_type = union_type.resolve_typedefs(self.typedefs)
         header_template = self.jinja_env.get_template('union_container.h.tmpl')
         cpp_template = self.jinja_env.get_template('union_container.cpp.tmpl')
         template_context = v8_union.container_context(
-            union_type, self.info_provider.interfaces_info)
+            union_type, self.info_provider)
         template_context['header_includes'].append(
             self.info_provider.include_path_for_export)
         template_context['header_includes'] = normalize_and_sort_includes(
diff --git a/third_party/WebKit/Source/bindings/scripts/compute_interfaces_info_individual.py b/third_party/WebKit/Source/bindings/scripts/compute_interfaces_info_individual.py
index 3947ff1..e7d4098 100755
--- a/third_party/WebKit/Source/bindings/scripts/compute_interfaces_info_individual.py
+++ b/third_party/WebKit/Source/bindings/scripts/compute_interfaces_info_individual.py
@@ -276,10 +276,6 @@
             partial_include_paths = []
             if this_include_path:
                 partial_include_paths.append(this_include_path)
-            for union_type in this_union_types:
-                name = shorten_union_name(union_type)
-                partial_include_paths.append(
-                    'bindings/%s/v8/%s.h' % (component, name))
             self.add_paths_to_partials_dict(definition.name, full_path, partial_include_paths)
             # Collects C++ header paths which should be included from generated
             # .cpp files.  The resulting structure is as follows.
diff --git a/third_party/WebKit/Source/bindings/scripts/compute_interfaces_info_overall.py b/third_party/WebKit/Source/bindings/scripts/compute_interfaces_info_overall.py
index eb412e7..5045270 100755
--- a/third_party/WebKit/Source/bindings/scripts/compute_interfaces_info_overall.py
+++ b/third_party/WebKit/Source/bindings/scripts/compute_interfaces_info_overall.py
@@ -285,11 +285,6 @@
             else:
                 dependencies_other_component_include_paths.append(include_path)
 
-        for union_type in interface_info.get('union_types', []):
-            name = shorten_union_name(union_type)
-            dependencies_include_paths.append(
-                'bindings/%s/v8/%s.h' % (component, name))
-
         interface_info.update({
             'dependencies_full_paths': dependencies_full_paths,
             'dependencies_include_paths': dependencies_include_paths,
diff --git a/third_party/WebKit/Source/bindings/scripts/idl_types.py b/third_party/WebKit/Source/bindings/scripts/idl_types.py
index d218ab0..104e020 100644
--- a/third_party/WebKit/Source/bindings/scripts/idl_types.py
+++ b/third_party/WebKit/Source/bindings/scripts/idl_types.py
@@ -379,7 +379,7 @@
 
     def resolve_typedefs(self, typedefs):
         self.member_types = [
-            typedefs.get(member_type, member_type)
+            member_type.resolve_typedefs(typedefs)
             for member_type in self.member_types]
         return self
 
diff --git a/third_party/WebKit/Source/bindings/scripts/idl_types_test.py b/third_party/WebKit/Source/bindings/scripts/idl_types_test.py
index c92fb42f..e360d5a 100644
--- a/third_party/WebKit/Source/bindings/scripts/idl_types_test.py
+++ b/third_party/WebKit/Source/bindings/scripts/idl_types_test.py
@@ -136,3 +136,39 @@
             set([IdlType('Node'), IdlSequenceType(IdlType('long')), IdlType('Event'),
                  IdlType('XMLHttpRequest'), IdlType('DOMString'),
                  IdlSequenceType(IdlUnionType([IdlSequenceType(IdlType('double')), IdlType('NodeList')]))]))
+
+    def test_resolve_typedefs(self):
+        # This is a simplification of the typedef mechanism to avoid having to
+        # use idl_definitions and use actual nodes from //tools/idl_parser.
+        typedefs = {
+            'Foo': IdlType('unsigned short'),
+            'MyBooleanType': IdlType('boolean'),
+            'SomeInterfaceT': IdlType('MyInterface'),
+        }
+
+        # (long long or MyBooleanType)
+        union = IdlUnionType([IdlType('long long'), IdlType('MyBooleanType')]).resolve_typedefs(typedefs)
+        self.assertEqual(union.name, 'LongLongOrBoolean')
+        self.assertEqual(union.member_types[0].name, 'LongLong')
+        self.assertEqual(union.member_types[1].name, 'Boolean')
+
+        # (Foo or SomeInterfaceT)
+        union = IdlUnionType([IdlType('Foo'), IdlType('SomeInterfaceT')]).resolve_typedefs(typedefs)
+        self.assertEqual(union.name, 'UnsignedShortOrMyInterface')
+        self.assertEqual(union.member_types[0].name, 'UnsignedShort')
+        self.assertEqual(union.member_types[1].name, 'MyInterface')
+
+        # (Foo or sequence<(MyBooleanType or double)>)
+        union = IdlUnionType([
+            IdlType('Foo'),
+            IdlSequenceType(IdlUnionType([IdlType('MyBooleanType'),
+                                          IdlType('double')]))]).resolve_typedefs(typedefs)
+        self.assertEqual(union.name, 'UnsignedShortOrBooleanOrDoubleSequence')
+        self.assertEqual(union.member_types[0].name, 'UnsignedShort')
+        self.assertEqual(union.member_types[1].name, 'BooleanOrDoubleSequence')
+        self.assertEqual(union.member_types[1].element_type.name, 'BooleanOrDouble')
+        self.assertEqual(union.member_types[1].element_type.member_types[0].name,
+                         'Boolean')
+        self.assertEqual(union.member_types[1].element_type.member_types[1].name,
+                         'Double')
+        self.assertEqual(2, len(union.flattened_member_types))
diff --git a/third_party/WebKit/Source/bindings/scripts/v8_union.py b/third_party/WebKit/Source/bindings/scripts/v8_union.py
index c277fb59..da19308 100644
--- a/third_party/WebKit/Source/bindings/scripts/v8_union.py
+++ b/third_party/WebKit/Source/bindings/scripts/v8_union.py
@@ -34,7 +34,7 @@
 header_includes = set()
 
 
-def container_context(union_type, interfaces_info):
+def container_context(union_type, info_provider):
     cpp_includes.clear()
     header_forward_decls.clear()
     header_includes.clear()
@@ -55,7 +55,7 @@
     record_type = None
     string_type = None
     for member in sorted(union_type.flattened_member_types, key=lambda m: m.name):
-        context = member_context(member, interfaces_info)
+        context = member_context(member, info_provider)
         members.append(context)
         if member.base_type == 'ArrayBuffer':
             if array_buffer_type:
@@ -123,7 +123,8 @@
     }
 
 
-def _update_includes_and_forward_decls(member, interface_info):
+def _update_includes_and_forward_decls(member, info_provider):
+    interface_info = info_provider.interfaces_info.get(member.name, None)
     if interface_info:
         cpp_includes.update(interface_info.get(
             'dependencies_include_paths', []))
@@ -135,16 +136,22 @@
             header_forward_decls.add(member.implemented_as)
     else:
         if member.is_record_type:
-            # The headers for both T and U must be present when
-            # Vector<std::pair<T, U>> is declared.
-            header_includes.update(member.includes_for_type())
+            _update_includes_and_forward_decls(member.key_type, info_provider)
+            _update_includes_and_forward_decls(member.value_type, info_provider)
+        elif member.is_array_or_sequence_type:
+            _update_includes_and_forward_decls(member.element_type, info_provider)
         else:
-            cpp_includes.update(member.includes_for_type())
+            if member.is_union_type:
+                # Reaching this block means we have a union that is inside a
+                # record or sequence.
+                header_forward_decls.add(member.name)
+                cpp_includes.update([info_provider.include_path_for_union_types(member)])
+            else:
+                cpp_includes.update(member.includes_for_type())
 
 
-def member_context(member, interfaces_info):
-    interface_info = interfaces_info.get(member.name, None)
-    _update_includes_and_forward_decls(member, interface_info)
+def member_context(member, info_provider):
+    _update_includes_and_forward_decls(member, info_provider)
     if member.is_nullable:
         member = member.inner_type
     return {
diff --git a/third_party/WebKit/Source/bindings/templates/callback_interface.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/callback_interface.cpp.tmpl
index a101dda..420c276 100644
--- a/third_party/WebKit/Source/bindings/templates/callback_interface.cpp.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/callback_interface.cpp.tmpl
@@ -30,10 +30,12 @@
     {{return_default}};
   if (!m_scriptState->contextIsValid())
     {{return_default}};
+
   ScriptState::Scope scope(m_scriptState.get());
   {% if method.call_with_this_handle %}
   v8::Local<v8::Value> thisHandle = thisValue.v8Value();
   {% endif %}
+
   {% for argument in method.arguments %}
   v8::Local<v8::Value> {{argument.handle}} = {{argument.cpp_value_to_v8_value}};
   {% endfor %}
@@ -44,15 +46,27 @@
   v8::Local<v8::Value> *argv = 0;
   {% endif %}
 
-  {% set this_handle_parameter = 'thisHandle, ' if method.call_with_this_handle else 'v8::Undefined(m_scriptState->isolate()), ' %}
+  v8::Isolate* isolate = m_scriptState->isolate();
+  {% set this_handle_parameter = 'thisHandle'
+         if method.call_with_this_handle else 'v8::Undefined(isolate)' %}
   {% if method.idl_type == 'boolean' %}
-  v8::TryCatch exceptionCatcher(m_scriptState->isolate());
+  v8::TryCatch exceptionCatcher(isolate);
   exceptionCatcher.SetVerbose(true);
-  V8ScriptRunner::callFunction(m_callback.newLocal(m_scriptState->isolate()), m_scriptState->getExecutionContext(), {{this_handle_parameter}}{{method.arguments | length}}, argv, m_scriptState->isolate());
+  V8ScriptRunner::callFunction(m_callback.newLocal(isolate),
+                               executionContext,
+                               {{this_handle_parameter}},
+                               {{method.arguments | length}},
+                               argv,
+                               isolate);
   return !exceptionCatcher.HasCaught();
   {% else %}{# void #}
-  V8ScriptRunner::callFunction(m_callback.newLocal(m_scriptState->isolate()), m_scriptState->getExecutionContext(), {{this_handle_parameter}}{{method.arguments | length}}, argv, m_scriptState->isolate());
-    {% endif %}
+  V8ScriptRunner::callFunction(m_callback.newLocal(isolate),
+                               m_scriptState->getExecutionContext(),
+                               {{this_handle_parameter}},
+                               {{method.arguments | length}},
+                               argv,
+                               isolate);
+  {% endif %}
 }
 
 {% endfor %}
diff --git a/third_party/WebKit/Source/bindings/templates/callback_interface.h.tmpl b/third_party/WebKit/Source/bindings/templates/callback_interface.h.tmpl
index 54dbfc1..e8d14121 100644
--- a/third_party/WebKit/Source/bindings/templates/callback_interface.h.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/callback_interface.h.tmpl
@@ -23,6 +23,7 @@
 {% for method in methods %}
   {{method.cpp_type}} {{method.name}}({{method.argument_declarations | join(', ')}}) override;
 {% endfor %}
+
  private:
   {{exported}}{{v8_class}}(v8::Local<v8::Function>, ScriptState*);
 
diff --git a/third_party/WebKit/Source/bindings/tests/idls/core/TestDictionary.idl b/third_party/WebKit/Source/bindings/tests/idls/core/TestDictionary.idl
index 0a188884..ef58931 100644
--- a/third_party/WebKit/Source/bindings/tests/idls/core/TestDictionary.idl
+++ b/third_party/WebKit/Source/bindings/tests/idls/core/TestDictionary.idl
@@ -39,4 +39,5 @@
     [PrefixGet] object prefixGetMember;
     record<ByteString, byte> recordMember;
     record<USVString, TestObject> garbageCollectedRecordMember;
+    (Float or BooleanType) unionWithTypedefs;
 };
diff --git a/third_party/WebKit/Source/bindings/tests/idls/core/TestObject.idl b/third_party/WebKit/Source/bindings/tests/idls/core/TestObject.idl
index 1022ee7..4e24d28 100644
--- a/third_party/WebKit/Source/bindings/tests/idls/core/TestObject.idl
+++ b/third_party/WebKit/Source/bindings/tests/idls/core/TestObject.idl
@@ -331,6 +331,10 @@
     void voidMethodDoubleOrNullOrDOMStringArg((double? or DOMString) arg);
     void voidMethodDOMStringOrArrayBufferOrArrayBufferViewArg((DOMString or ArrayBuffer or ArrayBufferView) arg);
     void voidMethodArrayBufferOrArrayBufferViewOrDictionaryArg((ArrayBuffer or ArrayBufferView or Dictionary) arg);
+    // Unions with types that need extra includes/forward declarations in container types
+    void voidMethodBooleanOrElementSequenceArg((BooleanType or sequence<Element>) arg);
+    void voidMethodDoubleOrLongOrBooleanSequenceArg((double or sequence<(long or boolean)>) arg);
+    void voidMethodElementSequenceOrByteStringDoubleOrStringRecord((sequence<Element> or record<ByteString, (double or DOMString)>) arg);
     // Array of Union types
     void voidMethodArrayOfDoubleOrDOMStringArg((double or DOMString)... arg);
     // Currently only used on interface type arguments
diff --git a/third_party/WebKit/Source/bindings/tests/idls/core/TestTypedefs.idl b/third_party/WebKit/Source/bindings/tests/idls/core/TestTypedefs.idl
index 3eca6f3..f82283f6 100644
--- a/third_party/WebKit/Source/bindings/tests/idls/core/TestTypedefs.idl
+++ b/third_party/WebKit/Source/bindings/tests/idls/core/TestTypedefs.idl
@@ -32,6 +32,7 @@
 // as typedefs are resolved during IR construction (after parsing,
 // before code generation), and thus the code generator never sees them.
 
+typedef boolean                    BooleanType;
 typedef float                      Float;
 typedef unsigned long long         ULongLong;
 typedef TestInterfaceEmpty         TestInterfaceEmptyType;
@@ -46,6 +47,7 @@
 typedef record<USVString, TestObject> RecordWithOilpanValueType;
 typedef (sequence<sequence<ByteString>> or record<ByteString, ByteString>) UnionWithRecord;
 typedef (Node or (sequence<long> or Event) or (XMLHttpRequest or DOMString)? or record<DOMString, (ByteString or NodeList)>) NestedUnionType;
+typedef (ULongLong or (BooleanType or TestCallbackInterfaceType)) UnionWithTypedef;
 
 [
     Constructor(String stringArg),
@@ -72,4 +74,6 @@
     record<DOMString, boolean> methodThatReturnsRecord();
 
     void voidMethodNestedUnionType(NestedUnionType arg);
+
+    void voidMethodUnionWithTypedef(UnionWithTypedef arg);
 };
diff --git a/third_party/WebKit/Source/bindings/tests/idls/modules/TestInterfacePartial3.idl b/third_party/WebKit/Source/bindings/tests/idls/modules/TestInterfacePartial3.idl
index 946f9cf0..9501a5d 100644
--- a/third_party/WebKit/Source/bindings/tests/idls/modules/TestInterfacePartial3.idl
+++ b/third_party/WebKit/Source/bindings/tests/idls/modules/TestInterfacePartial3.idl
@@ -45,4 +45,7 @@
     static void partial2StaticVoidMethod(DOMString value);
 
     [Unscopable] void unscopableVoidMethod();
+
+    // UnionWithTypedef is a typedef declared in core/TestTypedefs.idl.
+    UnionWithTypedef unionWithTypedefMethod();
 };
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrElementSequence.cpp b/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrElementSequence.cpp
new file mode 100644
index 0000000..c94eb1a1
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrElementSequence.cpp
@@ -0,0 +1,116 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file has been auto-generated by code_generator_v8.py.
+// DO NOT MODIFY!
+
+// This file has been generated from the Jinja2 template in
+// third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl
+
+// clang-format off
+#include "BooleanOrElementSequence.h"
+
+#include "bindings/core/v8/IDLTypes.h"
+#include "bindings/core/v8/NativeValueTraitsImpl.h"
+#include "bindings/core/v8/ToV8.h"
+#include "bindings/core/v8/V8Element.h"
+#include "core/animation/ElementAnimation.h"
+#include "core/dom/ChildNode.h"
+#include "core/dom/ElementFullscreen.h"
+#include "core/dom/NonDocumentTypeChildNode.h"
+#include "core/dom/ParentNode.h"
+
+namespace blink {
+
+BooleanOrElementSequence::BooleanOrElementSequence() : m_type(SpecificTypeNone) {}
+
+bool BooleanOrElementSequence::getAsBoolean() const {
+  DCHECK(isBoolean());
+  return m_boolean;
+}
+
+void BooleanOrElementSequence::setBoolean(bool value) {
+  DCHECK(isNull());
+  m_boolean = value;
+  m_type = SpecificTypeBoolean;
+}
+
+BooleanOrElementSequence BooleanOrElementSequence::fromBoolean(bool value) {
+  BooleanOrElementSequence container;
+  container.setBoolean(value);
+  return container;
+}
+
+const HeapVector<Member<Element>>& BooleanOrElementSequence::getAsElementSequence() const {
+  DCHECK(isElementSequence());
+  return m_elementSequence;
+}
+
+void BooleanOrElementSequence::setElementSequence(const HeapVector<Member<Element>>& value) {
+  DCHECK(isNull());
+  m_elementSequence = value;
+  m_type = SpecificTypeElementSequence;
+}
+
+BooleanOrElementSequence BooleanOrElementSequence::fromElementSequence(const HeapVector<Member<Element>>& value) {
+  BooleanOrElementSequence container;
+  container.setElementSequence(value);
+  return container;
+}
+
+BooleanOrElementSequence::BooleanOrElementSequence(const BooleanOrElementSequence&) = default;
+BooleanOrElementSequence::~BooleanOrElementSequence() = default;
+BooleanOrElementSequence& BooleanOrElementSequence::operator=(const BooleanOrElementSequence&) = default;
+
+DEFINE_TRACE(BooleanOrElementSequence) {
+  visitor->trace(m_elementSequence);
+}
+
+void V8BooleanOrElementSequence::toImpl(v8::Isolate* isolate, v8::Local<v8::Value> v8Value, BooleanOrElementSequence& impl, UnionTypeConversionMode conversionMode, ExceptionState& exceptionState) {
+  if (v8Value.IsEmpty())
+    return;
+
+  if (conversionMode == UnionTypeConversionMode::Nullable && isUndefinedOrNull(v8Value))
+    return;
+
+  if (v8Value->IsArray()) {
+    HeapVector<Member<Element>> cppValue = toMemberNativeArray<Element>(v8Value, 0, isolate, exceptionState);
+    if (exceptionState.hadException())
+      return;
+    impl.setElementSequence(cppValue);
+    return;
+  }
+
+  if (v8Value->IsBoolean()) {
+    impl.setBoolean(v8Value.As<v8::Boolean>()->Value());
+    return;
+  }
+
+  {
+    impl.setBoolean(v8Value->BooleanValue());
+    return;
+  }
+}
+
+v8::Local<v8::Value> ToV8(const BooleanOrElementSequence& impl, v8::Local<v8::Object> creationContext, v8::Isolate* isolate) {
+  switch (impl.m_type) {
+    case BooleanOrElementSequence::SpecificTypeNone:
+      return v8::Null(isolate);
+    case BooleanOrElementSequence::SpecificTypeBoolean:
+      return v8Boolean(impl.getAsBoolean(), isolate);
+    case BooleanOrElementSequence::SpecificTypeElementSequence:
+      return ToV8(impl.getAsElementSequence(), creationContext, isolate);
+    default:
+      NOTREACHED();
+  }
+  return v8::Local<v8::Value>();
+}
+
+BooleanOrElementSequence NativeValueTraits<BooleanOrElementSequence>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
+  BooleanOrElementSequence impl;
+  V8BooleanOrElementSequence::toImpl(isolate, value, impl, UnionTypeConversionMode::NotNullable, exceptionState);
+  return impl;
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrElementSequence.h b/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrElementSequence.h
new file mode 100644
index 0000000..fa3b024
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrElementSequence.h
@@ -0,0 +1,96 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file has been auto-generated by code_generator_v8.py.
+// DO NOT MODIFY!
+
+// This file has been generated from the Jinja2 template in
+// third_party/WebKit/Source/bindings/templates/union_container.h.tmpl
+
+// clang-format off
+#ifndef BooleanOrElementSequence_h
+#define BooleanOrElementSequence_h
+
+#include "bindings/core/v8/Dictionary.h"
+#include "bindings/core/v8/ExceptionState.h"
+#include "bindings/core/v8/NativeValueTraits.h"
+#include "bindings/core/v8/V8Binding.h"
+#include "core/CoreExport.h"
+#include "platform/heap/Handle.h"
+
+namespace blink {
+
+class Element;
+
+class CORE_EXPORT BooleanOrElementSequence final {
+  DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ public:
+  BooleanOrElementSequence();
+  bool isNull() const { return m_type == SpecificTypeNone; }
+
+  bool isBoolean() const { return m_type == SpecificTypeBoolean; }
+  bool getAsBoolean() const;
+  void setBoolean(bool);
+  static BooleanOrElementSequence fromBoolean(bool);
+
+  bool isElementSequence() const { return m_type == SpecificTypeElementSequence; }
+  const HeapVector<Member<Element>>& getAsElementSequence() const;
+  void setElementSequence(const HeapVector<Member<Element>>&);
+  static BooleanOrElementSequence fromElementSequence(const HeapVector<Member<Element>>&);
+
+  BooleanOrElementSequence(const BooleanOrElementSequence&);
+  ~BooleanOrElementSequence();
+  BooleanOrElementSequence& operator=(const BooleanOrElementSequence&);
+  DECLARE_TRACE();
+
+ private:
+  enum SpecificTypes {
+    SpecificTypeNone,
+    SpecificTypeBoolean,
+    SpecificTypeElementSequence,
+  };
+  SpecificTypes m_type;
+
+  bool m_boolean;
+  HeapVector<Member<Element>> m_elementSequence;
+
+  friend CORE_EXPORT v8::Local<v8::Value> ToV8(const BooleanOrElementSequence&, v8::Local<v8::Object>, v8::Isolate*);
+};
+
+class V8BooleanOrElementSequence final {
+ public:
+  CORE_EXPORT static void toImpl(v8::Isolate*, v8::Local<v8::Value>, BooleanOrElementSequence&, UnionTypeConversionMode, ExceptionState&);
+};
+
+CORE_EXPORT v8::Local<v8::Value> ToV8(const BooleanOrElementSequence&, v8::Local<v8::Object>, v8::Isolate*);
+
+template <class CallbackInfo>
+inline void v8SetReturnValue(const CallbackInfo& callbackInfo, BooleanOrElementSequence& impl) {
+  v8SetReturnValue(callbackInfo, ToV8(impl, callbackInfo.Holder(), callbackInfo.GetIsolate()));
+}
+
+template <class CallbackInfo>
+inline void v8SetReturnValue(const CallbackInfo& callbackInfo, BooleanOrElementSequence& impl, v8::Local<v8::Object> creationContext) {
+  v8SetReturnValue(callbackInfo, ToV8(impl, creationContext, callbackInfo.GetIsolate()));
+}
+
+template <>
+struct NativeValueTraits<BooleanOrElementSequence> : public NativeValueTraitsBase<BooleanOrElementSequence> {
+  CORE_EXPORT static BooleanOrElementSequence nativeValue(v8::Isolate*, v8::Local<v8::Value>, ExceptionState&);
+};
+
+template <>
+struct V8TypeOf<BooleanOrElementSequence> {
+  typedef V8BooleanOrElementSequence Type;
+};
+
+}  // namespace blink
+
+// We need to set canInitializeWithMemset=true because HeapVector supports
+// items that can initialize with memset or have a vtable. It is safe to
+// set canInitializeWithMemset=true for a union type object in practice.
+// See https://codereview.chromium.org/1118993002/#msg5 for more details.
+WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::BooleanOrElementSequence);
+
+#endif  // BooleanOrElementSequence_h
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrTestCallbackInterface.cpp b/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrTestCallbackInterface.cpp
new file mode 100644
index 0000000..8421b50
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrTestCallbackInterface.cpp
@@ -0,0 +1,109 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file has been auto-generated by code_generator_v8.py.
+// DO NOT MODIFY!
+
+// This file has been generated from the Jinja2 template in
+// third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl
+
+// clang-format off
+#include "BooleanOrTestCallbackInterface.h"
+
+#include "bindings/core/v8/IDLTypes.h"
+#include "bindings/core/v8/NativeValueTraitsImpl.h"
+#include "bindings/core/v8/ToV8.h"
+#include "bindings/core/v8/V8TestCallbackInterface.h"
+
+namespace blink {
+
+BooleanOrTestCallbackInterface::BooleanOrTestCallbackInterface() : m_type(SpecificTypeNone) {}
+
+bool BooleanOrTestCallbackInterface::getAsBoolean() const {
+  DCHECK(isBoolean());
+  return m_boolean;
+}
+
+void BooleanOrTestCallbackInterface::setBoolean(bool value) {
+  DCHECK(isNull());
+  m_boolean = value;
+  m_type = SpecificTypeBoolean;
+}
+
+BooleanOrTestCallbackInterface BooleanOrTestCallbackInterface::fromBoolean(bool value) {
+  BooleanOrTestCallbackInterface container;
+  container.setBoolean(value);
+  return container;
+}
+
+TestCallbackInterface* BooleanOrTestCallbackInterface::getAsTestCallbackInterface() const {
+  DCHECK(isTestCallbackInterface());
+  return m_testCallbackInterface;
+}
+
+void BooleanOrTestCallbackInterface::setTestCallbackInterface(TestCallbackInterface* value) {
+  DCHECK(isNull());
+  m_testCallbackInterface = value;
+  m_type = SpecificTypeTestCallbackInterface;
+}
+
+BooleanOrTestCallbackInterface BooleanOrTestCallbackInterface::fromTestCallbackInterface(TestCallbackInterface* value) {
+  BooleanOrTestCallbackInterface container;
+  container.setTestCallbackInterface(value);
+  return container;
+}
+
+BooleanOrTestCallbackInterface::BooleanOrTestCallbackInterface(const BooleanOrTestCallbackInterface&) = default;
+BooleanOrTestCallbackInterface::~BooleanOrTestCallbackInterface() = default;
+BooleanOrTestCallbackInterface& BooleanOrTestCallbackInterface::operator=(const BooleanOrTestCallbackInterface&) = default;
+
+DEFINE_TRACE(BooleanOrTestCallbackInterface) {
+  visitor->trace(m_testCallbackInterface);
+}
+
+void V8BooleanOrTestCallbackInterface::toImpl(v8::Isolate* isolate, v8::Local<v8::Value> v8Value, BooleanOrTestCallbackInterface& impl, UnionTypeConversionMode conversionMode, ExceptionState& exceptionState) {
+  if (v8Value.IsEmpty())
+    return;
+
+  if (conversionMode == UnionTypeConversionMode::Nullable && isUndefinedOrNull(v8Value))
+    return;
+
+  if (V8TestCallbackInterface::hasInstance(v8Value, isolate)) {
+    TestCallbackInterface* cppValue = V8TestCallbackInterface::toImpl(v8::Local<v8::Object>::Cast(v8Value));
+    impl.setTestCallbackInterface(cppValue);
+    return;
+  }
+
+  if (v8Value->IsBoolean()) {
+    impl.setBoolean(v8Value.As<v8::Boolean>()->Value());
+    return;
+  }
+
+  {
+    impl.setBoolean(v8Value->BooleanValue());
+    return;
+  }
+}
+
+v8::Local<v8::Value> ToV8(const BooleanOrTestCallbackInterface& impl, v8::Local<v8::Object> creationContext, v8::Isolate* isolate) {
+  switch (impl.m_type) {
+    case BooleanOrTestCallbackInterface::SpecificTypeNone:
+      return v8::Null(isolate);
+    case BooleanOrTestCallbackInterface::SpecificTypeBoolean:
+      return v8Boolean(impl.getAsBoolean(), isolate);
+    case BooleanOrTestCallbackInterface::SpecificTypeTestCallbackInterface:
+      return ToV8(impl.getAsTestCallbackInterface(), creationContext, isolate);
+    default:
+      NOTREACHED();
+  }
+  return v8::Local<v8::Value>();
+}
+
+BooleanOrTestCallbackInterface NativeValueTraits<BooleanOrTestCallbackInterface>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
+  BooleanOrTestCallbackInterface impl;
+  V8BooleanOrTestCallbackInterface::toImpl(isolate, value, impl, UnionTypeConversionMode::NotNullable, exceptionState);
+  return impl;
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrTestCallbackInterface.h b/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrTestCallbackInterface.h
new file mode 100644
index 0000000..e8c3bd36
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrTestCallbackInterface.h
@@ -0,0 +1,96 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file has been auto-generated by code_generator_v8.py.
+// DO NOT MODIFY!
+
+// This file has been generated from the Jinja2 template in
+// third_party/WebKit/Source/bindings/templates/union_container.h.tmpl
+
+// clang-format off
+#ifndef BooleanOrTestCallbackInterface_h
+#define BooleanOrTestCallbackInterface_h
+
+#include "bindings/core/v8/Dictionary.h"
+#include "bindings/core/v8/ExceptionState.h"
+#include "bindings/core/v8/NativeValueTraits.h"
+#include "bindings/core/v8/V8Binding.h"
+#include "core/CoreExport.h"
+#include "platform/heap/Handle.h"
+
+namespace blink {
+
+class TestCallbackInterface;
+
+class CORE_EXPORT BooleanOrTestCallbackInterface final {
+  DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ public:
+  BooleanOrTestCallbackInterface();
+  bool isNull() const { return m_type == SpecificTypeNone; }
+
+  bool isBoolean() const { return m_type == SpecificTypeBoolean; }
+  bool getAsBoolean() const;
+  void setBoolean(bool);
+  static BooleanOrTestCallbackInterface fromBoolean(bool);
+
+  bool isTestCallbackInterface() const { return m_type == SpecificTypeTestCallbackInterface; }
+  TestCallbackInterface* getAsTestCallbackInterface() const;
+  void setTestCallbackInterface(TestCallbackInterface*);
+  static BooleanOrTestCallbackInterface fromTestCallbackInterface(TestCallbackInterface*);
+
+  BooleanOrTestCallbackInterface(const BooleanOrTestCallbackInterface&);
+  ~BooleanOrTestCallbackInterface();
+  BooleanOrTestCallbackInterface& operator=(const BooleanOrTestCallbackInterface&);
+  DECLARE_TRACE();
+
+ private:
+  enum SpecificTypes {
+    SpecificTypeNone,
+    SpecificTypeBoolean,
+    SpecificTypeTestCallbackInterface,
+  };
+  SpecificTypes m_type;
+
+  bool m_boolean;
+  Member<TestCallbackInterface> m_testCallbackInterface;
+
+  friend CORE_EXPORT v8::Local<v8::Value> ToV8(const BooleanOrTestCallbackInterface&, v8::Local<v8::Object>, v8::Isolate*);
+};
+
+class V8BooleanOrTestCallbackInterface final {
+ public:
+  CORE_EXPORT static void toImpl(v8::Isolate*, v8::Local<v8::Value>, BooleanOrTestCallbackInterface&, UnionTypeConversionMode, ExceptionState&);
+};
+
+CORE_EXPORT v8::Local<v8::Value> ToV8(const BooleanOrTestCallbackInterface&, v8::Local<v8::Object>, v8::Isolate*);
+
+template <class CallbackInfo>
+inline void v8SetReturnValue(const CallbackInfo& callbackInfo, BooleanOrTestCallbackInterface& impl) {
+  v8SetReturnValue(callbackInfo, ToV8(impl, callbackInfo.Holder(), callbackInfo.GetIsolate()));
+}
+
+template <class CallbackInfo>
+inline void v8SetReturnValue(const CallbackInfo& callbackInfo, BooleanOrTestCallbackInterface& impl, v8::Local<v8::Object> creationContext) {
+  v8SetReturnValue(callbackInfo, ToV8(impl, creationContext, callbackInfo.GetIsolate()));
+}
+
+template <>
+struct NativeValueTraits<BooleanOrTestCallbackInterface> : public NativeValueTraitsBase<BooleanOrTestCallbackInterface> {
+  CORE_EXPORT static BooleanOrTestCallbackInterface nativeValue(v8::Isolate*, v8::Local<v8::Value>, ExceptionState&);
+};
+
+template <>
+struct V8TypeOf<BooleanOrTestCallbackInterface> {
+  typedef V8BooleanOrTestCallbackInterface Type;
+};
+
+}  // namespace blink
+
+// We need to set canInitializeWithMemset=true because HeapVector supports
+// items that can initialize with memset or have a vtable. It is safe to
+// set canInitializeWithMemset=true for a union type object in practice.
+// See https://codereview.chromium.org/1118993002/#msg5 for more details.
+WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::BooleanOrTestCallbackInterface);
+
+#endif  // BooleanOrTestCallbackInterface_h
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/ByteStringSequenceSequenceOrByteStringByteStringRecord.h b/third_party/WebKit/Source/bindings/tests/results/core/ByteStringSequenceSequenceOrByteStringByteStringRecord.h
index 009bd64..c30afda4 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/ByteStringSequenceSequenceOrByteStringByteStringRecord.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/ByteStringSequenceSequenceOrByteStringByteStringRecord.h
@@ -14,9 +14,7 @@
 
 #include "bindings/core/v8/Dictionary.h"
 #include "bindings/core/v8/ExceptionState.h"
-#include "bindings/core/v8/IDLTypes.h"
 #include "bindings/core/v8/NativeValueTraits.h"
-#include "bindings/core/v8/NativeValueTraitsImpl.h"
 #include "bindings/core/v8/V8Binding.h"
 #include "core/CoreExport.h"
 #include "platform/heap/Handle.h"
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrLongOrBooleanSequence.cpp b/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrLongOrBooleanSequence.cpp
new file mode 100644
index 0000000..cd135c3
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrLongOrBooleanSequence.cpp
@@ -0,0 +1,117 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file has been auto-generated by code_generator_v8.py.
+// DO NOT MODIFY!
+
+// This file has been generated from the Jinja2 template in
+// third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl
+
+// clang-format off
+#include "DoubleOrLongOrBooleanSequence.h"
+
+#include "bindings/core/v8/IDLTypes.h"
+#include "bindings/core/v8/LongOrBoolean.h"
+#include "bindings/core/v8/NativeValueTraitsImpl.h"
+#include "bindings/core/v8/ToV8.h"
+
+namespace blink {
+
+DoubleOrLongOrBooleanSequence::DoubleOrLongOrBooleanSequence() : m_type(SpecificTypeNone) {}
+
+double DoubleOrLongOrBooleanSequence::getAsDouble() const {
+  DCHECK(isDouble());
+  return m_double;
+}
+
+void DoubleOrLongOrBooleanSequence::setDouble(double value) {
+  DCHECK(isNull());
+  m_double = value;
+  m_type = SpecificTypeDouble;
+}
+
+DoubleOrLongOrBooleanSequence DoubleOrLongOrBooleanSequence::fromDouble(double value) {
+  DoubleOrLongOrBooleanSequence container;
+  container.setDouble(value);
+  return container;
+}
+
+const HeapVector<LongOrBoolean>& DoubleOrLongOrBooleanSequence::getAsLongOrBooleanSequence() const {
+  DCHECK(isLongOrBooleanSequence());
+  return m_longOrBooleanSequence;
+}
+
+void DoubleOrLongOrBooleanSequence::setLongOrBooleanSequence(const HeapVector<LongOrBoolean>& value) {
+  DCHECK(isNull());
+  m_longOrBooleanSequence = value;
+  m_type = SpecificTypeLongOrBooleanSequence;
+}
+
+DoubleOrLongOrBooleanSequence DoubleOrLongOrBooleanSequence::fromLongOrBooleanSequence(const HeapVector<LongOrBoolean>& value) {
+  DoubleOrLongOrBooleanSequence container;
+  container.setLongOrBooleanSequence(value);
+  return container;
+}
+
+DoubleOrLongOrBooleanSequence::DoubleOrLongOrBooleanSequence(const DoubleOrLongOrBooleanSequence&) = default;
+DoubleOrLongOrBooleanSequence::~DoubleOrLongOrBooleanSequence() = default;
+DoubleOrLongOrBooleanSequence& DoubleOrLongOrBooleanSequence::operator=(const DoubleOrLongOrBooleanSequence&) = default;
+
+DEFINE_TRACE(DoubleOrLongOrBooleanSequence) {
+  visitor->trace(m_longOrBooleanSequence);
+}
+
+void V8DoubleOrLongOrBooleanSequence::toImpl(v8::Isolate* isolate, v8::Local<v8::Value> v8Value, DoubleOrLongOrBooleanSequence& impl, UnionTypeConversionMode conversionMode, ExceptionState& exceptionState) {
+  if (v8Value.IsEmpty())
+    return;
+
+  if (conversionMode == UnionTypeConversionMode::Nullable && isUndefinedOrNull(v8Value))
+    return;
+
+  if (v8Value->IsArray()) {
+    HeapVector<LongOrBoolean> cppValue = toImplArray<HeapVector<LongOrBoolean>>(v8Value, 0, isolate, exceptionState);
+    if (exceptionState.hadException())
+      return;
+    impl.setLongOrBooleanSequence(cppValue);
+    return;
+  }
+
+  if (v8Value->IsNumber()) {
+    double cppValue = NativeValueTraits<IDLDouble>::nativeValue(isolate, v8Value, exceptionState);
+    if (exceptionState.hadException())
+      return;
+    impl.setDouble(cppValue);
+    return;
+  }
+
+  {
+    double cppValue = NativeValueTraits<IDLDouble>::nativeValue(isolate, v8Value, exceptionState);
+    if (exceptionState.hadException())
+      return;
+    impl.setDouble(cppValue);
+    return;
+  }
+}
+
+v8::Local<v8::Value> ToV8(const DoubleOrLongOrBooleanSequence& impl, v8::Local<v8::Object> creationContext, v8::Isolate* isolate) {
+  switch (impl.m_type) {
+    case DoubleOrLongOrBooleanSequence::SpecificTypeNone:
+      return v8::Null(isolate);
+    case DoubleOrLongOrBooleanSequence::SpecificTypeDouble:
+      return v8::Number::New(isolate, impl.getAsDouble());
+    case DoubleOrLongOrBooleanSequence::SpecificTypeLongOrBooleanSequence:
+      return ToV8(impl.getAsLongOrBooleanSequence(), creationContext, isolate);
+    default:
+      NOTREACHED();
+  }
+  return v8::Local<v8::Value>();
+}
+
+DoubleOrLongOrBooleanSequence NativeValueTraits<DoubleOrLongOrBooleanSequence>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
+  DoubleOrLongOrBooleanSequence impl;
+  V8DoubleOrLongOrBooleanSequence::toImpl(isolate, value, impl, UnionTypeConversionMode::NotNullable, exceptionState);
+  return impl;
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrLongOrBooleanSequence.h b/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrLongOrBooleanSequence.h
new file mode 100644
index 0000000..8ed5bc33
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrLongOrBooleanSequence.h
@@ -0,0 +1,96 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file has been auto-generated by code_generator_v8.py.
+// DO NOT MODIFY!
+
+// This file has been generated from the Jinja2 template in
+// third_party/WebKit/Source/bindings/templates/union_container.h.tmpl
+
+// clang-format off
+#ifndef DoubleOrLongOrBooleanSequence_h
+#define DoubleOrLongOrBooleanSequence_h
+
+#include "bindings/core/v8/Dictionary.h"
+#include "bindings/core/v8/ExceptionState.h"
+#include "bindings/core/v8/NativeValueTraits.h"
+#include "bindings/core/v8/V8Binding.h"
+#include "core/CoreExport.h"
+#include "platform/heap/Handle.h"
+
+namespace blink {
+
+class LongOrBoolean;
+
+class CORE_EXPORT DoubleOrLongOrBooleanSequence final {
+  DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ public:
+  DoubleOrLongOrBooleanSequence();
+  bool isNull() const { return m_type == SpecificTypeNone; }
+
+  bool isDouble() const { return m_type == SpecificTypeDouble; }
+  double getAsDouble() const;
+  void setDouble(double);
+  static DoubleOrLongOrBooleanSequence fromDouble(double);
+
+  bool isLongOrBooleanSequence() const { return m_type == SpecificTypeLongOrBooleanSequence; }
+  const HeapVector<LongOrBoolean>& getAsLongOrBooleanSequence() const;
+  void setLongOrBooleanSequence(const HeapVector<LongOrBoolean>&);
+  static DoubleOrLongOrBooleanSequence fromLongOrBooleanSequence(const HeapVector<LongOrBoolean>&);
+
+  DoubleOrLongOrBooleanSequence(const DoubleOrLongOrBooleanSequence&);
+  ~DoubleOrLongOrBooleanSequence();
+  DoubleOrLongOrBooleanSequence& operator=(const DoubleOrLongOrBooleanSequence&);
+  DECLARE_TRACE();
+
+ private:
+  enum SpecificTypes {
+    SpecificTypeNone,
+    SpecificTypeDouble,
+    SpecificTypeLongOrBooleanSequence,
+  };
+  SpecificTypes m_type;
+
+  double m_double;
+  HeapVector<LongOrBoolean> m_longOrBooleanSequence;
+
+  friend CORE_EXPORT v8::Local<v8::Value> ToV8(const DoubleOrLongOrBooleanSequence&, v8::Local<v8::Object>, v8::Isolate*);
+};
+
+class V8DoubleOrLongOrBooleanSequence final {
+ public:
+  CORE_EXPORT static void toImpl(v8::Isolate*, v8::Local<v8::Value>, DoubleOrLongOrBooleanSequence&, UnionTypeConversionMode, ExceptionState&);
+};
+
+CORE_EXPORT v8::Local<v8::Value> ToV8(const DoubleOrLongOrBooleanSequence&, v8::Local<v8::Object>, v8::Isolate*);
+
+template <class CallbackInfo>
+inline void v8SetReturnValue(const CallbackInfo& callbackInfo, DoubleOrLongOrBooleanSequence& impl) {
+  v8SetReturnValue(callbackInfo, ToV8(impl, callbackInfo.Holder(), callbackInfo.GetIsolate()));
+}
+
+template <class CallbackInfo>
+inline void v8SetReturnValue(const CallbackInfo& callbackInfo, DoubleOrLongOrBooleanSequence& impl, v8::Local<v8::Object> creationContext) {
+  v8SetReturnValue(callbackInfo, ToV8(impl, creationContext, callbackInfo.GetIsolate()));
+}
+
+template <>
+struct NativeValueTraits<DoubleOrLongOrBooleanSequence> : public NativeValueTraitsBase<DoubleOrLongOrBooleanSequence> {
+  CORE_EXPORT static DoubleOrLongOrBooleanSequence nativeValue(v8::Isolate*, v8::Local<v8::Value>, ExceptionState&);
+};
+
+template <>
+struct V8TypeOf<DoubleOrLongOrBooleanSequence> {
+  typedef V8DoubleOrLongOrBooleanSequence Type;
+};
+
+}  // namespace blink
+
+// We need to set canInitializeWithMemset=true because HeapVector supports
+// items that can initialize with memset or have a vtable. It is safe to
+// set canInitializeWithMemset=true for a union type object in practice.
+// See https://codereview.chromium.org/1118993002/#msg5 for more details.
+WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::DoubleOrLongOrBooleanSequence);
+
+#endif  // DoubleOrLongOrBooleanSequence_h
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrStringOrDoubleOrStringSequence.cpp b/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrStringOrDoubleOrStringSequence.cpp
index d7cc2ad..a6eef7f85 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrStringOrDoubleOrStringSequence.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrStringOrDoubleOrStringSequence.cpp
@@ -11,6 +11,7 @@
 // clang-format off
 #include "DoubleOrStringOrDoubleOrStringSequence.h"
 
+#include "bindings/core/v8/DoubleOrString.h"
 #include "bindings/core/v8/IDLTypes.h"
 #include "bindings/core/v8/NativeValueTraitsImpl.h"
 #include "bindings/core/v8/ToV8.h"
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrStringOrDoubleOrStringSequence.h b/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrStringOrDoubleOrStringSequence.h
index 4c725ef..6b723db 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrStringOrDoubleOrStringSequence.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrStringOrDoubleOrStringSequence.h
@@ -21,6 +21,8 @@
 
 namespace blink {
 
+class DoubleOrString;
+
 class CORE_EXPORT DoubleOrStringOrDoubleOrStringSequence final {
   DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
  public:
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/ElementSequenceOrByteStringDoubleOrStringRecord.cpp b/third_party/WebKit/Source/bindings/tests/results/core/ElementSequenceOrByteStringDoubleOrStringRecord.cpp
new file mode 100644
index 0000000..e511eaa
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/tests/results/core/ElementSequenceOrByteStringDoubleOrStringRecord.cpp
@@ -0,0 +1,118 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file has been auto-generated by code_generator_v8.py.
+// DO NOT MODIFY!
+
+// This file has been generated from the Jinja2 template in
+// third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl
+
+// clang-format off
+#include "ElementSequenceOrByteStringDoubleOrStringRecord.h"
+
+#include "bindings/core/v8/DoubleOrString.h"
+#include "bindings/core/v8/IDLTypes.h"
+#include "bindings/core/v8/NativeValueTraitsImpl.h"
+#include "bindings/core/v8/ToV8.h"
+#include "bindings/core/v8/V8Element.h"
+#include "core/animation/ElementAnimation.h"
+#include "core/dom/ChildNode.h"
+#include "core/dom/ElementFullscreen.h"
+#include "core/dom/NonDocumentTypeChildNode.h"
+#include "core/dom/ParentNode.h"
+
+namespace blink {
+
+ElementSequenceOrByteStringDoubleOrStringRecord::ElementSequenceOrByteStringDoubleOrStringRecord() : m_type(SpecificTypeNone) {}
+
+const HeapVector<std::pair<String, DoubleOrString>>& ElementSequenceOrByteStringDoubleOrStringRecord::getAsByteStringDoubleOrStringRecord() const {
+  DCHECK(isByteStringDoubleOrStringRecord());
+  return m_byteStringDoubleOrStringRecord;
+}
+
+void ElementSequenceOrByteStringDoubleOrStringRecord::setByteStringDoubleOrStringRecord(const HeapVector<std::pair<String, DoubleOrString>>& value) {
+  DCHECK(isNull());
+  m_byteStringDoubleOrStringRecord = value;
+  m_type = SpecificTypeByteStringDoubleOrStringRecord;
+}
+
+ElementSequenceOrByteStringDoubleOrStringRecord ElementSequenceOrByteStringDoubleOrStringRecord::fromByteStringDoubleOrStringRecord(const HeapVector<std::pair<String, DoubleOrString>>& value) {
+  ElementSequenceOrByteStringDoubleOrStringRecord container;
+  container.setByteStringDoubleOrStringRecord(value);
+  return container;
+}
+
+const HeapVector<Member<Element>>& ElementSequenceOrByteStringDoubleOrStringRecord::getAsElementSequence() const {
+  DCHECK(isElementSequence());
+  return m_elementSequence;
+}
+
+void ElementSequenceOrByteStringDoubleOrStringRecord::setElementSequence(const HeapVector<Member<Element>>& value) {
+  DCHECK(isNull());
+  m_elementSequence = value;
+  m_type = SpecificTypeElementSequence;
+}
+
+ElementSequenceOrByteStringDoubleOrStringRecord ElementSequenceOrByteStringDoubleOrStringRecord::fromElementSequence(const HeapVector<Member<Element>>& value) {
+  ElementSequenceOrByteStringDoubleOrStringRecord container;
+  container.setElementSequence(value);
+  return container;
+}
+
+ElementSequenceOrByteStringDoubleOrStringRecord::ElementSequenceOrByteStringDoubleOrStringRecord(const ElementSequenceOrByteStringDoubleOrStringRecord&) = default;
+ElementSequenceOrByteStringDoubleOrStringRecord::~ElementSequenceOrByteStringDoubleOrStringRecord() = default;
+ElementSequenceOrByteStringDoubleOrStringRecord& ElementSequenceOrByteStringDoubleOrStringRecord::operator=(const ElementSequenceOrByteStringDoubleOrStringRecord&) = default;
+
+DEFINE_TRACE(ElementSequenceOrByteStringDoubleOrStringRecord) {
+  visitor->trace(m_byteStringDoubleOrStringRecord);
+  visitor->trace(m_elementSequence);
+}
+
+void V8ElementSequenceOrByteStringDoubleOrStringRecord::toImpl(v8::Isolate* isolate, v8::Local<v8::Value> v8Value, ElementSequenceOrByteStringDoubleOrStringRecord& impl, UnionTypeConversionMode conversionMode, ExceptionState& exceptionState) {
+  if (v8Value.IsEmpty())
+    return;
+
+  if (conversionMode == UnionTypeConversionMode::Nullable && isUndefinedOrNull(v8Value))
+    return;
+
+  if (v8Value->IsArray()) {
+    HeapVector<Member<Element>> cppValue = toMemberNativeArray<Element>(v8Value, 0, isolate, exceptionState);
+    if (exceptionState.hadException())
+      return;
+    impl.setElementSequence(cppValue);
+    return;
+  }
+
+  if (v8Value->IsObject()) {
+    HeapVector<std::pair<String, DoubleOrString>> cppValue = NativeValueTraits<IDLRecord<IDLByteString, DoubleOrString>>::nativeValue(isolate, v8Value, exceptionState);
+    if (exceptionState.hadException())
+      return;
+    impl.setByteStringDoubleOrStringRecord(cppValue);
+    return;
+  }
+
+  exceptionState.throwTypeError("The provided value is not of type '(sequence<Element> or record<ByteString, (double or DOMString)>)'");
+}
+
+v8::Local<v8::Value> ToV8(const ElementSequenceOrByteStringDoubleOrStringRecord& impl, v8::Local<v8::Object> creationContext, v8::Isolate* isolate) {
+  switch (impl.m_type) {
+    case ElementSequenceOrByteStringDoubleOrStringRecord::SpecificTypeNone:
+      return v8::Null(isolate);
+    case ElementSequenceOrByteStringDoubleOrStringRecord::SpecificTypeByteStringDoubleOrStringRecord:
+      return ToV8(impl.getAsByteStringDoubleOrStringRecord(), creationContext, isolate);
+    case ElementSequenceOrByteStringDoubleOrStringRecord::SpecificTypeElementSequence:
+      return ToV8(impl.getAsElementSequence(), creationContext, isolate);
+    default:
+      NOTREACHED();
+  }
+  return v8::Local<v8::Value>();
+}
+
+ElementSequenceOrByteStringDoubleOrStringRecord NativeValueTraits<ElementSequenceOrByteStringDoubleOrStringRecord>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
+  ElementSequenceOrByteStringDoubleOrStringRecord impl;
+  V8ElementSequenceOrByteStringDoubleOrStringRecord::toImpl(isolate, value, impl, UnionTypeConversionMode::NotNullable, exceptionState);
+  return impl;
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/ElementSequenceOrByteStringDoubleOrStringRecord.h b/third_party/WebKit/Source/bindings/tests/results/core/ElementSequenceOrByteStringDoubleOrStringRecord.h
new file mode 100644
index 0000000..29775c1
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/tests/results/core/ElementSequenceOrByteStringDoubleOrStringRecord.h
@@ -0,0 +1,97 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file has been auto-generated by code_generator_v8.py.
+// DO NOT MODIFY!
+
+// This file has been generated from the Jinja2 template in
+// third_party/WebKit/Source/bindings/templates/union_container.h.tmpl
+
+// clang-format off
+#ifndef ElementSequenceOrByteStringDoubleOrStringRecord_h
+#define ElementSequenceOrByteStringDoubleOrStringRecord_h
+
+#include "bindings/core/v8/Dictionary.h"
+#include "bindings/core/v8/ExceptionState.h"
+#include "bindings/core/v8/NativeValueTraits.h"
+#include "bindings/core/v8/V8Binding.h"
+#include "core/CoreExport.h"
+#include "platform/heap/Handle.h"
+
+namespace blink {
+
+class DoubleOrString;
+class Element;
+
+class CORE_EXPORT ElementSequenceOrByteStringDoubleOrStringRecord final {
+  DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ public:
+  ElementSequenceOrByteStringDoubleOrStringRecord();
+  bool isNull() const { return m_type == SpecificTypeNone; }
+
+  bool isByteStringDoubleOrStringRecord() const { return m_type == SpecificTypeByteStringDoubleOrStringRecord; }
+  const HeapVector<std::pair<String, DoubleOrString>>& getAsByteStringDoubleOrStringRecord() const;
+  void setByteStringDoubleOrStringRecord(const HeapVector<std::pair<String, DoubleOrString>>&);
+  static ElementSequenceOrByteStringDoubleOrStringRecord fromByteStringDoubleOrStringRecord(const HeapVector<std::pair<String, DoubleOrString>>&);
+
+  bool isElementSequence() const { return m_type == SpecificTypeElementSequence; }
+  const HeapVector<Member<Element>>& getAsElementSequence() const;
+  void setElementSequence(const HeapVector<Member<Element>>&);
+  static ElementSequenceOrByteStringDoubleOrStringRecord fromElementSequence(const HeapVector<Member<Element>>&);
+
+  ElementSequenceOrByteStringDoubleOrStringRecord(const ElementSequenceOrByteStringDoubleOrStringRecord&);
+  ~ElementSequenceOrByteStringDoubleOrStringRecord();
+  ElementSequenceOrByteStringDoubleOrStringRecord& operator=(const ElementSequenceOrByteStringDoubleOrStringRecord&);
+  DECLARE_TRACE();
+
+ private:
+  enum SpecificTypes {
+    SpecificTypeNone,
+    SpecificTypeByteStringDoubleOrStringRecord,
+    SpecificTypeElementSequence,
+  };
+  SpecificTypes m_type;
+
+  HeapVector<std::pair<String, DoubleOrString>> m_byteStringDoubleOrStringRecord;
+  HeapVector<Member<Element>> m_elementSequence;
+
+  friend CORE_EXPORT v8::Local<v8::Value> ToV8(const ElementSequenceOrByteStringDoubleOrStringRecord&, v8::Local<v8::Object>, v8::Isolate*);
+};
+
+class V8ElementSequenceOrByteStringDoubleOrStringRecord final {
+ public:
+  CORE_EXPORT static void toImpl(v8::Isolate*, v8::Local<v8::Value>, ElementSequenceOrByteStringDoubleOrStringRecord&, UnionTypeConversionMode, ExceptionState&);
+};
+
+CORE_EXPORT v8::Local<v8::Value> ToV8(const ElementSequenceOrByteStringDoubleOrStringRecord&, v8::Local<v8::Object>, v8::Isolate*);
+
+template <class CallbackInfo>
+inline void v8SetReturnValue(const CallbackInfo& callbackInfo, ElementSequenceOrByteStringDoubleOrStringRecord& impl) {
+  v8SetReturnValue(callbackInfo, ToV8(impl, callbackInfo.Holder(), callbackInfo.GetIsolate()));
+}
+
+template <class CallbackInfo>
+inline void v8SetReturnValue(const CallbackInfo& callbackInfo, ElementSequenceOrByteStringDoubleOrStringRecord& impl, v8::Local<v8::Object> creationContext) {
+  v8SetReturnValue(callbackInfo, ToV8(impl, creationContext, callbackInfo.GetIsolate()));
+}
+
+template <>
+struct NativeValueTraits<ElementSequenceOrByteStringDoubleOrStringRecord> : public NativeValueTraitsBase<ElementSequenceOrByteStringDoubleOrStringRecord> {
+  CORE_EXPORT static ElementSequenceOrByteStringDoubleOrStringRecord nativeValue(v8::Isolate*, v8::Local<v8::Value>, ExceptionState&);
+};
+
+template <>
+struct V8TypeOf<ElementSequenceOrByteStringDoubleOrStringRecord> {
+  typedef V8ElementSequenceOrByteStringDoubleOrStringRecord Type;
+};
+
+}  // namespace blink
+
+// We need to set canInitializeWithMemset=true because HeapVector supports
+// items that can initialize with memset or have a vtable. It is safe to
+// set canInitializeWithMemset=true for a union type object in practice.
+// See https://codereview.chromium.org/1118993002/#msg5 for more details.
+WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::ElementSequenceOrByteStringDoubleOrStringRecord);
+
+#endif  // ElementSequenceOrByteStringDoubleOrStringRecord_h
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/FloatOrBoolean.cpp b/third_party/WebKit/Source/bindings/tests/results/core/FloatOrBoolean.cpp
new file mode 100644
index 0000000..b42f06f7
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/tests/results/core/FloatOrBoolean.cpp
@@ -0,0 +1,112 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file has been auto-generated by code_generator_v8.py.
+// DO NOT MODIFY!
+
+// This file has been generated from the Jinja2 template in
+// third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl
+
+// clang-format off
+#include "FloatOrBoolean.h"
+
+#include "bindings/core/v8/IDLTypes.h"
+#include "bindings/core/v8/NativeValueTraitsImpl.h"
+#include "bindings/core/v8/ToV8.h"
+
+namespace blink {
+
+FloatOrBoolean::FloatOrBoolean() : m_type(SpecificTypeNone) {}
+
+bool FloatOrBoolean::getAsBoolean() const {
+  DCHECK(isBoolean());
+  return m_boolean;
+}
+
+void FloatOrBoolean::setBoolean(bool value) {
+  DCHECK(isNull());
+  m_boolean = value;
+  m_type = SpecificTypeBoolean;
+}
+
+FloatOrBoolean FloatOrBoolean::fromBoolean(bool value) {
+  FloatOrBoolean container;
+  container.setBoolean(value);
+  return container;
+}
+
+float FloatOrBoolean::getAsFloat() const {
+  DCHECK(isFloat());
+  return m_float;
+}
+
+void FloatOrBoolean::setFloat(float value) {
+  DCHECK(isNull());
+  m_float = value;
+  m_type = SpecificTypeFloat;
+}
+
+FloatOrBoolean FloatOrBoolean::fromFloat(float value) {
+  FloatOrBoolean container;
+  container.setFloat(value);
+  return container;
+}
+
+FloatOrBoolean::FloatOrBoolean(const FloatOrBoolean&) = default;
+FloatOrBoolean::~FloatOrBoolean() = default;
+FloatOrBoolean& FloatOrBoolean::operator=(const FloatOrBoolean&) = default;
+
+DEFINE_TRACE(FloatOrBoolean) {
+}
+
+void V8FloatOrBoolean::toImpl(v8::Isolate* isolate, v8::Local<v8::Value> v8Value, FloatOrBoolean& impl, UnionTypeConversionMode conversionMode, ExceptionState& exceptionState) {
+  if (v8Value.IsEmpty())
+    return;
+
+  if (conversionMode == UnionTypeConversionMode::Nullable && isUndefinedOrNull(v8Value))
+    return;
+
+  if (v8Value->IsBoolean()) {
+    impl.setBoolean(v8Value.As<v8::Boolean>()->Value());
+    return;
+  }
+
+  if (v8Value->IsNumber()) {
+    float cppValue = NativeValueTraits<IDLFloat>::nativeValue(isolate, v8Value, exceptionState);
+    if (exceptionState.hadException())
+      return;
+    impl.setFloat(cppValue);
+    return;
+  }
+
+  {
+    float cppValue = NativeValueTraits<IDLFloat>::nativeValue(isolate, v8Value, exceptionState);
+    if (exceptionState.hadException())
+      return;
+    impl.setFloat(cppValue);
+    return;
+  }
+}
+
+v8::Local<v8::Value> ToV8(const FloatOrBoolean& impl, v8::Local<v8::Object> creationContext, v8::Isolate* isolate) {
+  switch (impl.m_type) {
+    case FloatOrBoolean::SpecificTypeNone:
+      return v8::Null(isolate);
+    case FloatOrBoolean::SpecificTypeBoolean:
+      return v8Boolean(impl.getAsBoolean(), isolate);
+    case FloatOrBoolean::SpecificTypeFloat:
+      return v8::Number::New(isolate, impl.getAsFloat());
+    default:
+      NOTREACHED();
+  }
+  return v8::Local<v8::Value>();
+}
+
+FloatOrBoolean NativeValueTraits<FloatOrBoolean>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
+  FloatOrBoolean impl;
+  V8FloatOrBoolean::toImpl(isolate, value, impl, UnionTypeConversionMode::NotNullable, exceptionState);
+  return impl;
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/FloatOrBoolean.h b/third_party/WebKit/Source/bindings/tests/results/core/FloatOrBoolean.h
new file mode 100644
index 0000000..2402414
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/tests/results/core/FloatOrBoolean.h
@@ -0,0 +1,94 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file has been auto-generated by code_generator_v8.py.
+// DO NOT MODIFY!
+
+// This file has been generated from the Jinja2 template in
+// third_party/WebKit/Source/bindings/templates/union_container.h.tmpl
+
+// clang-format off
+#ifndef FloatOrBoolean_h
+#define FloatOrBoolean_h
+
+#include "bindings/core/v8/Dictionary.h"
+#include "bindings/core/v8/ExceptionState.h"
+#include "bindings/core/v8/NativeValueTraits.h"
+#include "bindings/core/v8/V8Binding.h"
+#include "core/CoreExport.h"
+#include "platform/heap/Handle.h"
+
+namespace blink {
+
+class CORE_EXPORT FloatOrBoolean final {
+  DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ public:
+  FloatOrBoolean();
+  bool isNull() const { return m_type == SpecificTypeNone; }
+
+  bool isBoolean() const { return m_type == SpecificTypeBoolean; }
+  bool getAsBoolean() const;
+  void setBoolean(bool);
+  static FloatOrBoolean fromBoolean(bool);
+
+  bool isFloat() const { return m_type == SpecificTypeFloat; }
+  float getAsFloat() const;
+  void setFloat(float);
+  static FloatOrBoolean fromFloat(float);
+
+  FloatOrBoolean(const FloatOrBoolean&);
+  ~FloatOrBoolean();
+  FloatOrBoolean& operator=(const FloatOrBoolean&);
+  DECLARE_TRACE();
+
+ private:
+  enum SpecificTypes {
+    SpecificTypeNone,
+    SpecificTypeBoolean,
+    SpecificTypeFloat,
+  };
+  SpecificTypes m_type;
+
+  bool m_boolean;
+  float m_float;
+
+  friend CORE_EXPORT v8::Local<v8::Value> ToV8(const FloatOrBoolean&, v8::Local<v8::Object>, v8::Isolate*);
+};
+
+class V8FloatOrBoolean final {
+ public:
+  CORE_EXPORT static void toImpl(v8::Isolate*, v8::Local<v8::Value>, FloatOrBoolean&, UnionTypeConversionMode, ExceptionState&);
+};
+
+CORE_EXPORT v8::Local<v8::Value> ToV8(const FloatOrBoolean&, v8::Local<v8::Object>, v8::Isolate*);
+
+template <class CallbackInfo>
+inline void v8SetReturnValue(const CallbackInfo& callbackInfo, FloatOrBoolean& impl) {
+  v8SetReturnValue(callbackInfo, ToV8(impl, callbackInfo.Holder(), callbackInfo.GetIsolate()));
+}
+
+template <class CallbackInfo>
+inline void v8SetReturnValue(const CallbackInfo& callbackInfo, FloatOrBoolean& impl, v8::Local<v8::Object> creationContext) {
+  v8SetReturnValue(callbackInfo, ToV8(impl, creationContext, callbackInfo.GetIsolate()));
+}
+
+template <>
+struct NativeValueTraits<FloatOrBoolean> : public NativeValueTraitsBase<FloatOrBoolean> {
+  CORE_EXPORT static FloatOrBoolean nativeValue(v8::Isolate*, v8::Local<v8::Value>, ExceptionState&);
+};
+
+template <>
+struct V8TypeOf<FloatOrBoolean> {
+  typedef V8FloatOrBoolean Type;
+};
+
+}  // namespace blink
+
+// We need to set canInitializeWithMemset=true because HeapVector supports
+// items that can initialize with memset or have a vtable. It is safe to
+// set canInitializeWithMemset=true for a union type object in practice.
+// See https://codereview.chromium.org/1118993002/#msg5 for more details.
+WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::FloatOrBoolean);
+
+#endif  // FloatOrBoolean_h
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/LongOrBoolean.cpp b/third_party/WebKit/Source/bindings/tests/results/core/LongOrBoolean.cpp
new file mode 100644
index 0000000..efcfd08
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/tests/results/core/LongOrBoolean.cpp
@@ -0,0 +1,112 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file has been auto-generated by code_generator_v8.py.
+// DO NOT MODIFY!
+
+// This file has been generated from the Jinja2 template in
+// third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl
+
+// clang-format off
+#include "LongOrBoolean.h"
+
+#include "bindings/core/v8/IDLTypes.h"
+#include "bindings/core/v8/NativeValueTraitsImpl.h"
+#include "bindings/core/v8/ToV8.h"
+
+namespace blink {
+
+LongOrBoolean::LongOrBoolean() : m_type(SpecificTypeNone) {}
+
+bool LongOrBoolean::getAsBoolean() const {
+  DCHECK(isBoolean());
+  return m_boolean;
+}
+
+void LongOrBoolean::setBoolean(bool value) {
+  DCHECK(isNull());
+  m_boolean = value;
+  m_type = SpecificTypeBoolean;
+}
+
+LongOrBoolean LongOrBoolean::fromBoolean(bool value) {
+  LongOrBoolean container;
+  container.setBoolean(value);
+  return container;
+}
+
+int32_t LongOrBoolean::getAsLong() const {
+  DCHECK(isLong());
+  return m_long;
+}
+
+void LongOrBoolean::setLong(int32_t value) {
+  DCHECK(isNull());
+  m_long = value;
+  m_type = SpecificTypeLong;
+}
+
+LongOrBoolean LongOrBoolean::fromLong(int32_t value) {
+  LongOrBoolean container;
+  container.setLong(value);
+  return container;
+}
+
+LongOrBoolean::LongOrBoolean(const LongOrBoolean&) = default;
+LongOrBoolean::~LongOrBoolean() = default;
+LongOrBoolean& LongOrBoolean::operator=(const LongOrBoolean&) = default;
+
+DEFINE_TRACE(LongOrBoolean) {
+}
+
+void V8LongOrBoolean::toImpl(v8::Isolate* isolate, v8::Local<v8::Value> v8Value, LongOrBoolean& impl, UnionTypeConversionMode conversionMode, ExceptionState& exceptionState) {
+  if (v8Value.IsEmpty())
+    return;
+
+  if (conversionMode == UnionTypeConversionMode::Nullable && isUndefinedOrNull(v8Value))
+    return;
+
+  if (v8Value->IsBoolean()) {
+    impl.setBoolean(v8Value.As<v8::Boolean>()->Value());
+    return;
+  }
+
+  if (v8Value->IsNumber()) {
+    int32_t cppValue = NativeValueTraits<IDLLong>::nativeValue(isolate, v8Value, exceptionState, NormalConversion);
+    if (exceptionState.hadException())
+      return;
+    impl.setLong(cppValue);
+    return;
+  }
+
+  {
+    int32_t cppValue = NativeValueTraits<IDLLong>::nativeValue(isolate, v8Value, exceptionState, NormalConversion);
+    if (exceptionState.hadException())
+      return;
+    impl.setLong(cppValue);
+    return;
+  }
+}
+
+v8::Local<v8::Value> ToV8(const LongOrBoolean& impl, v8::Local<v8::Object> creationContext, v8::Isolate* isolate) {
+  switch (impl.m_type) {
+    case LongOrBoolean::SpecificTypeNone:
+      return v8::Null(isolate);
+    case LongOrBoolean::SpecificTypeBoolean:
+      return v8Boolean(impl.getAsBoolean(), isolate);
+    case LongOrBoolean::SpecificTypeLong:
+      return v8::Integer::New(isolate, impl.getAsLong());
+    default:
+      NOTREACHED();
+  }
+  return v8::Local<v8::Value>();
+}
+
+LongOrBoolean NativeValueTraits<LongOrBoolean>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
+  LongOrBoolean impl;
+  V8LongOrBoolean::toImpl(isolate, value, impl, UnionTypeConversionMode::NotNullable, exceptionState);
+  return impl;
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/LongOrBoolean.h b/third_party/WebKit/Source/bindings/tests/results/core/LongOrBoolean.h
new file mode 100644
index 0000000..689e050
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/tests/results/core/LongOrBoolean.h
@@ -0,0 +1,94 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file has been auto-generated by code_generator_v8.py.
+// DO NOT MODIFY!
+
+// This file has been generated from the Jinja2 template in
+// third_party/WebKit/Source/bindings/templates/union_container.h.tmpl
+
+// clang-format off
+#ifndef LongOrBoolean_h
+#define LongOrBoolean_h
+
+#include "bindings/core/v8/Dictionary.h"
+#include "bindings/core/v8/ExceptionState.h"
+#include "bindings/core/v8/NativeValueTraits.h"
+#include "bindings/core/v8/V8Binding.h"
+#include "core/CoreExport.h"
+#include "platform/heap/Handle.h"
+
+namespace blink {
+
+class CORE_EXPORT LongOrBoolean final {
+  DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ public:
+  LongOrBoolean();
+  bool isNull() const { return m_type == SpecificTypeNone; }
+
+  bool isBoolean() const { return m_type == SpecificTypeBoolean; }
+  bool getAsBoolean() const;
+  void setBoolean(bool);
+  static LongOrBoolean fromBoolean(bool);
+
+  bool isLong() const { return m_type == SpecificTypeLong; }
+  int32_t getAsLong() const;
+  void setLong(int32_t);
+  static LongOrBoolean fromLong(int32_t);
+
+  LongOrBoolean(const LongOrBoolean&);
+  ~LongOrBoolean();
+  LongOrBoolean& operator=(const LongOrBoolean&);
+  DECLARE_TRACE();
+
+ private:
+  enum SpecificTypes {
+    SpecificTypeNone,
+    SpecificTypeBoolean,
+    SpecificTypeLong,
+  };
+  SpecificTypes m_type;
+
+  bool m_boolean;
+  int32_t m_long;
+
+  friend CORE_EXPORT v8::Local<v8::Value> ToV8(const LongOrBoolean&, v8::Local<v8::Object>, v8::Isolate*);
+};
+
+class V8LongOrBoolean final {
+ public:
+  CORE_EXPORT static void toImpl(v8::Isolate*, v8::Local<v8::Value>, LongOrBoolean&, UnionTypeConversionMode, ExceptionState&);
+};
+
+CORE_EXPORT v8::Local<v8::Value> ToV8(const LongOrBoolean&, v8::Local<v8::Object>, v8::Isolate*);
+
+template <class CallbackInfo>
+inline void v8SetReturnValue(const CallbackInfo& callbackInfo, LongOrBoolean& impl) {
+  v8SetReturnValue(callbackInfo, ToV8(impl, callbackInfo.Holder(), callbackInfo.GetIsolate()));
+}
+
+template <class CallbackInfo>
+inline void v8SetReturnValue(const CallbackInfo& callbackInfo, LongOrBoolean& impl, v8::Local<v8::Object> creationContext) {
+  v8SetReturnValue(callbackInfo, ToV8(impl, creationContext, callbackInfo.GetIsolate()));
+}
+
+template <>
+struct NativeValueTraits<LongOrBoolean> : public NativeValueTraitsBase<LongOrBoolean> {
+  CORE_EXPORT static LongOrBoolean nativeValue(v8::Isolate*, v8::Local<v8::Value>, ExceptionState&);
+};
+
+template <>
+struct V8TypeOf<LongOrBoolean> {
+  typedef V8LongOrBoolean Type;
+};
+
+}  // namespace blink
+
+// We need to set canInitializeWithMemset=true because HeapVector supports
+// items that can initialize with memset or have a vtable. It is safe to
+// set canInitializeWithMemset=true for a union type object in practice.
+// See https://codereview.chromium.org/1118993002/#msg5 for more details.
+WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::LongOrBoolean);
+
+#endif  // LongOrBoolean_h
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/LongOrTestDictionary.cpp b/third_party/WebKit/Source/bindings/tests/results/core/LongOrTestDictionary.cpp
index 8a7037d..08e3311 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/LongOrTestDictionary.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/LongOrTestDictionary.cpp
@@ -11,10 +11,8 @@
 // clang-format off
 #include "LongOrTestDictionary.h"
 
-#include "bindings/core/v8/DoubleOrString.h"
 #include "bindings/core/v8/IDLTypes.h"
 #include "bindings/core/v8/NativeValueTraitsImpl.h"
-#include "bindings/core/v8/TestInterface2OrUint8Array.h"
 #include "bindings/core/v8/ToV8.h"
 
 namespace blink {
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord.cpp b/third_party/WebKit/Source/bindings/tests/results/core/NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord.cpp
index 1e8f7ec..228e52d 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord.cpp
@@ -11,7 +11,7 @@
 // clang-format off
 #include "NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord.h"
 
-#include "bindings/core/v8/ArrayBufferOrArrayBufferViewOrBlobOrDocumentOrStringOrFormDataOrURLSearchParams.h"
+#include "bindings/core/v8/ByteStringOrNodeList.h"
 #include "bindings/core/v8/IDLTypes.h"
 #include "bindings/core/v8/NativeValueTraitsImpl.h"
 #include "bindings/core/v8/ToV8.h"
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord.h b/third_party/WebKit/Source/bindings/tests/results/core/NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord.h
index 6f9239c..9557458b 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord.h
@@ -14,20 +14,14 @@
 
 #include "bindings/core/v8/Dictionary.h"
 #include "bindings/core/v8/ExceptionState.h"
-#include "bindings/core/v8/IDLTypes.h"
 #include "bindings/core/v8/NativeValueTraits.h"
-#include "bindings/core/v8/NativeValueTraitsImpl.h"
 #include "bindings/core/v8/V8Binding.h"
-#include "bindings/core/v8/V8NodeList.h"
 #include "core/CoreExport.h"
-#include "core/dom/NameNodeList.h"
-#include "core/dom/NodeList.h"
-#include "core/dom/StaticNodeList.h"
-#include "core/html/LabelsNodeList.h"
 #include "platform/heap/Handle.h"
 
 namespace blink {
 
+class ByteStringOrNodeList;
 class Event;
 class Node;
 class XMLHttpRequest;
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.cpp b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.cpp
index 75e4953..e8357a8 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.cpp
@@ -11,8 +11,6 @@
 // clang-format off
 #include "TestDictionary.h"
 
-#include "bindings/core/v8/DoubleOrString.h"
-#include "bindings/core/v8/TestInterface2OrUint8Array.h"
 #include "bindings/tests/idls/core/TestInterfaceGarbageCollected.h"
 #include "bindings/tests/idls/core/TestInterfaceImplementation.h"
 #include "bindings/tests/idls/core/TestObject.h"
@@ -397,6 +395,15 @@
 void TestDictionary::setUint8ArrayMember(DOMUint8Array* value) {
   m_uint8ArrayMember = value;
 }
+bool TestDictionary::hasUnionWithTypedefs() const {
+  return !m_unionWithTypedefs.isNull();
+}
+const FloatOrBoolean& TestDictionary::unionWithTypedefs() const {
+  return m_unionWithTypedefs;
+}
+void TestDictionary::setUnionWithTypedefs(const FloatOrBoolean& value) {
+  m_unionWithTypedefs = value;
+}
 bool TestDictionary::hasUnrestrictedDoubleMember() const {
   return m_hasUnrestrictedDoubleMember;
 }
@@ -426,6 +433,7 @@
   visitor->trace(m_testInterfaceSequenceMember);
   visitor->trace(m_testObjectSequenceMember);
   visitor->trace(m_uint8ArrayMember);
+  visitor->trace(m_unionWithTypedefs);
   IDLDictionaryBase::trace(visitor);
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h
index 2f925294..e88e977 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h
@@ -14,6 +14,7 @@
 
 #include "bindings/core/v8/Dictionary.h"
 #include "bindings/core/v8/DoubleOrString.h"
+#include "bindings/core/v8/FloatOrBoolean.h"
 #include "bindings/core/v8/IDLDictionaryBase.h"
 #include "bindings/core/v8/ScriptValue.h"
 #include "bindings/core/v8/TestInterface2OrUint8Array.h"
@@ -183,6 +184,10 @@
   DOMUint8Array* uint8ArrayMember() const;
   void setUint8ArrayMember(DOMUint8Array*);
 
+  bool hasUnionWithTypedefs() const;
+  const FloatOrBoolean& unionWithTypedefs() const;
+  void setUnionWithTypedefs(const FloatOrBoolean&);
+
   bool hasUnrestrictedDoubleMember() const;
   double unrestrictedDoubleMember() const;
   void setUnrestrictedDoubleMember(double);
@@ -241,6 +246,7 @@
   bool m_hasTestObjectSequenceMember = false;
   HeapVector<Member<TestObject>> m_testObjectSequenceMember;
   Member<DOMUint8Array> m_uint8ArrayMember;
+  FloatOrBoolean m_unionWithTypedefs;
   bool m_hasUnrestrictedDoubleMember = false;
   double m_unrestrictedDoubleMember;
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/UnsignedLongLongOrBooleanOrTestCallbackInterface.cpp b/third_party/WebKit/Source/bindings/tests/results/core/UnsignedLongLongOrBooleanOrTestCallbackInterface.cpp
new file mode 100644
index 0000000..c19e694
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/tests/results/core/UnsignedLongLongOrBooleanOrTestCallbackInterface.cpp
@@ -0,0 +1,139 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file has been auto-generated by code_generator_v8.py.
+// DO NOT MODIFY!
+
+// This file has been generated from the Jinja2 template in
+// third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl
+
+// clang-format off
+#include "UnsignedLongLongOrBooleanOrTestCallbackInterface.h"
+
+#include "bindings/core/v8/IDLTypes.h"
+#include "bindings/core/v8/NativeValueTraitsImpl.h"
+#include "bindings/core/v8/ToV8.h"
+#include "bindings/core/v8/V8TestCallbackInterface.h"
+
+namespace blink {
+
+UnsignedLongLongOrBooleanOrTestCallbackInterface::UnsignedLongLongOrBooleanOrTestCallbackInterface() : m_type(SpecificTypeNone) {}
+
+bool UnsignedLongLongOrBooleanOrTestCallbackInterface::getAsBoolean() const {
+  DCHECK(isBoolean());
+  return m_boolean;
+}
+
+void UnsignedLongLongOrBooleanOrTestCallbackInterface::setBoolean(bool value) {
+  DCHECK(isNull());
+  m_boolean = value;
+  m_type = SpecificTypeBoolean;
+}
+
+UnsignedLongLongOrBooleanOrTestCallbackInterface UnsignedLongLongOrBooleanOrTestCallbackInterface::fromBoolean(bool value) {
+  UnsignedLongLongOrBooleanOrTestCallbackInterface container;
+  container.setBoolean(value);
+  return container;
+}
+
+TestCallbackInterface* UnsignedLongLongOrBooleanOrTestCallbackInterface::getAsTestCallbackInterface() const {
+  DCHECK(isTestCallbackInterface());
+  return m_testCallbackInterface;
+}
+
+void UnsignedLongLongOrBooleanOrTestCallbackInterface::setTestCallbackInterface(TestCallbackInterface* value) {
+  DCHECK(isNull());
+  m_testCallbackInterface = value;
+  m_type = SpecificTypeTestCallbackInterface;
+}
+
+UnsignedLongLongOrBooleanOrTestCallbackInterface UnsignedLongLongOrBooleanOrTestCallbackInterface::fromTestCallbackInterface(TestCallbackInterface* value) {
+  UnsignedLongLongOrBooleanOrTestCallbackInterface container;
+  container.setTestCallbackInterface(value);
+  return container;
+}
+
+uint64_t UnsignedLongLongOrBooleanOrTestCallbackInterface::getAsUnsignedLongLong() const {
+  DCHECK(isUnsignedLongLong());
+  return m_unsignedLongLong;
+}
+
+void UnsignedLongLongOrBooleanOrTestCallbackInterface::setUnsignedLongLong(uint64_t value) {
+  DCHECK(isNull());
+  m_unsignedLongLong = value;
+  m_type = SpecificTypeUnsignedLongLong;
+}
+
+UnsignedLongLongOrBooleanOrTestCallbackInterface UnsignedLongLongOrBooleanOrTestCallbackInterface::fromUnsignedLongLong(uint64_t value) {
+  UnsignedLongLongOrBooleanOrTestCallbackInterface container;
+  container.setUnsignedLongLong(value);
+  return container;
+}
+
+UnsignedLongLongOrBooleanOrTestCallbackInterface::UnsignedLongLongOrBooleanOrTestCallbackInterface(const UnsignedLongLongOrBooleanOrTestCallbackInterface&) = default;
+UnsignedLongLongOrBooleanOrTestCallbackInterface::~UnsignedLongLongOrBooleanOrTestCallbackInterface() = default;
+UnsignedLongLongOrBooleanOrTestCallbackInterface& UnsignedLongLongOrBooleanOrTestCallbackInterface::operator=(const UnsignedLongLongOrBooleanOrTestCallbackInterface&) = default;
+
+DEFINE_TRACE(UnsignedLongLongOrBooleanOrTestCallbackInterface) {
+  visitor->trace(m_testCallbackInterface);
+}
+
+void V8UnsignedLongLongOrBooleanOrTestCallbackInterface::toImpl(v8::Isolate* isolate, v8::Local<v8::Value> v8Value, UnsignedLongLongOrBooleanOrTestCallbackInterface& impl, UnionTypeConversionMode conversionMode, ExceptionState& exceptionState) {
+  if (v8Value.IsEmpty())
+    return;
+
+  if (conversionMode == UnionTypeConversionMode::Nullable && isUndefinedOrNull(v8Value))
+    return;
+
+  if (V8TestCallbackInterface::hasInstance(v8Value, isolate)) {
+    TestCallbackInterface* cppValue = V8TestCallbackInterface::toImpl(v8::Local<v8::Object>::Cast(v8Value));
+    impl.setTestCallbackInterface(cppValue);
+    return;
+  }
+
+  if (v8Value->IsBoolean()) {
+    impl.setBoolean(v8Value.As<v8::Boolean>()->Value());
+    return;
+  }
+
+  if (v8Value->IsNumber()) {
+    uint64_t cppValue = NativeValueTraits<IDLUnsignedLongLong>::nativeValue(isolate, v8Value, exceptionState, NormalConversion);
+    if (exceptionState.hadException())
+      return;
+    impl.setUnsignedLongLong(cppValue);
+    return;
+  }
+
+  {
+    uint64_t cppValue = NativeValueTraits<IDLUnsignedLongLong>::nativeValue(isolate, v8Value, exceptionState, NormalConversion);
+    if (exceptionState.hadException())
+      return;
+    impl.setUnsignedLongLong(cppValue);
+    return;
+  }
+}
+
+v8::Local<v8::Value> ToV8(const UnsignedLongLongOrBooleanOrTestCallbackInterface& impl, v8::Local<v8::Object> creationContext, v8::Isolate* isolate) {
+  switch (impl.m_type) {
+    case UnsignedLongLongOrBooleanOrTestCallbackInterface::SpecificTypeNone:
+      return v8::Null(isolate);
+    case UnsignedLongLongOrBooleanOrTestCallbackInterface::SpecificTypeBoolean:
+      return v8Boolean(impl.getAsBoolean(), isolate);
+    case UnsignedLongLongOrBooleanOrTestCallbackInterface::SpecificTypeTestCallbackInterface:
+      return ToV8(impl.getAsTestCallbackInterface(), creationContext, isolate);
+    case UnsignedLongLongOrBooleanOrTestCallbackInterface::SpecificTypeUnsignedLongLong:
+      return v8::Number::New(isolate, static_cast<double>(impl.getAsUnsignedLongLong()));
+    default:
+      NOTREACHED();
+  }
+  return v8::Local<v8::Value>();
+}
+
+UnsignedLongLongOrBooleanOrTestCallbackInterface NativeValueTraits<UnsignedLongLongOrBooleanOrTestCallbackInterface>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
+  UnsignedLongLongOrBooleanOrTestCallbackInterface impl;
+  V8UnsignedLongLongOrBooleanOrTestCallbackInterface::toImpl(isolate, value, impl, UnionTypeConversionMode::NotNullable, exceptionState);
+  return impl;
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/UnsignedLongLongOrBooleanOrTestCallbackInterface.h b/third_party/WebKit/Source/bindings/tests/results/core/UnsignedLongLongOrBooleanOrTestCallbackInterface.h
new file mode 100644
index 0000000..8399452
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/tests/results/core/UnsignedLongLongOrBooleanOrTestCallbackInterface.h
@@ -0,0 +1,103 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file has been auto-generated by code_generator_v8.py.
+// DO NOT MODIFY!
+
+// This file has been generated from the Jinja2 template in
+// third_party/WebKit/Source/bindings/templates/union_container.h.tmpl
+
+// clang-format off
+#ifndef UnsignedLongLongOrBooleanOrTestCallbackInterface_h
+#define UnsignedLongLongOrBooleanOrTestCallbackInterface_h
+
+#include "bindings/core/v8/Dictionary.h"
+#include "bindings/core/v8/ExceptionState.h"
+#include "bindings/core/v8/NativeValueTraits.h"
+#include "bindings/core/v8/V8Binding.h"
+#include "core/CoreExport.h"
+#include "platform/heap/Handle.h"
+
+namespace blink {
+
+class TestCallbackInterface;
+
+class CORE_EXPORT UnsignedLongLongOrBooleanOrTestCallbackInterface final {
+  DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ public:
+  UnsignedLongLongOrBooleanOrTestCallbackInterface();
+  bool isNull() const { return m_type == SpecificTypeNone; }
+
+  bool isBoolean() const { return m_type == SpecificTypeBoolean; }
+  bool getAsBoolean() const;
+  void setBoolean(bool);
+  static UnsignedLongLongOrBooleanOrTestCallbackInterface fromBoolean(bool);
+
+  bool isTestCallbackInterface() const { return m_type == SpecificTypeTestCallbackInterface; }
+  TestCallbackInterface* getAsTestCallbackInterface() const;
+  void setTestCallbackInterface(TestCallbackInterface*);
+  static UnsignedLongLongOrBooleanOrTestCallbackInterface fromTestCallbackInterface(TestCallbackInterface*);
+
+  bool isUnsignedLongLong() const { return m_type == SpecificTypeUnsignedLongLong; }
+  uint64_t getAsUnsignedLongLong() const;
+  void setUnsignedLongLong(uint64_t);
+  static UnsignedLongLongOrBooleanOrTestCallbackInterface fromUnsignedLongLong(uint64_t);
+
+  UnsignedLongLongOrBooleanOrTestCallbackInterface(const UnsignedLongLongOrBooleanOrTestCallbackInterface&);
+  ~UnsignedLongLongOrBooleanOrTestCallbackInterface();
+  UnsignedLongLongOrBooleanOrTestCallbackInterface& operator=(const UnsignedLongLongOrBooleanOrTestCallbackInterface&);
+  DECLARE_TRACE();
+
+ private:
+  enum SpecificTypes {
+    SpecificTypeNone,
+    SpecificTypeBoolean,
+    SpecificTypeTestCallbackInterface,
+    SpecificTypeUnsignedLongLong,
+  };
+  SpecificTypes m_type;
+
+  bool m_boolean;
+  Member<TestCallbackInterface> m_testCallbackInterface;
+  uint64_t m_unsignedLongLong;
+
+  friend CORE_EXPORT v8::Local<v8::Value> ToV8(const UnsignedLongLongOrBooleanOrTestCallbackInterface&, v8::Local<v8::Object>, v8::Isolate*);
+};
+
+class V8UnsignedLongLongOrBooleanOrTestCallbackInterface final {
+ public:
+  CORE_EXPORT static void toImpl(v8::Isolate*, v8::Local<v8::Value>, UnsignedLongLongOrBooleanOrTestCallbackInterface&, UnionTypeConversionMode, ExceptionState&);
+};
+
+CORE_EXPORT v8::Local<v8::Value> ToV8(const UnsignedLongLongOrBooleanOrTestCallbackInterface&, v8::Local<v8::Object>, v8::Isolate*);
+
+template <class CallbackInfo>
+inline void v8SetReturnValue(const CallbackInfo& callbackInfo, UnsignedLongLongOrBooleanOrTestCallbackInterface& impl) {
+  v8SetReturnValue(callbackInfo, ToV8(impl, callbackInfo.Holder(), callbackInfo.GetIsolate()));
+}
+
+template <class CallbackInfo>
+inline void v8SetReturnValue(const CallbackInfo& callbackInfo, UnsignedLongLongOrBooleanOrTestCallbackInterface& impl, v8::Local<v8::Object> creationContext) {
+  v8SetReturnValue(callbackInfo, ToV8(impl, creationContext, callbackInfo.GetIsolate()));
+}
+
+template <>
+struct NativeValueTraits<UnsignedLongLongOrBooleanOrTestCallbackInterface> : public NativeValueTraitsBase<UnsignedLongLongOrBooleanOrTestCallbackInterface> {
+  CORE_EXPORT static UnsignedLongLongOrBooleanOrTestCallbackInterface nativeValue(v8::Isolate*, v8::Local<v8::Value>, ExceptionState&);
+};
+
+template <>
+struct V8TypeOf<UnsignedLongLongOrBooleanOrTestCallbackInterface> {
+  typedef V8UnsignedLongLongOrBooleanOrTestCallbackInterface Type;
+};
+
+}  // namespace blink
+
+// We need to set canInitializeWithMemset=true because HeapVector supports
+// items that can initialize with memset or have a vtable. It is safe to
+// set canInitializeWithMemset=true for a union type object in practice.
+// See https://codereview.chromium.org/1118993002/#msg5 for more details.
+WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::UnsignedLongLongOrBooleanOrTestCallbackInterface);
+
+#endif  // UnsignedLongLongOrBooleanOrTestCallbackInterface_h
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackInterface.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackInterface.cpp
index 8c51693..9d785420 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackInterface.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackInterface.cpp
@@ -41,10 +41,18 @@
     return;
   if (!m_scriptState->contextIsValid())
     return;
+
   ScriptState::Scope scope(m_scriptState.get());
+
   v8::Local<v8::Value> *argv = 0;
 
-  V8ScriptRunner::callFunction(m_callback.newLocal(m_scriptState->isolate()), m_scriptState->getExecutionContext(), v8::Undefined(m_scriptState->isolate()), 0, argv, m_scriptState->isolate());
+  v8::Isolate* isolate = m_scriptState->isolate();
+  V8ScriptRunner::callFunction(m_callback.newLocal(isolate),
+                               m_scriptState->getExecutionContext(),
+                               v8::Undefined(isolate),
+                               0,
+                               argv,
+                               isolate);
 }
 
 bool V8TestCallbackInterface::booleanMethod() {
@@ -54,12 +62,20 @@
     return true;
   if (!m_scriptState->contextIsValid())
     return true;
+
   ScriptState::Scope scope(m_scriptState.get());
+
   v8::Local<v8::Value> *argv = 0;
 
-  v8::TryCatch exceptionCatcher(m_scriptState->isolate());
+  v8::Isolate* isolate = m_scriptState->isolate();
+  v8::TryCatch exceptionCatcher(isolate);
   exceptionCatcher.SetVerbose(true);
-  V8ScriptRunner::callFunction(m_callback.newLocal(m_scriptState->isolate()), m_scriptState->getExecutionContext(), v8::Undefined(m_scriptState->isolate()), 0, argv, m_scriptState->isolate());
+  V8ScriptRunner::callFunction(m_callback.newLocal(isolate),
+                               executionContext,
+                               v8::Undefined(isolate),
+                               0,
+                               argv,
+                               isolate);
   return !exceptionCatcher.HasCaught();
 }
 
@@ -70,11 +86,19 @@
     return;
   if (!m_scriptState->contextIsValid())
     return;
+
   ScriptState::Scope scope(m_scriptState.get());
+
   v8::Local<v8::Value> boolArgHandle = v8Boolean(boolArg, m_scriptState->isolate());
   v8::Local<v8::Value> argv[] = { boolArgHandle };
 
-  V8ScriptRunner::callFunction(m_callback.newLocal(m_scriptState->isolate()), m_scriptState->getExecutionContext(), v8::Undefined(m_scriptState->isolate()), 1, argv, m_scriptState->isolate());
+  v8::Isolate* isolate = m_scriptState->isolate();
+  V8ScriptRunner::callFunction(m_callback.newLocal(isolate),
+                               m_scriptState->getExecutionContext(),
+                               v8::Undefined(isolate),
+                               1,
+                               argv,
+                               isolate);
 }
 
 void V8TestCallbackInterface::voidMethodSequenceArg(const HeapVector<Member<TestInterfaceEmpty>>& sequenceArg) {
@@ -84,11 +108,19 @@
     return;
   if (!m_scriptState->contextIsValid())
     return;
+
   ScriptState::Scope scope(m_scriptState.get());
+
   v8::Local<v8::Value> sequenceArgHandle = ToV8(sequenceArg, m_scriptState->context()->Global(), m_scriptState->isolate());
   v8::Local<v8::Value> argv[] = { sequenceArgHandle };
 
-  V8ScriptRunner::callFunction(m_callback.newLocal(m_scriptState->isolate()), m_scriptState->getExecutionContext(), v8::Undefined(m_scriptState->isolate()), 1, argv, m_scriptState->isolate());
+  v8::Isolate* isolate = m_scriptState->isolate();
+  V8ScriptRunner::callFunction(m_callback.newLocal(isolate),
+                               m_scriptState->getExecutionContext(),
+                               v8::Undefined(isolate),
+                               1,
+                               argv,
+                               isolate);
 }
 
 void V8TestCallbackInterface::voidMethodFloatArg(float floatArg) {
@@ -98,11 +130,19 @@
     return;
   if (!m_scriptState->contextIsValid())
     return;
+
   ScriptState::Scope scope(m_scriptState.get());
+
   v8::Local<v8::Value> floatArgHandle = v8::Number::New(m_scriptState->isolate(), floatArg);
   v8::Local<v8::Value> argv[] = { floatArgHandle };
 
-  V8ScriptRunner::callFunction(m_callback.newLocal(m_scriptState->isolate()), m_scriptState->getExecutionContext(), v8::Undefined(m_scriptState->isolate()), 1, argv, m_scriptState->isolate());
+  v8::Isolate* isolate = m_scriptState->isolate();
+  V8ScriptRunner::callFunction(m_callback.newLocal(isolate),
+                               m_scriptState->getExecutionContext(),
+                               v8::Undefined(isolate),
+                               1,
+                               argv,
+                               isolate);
 }
 
 void V8TestCallbackInterface::voidMethodTestInterfaceEmptyArg(TestInterfaceEmpty* testInterfaceEmptyArg) {
@@ -112,11 +152,19 @@
     return;
   if (!m_scriptState->contextIsValid())
     return;
+
   ScriptState::Scope scope(m_scriptState.get());
+
   v8::Local<v8::Value> testInterfaceEmptyArgHandle = ToV8(testInterfaceEmptyArg, m_scriptState->context()->Global(), m_scriptState->isolate());
   v8::Local<v8::Value> argv[] = { testInterfaceEmptyArgHandle };
 
-  V8ScriptRunner::callFunction(m_callback.newLocal(m_scriptState->isolate()), m_scriptState->getExecutionContext(), v8::Undefined(m_scriptState->isolate()), 1, argv, m_scriptState->isolate());
+  v8::Isolate* isolate = m_scriptState->isolate();
+  V8ScriptRunner::callFunction(m_callback.newLocal(isolate),
+                               m_scriptState->getExecutionContext(),
+                               v8::Undefined(isolate),
+                               1,
+                               argv,
+                               isolate);
 }
 
 void V8TestCallbackInterface::voidMethodTestInterfaceEmptyStringArg(TestInterfaceEmpty* testInterfaceEmptyArg, const String& stringArg) {
@@ -126,12 +174,20 @@
     return;
   if (!m_scriptState->contextIsValid())
     return;
+
   ScriptState::Scope scope(m_scriptState.get());
+
   v8::Local<v8::Value> testInterfaceEmptyArgHandle = ToV8(testInterfaceEmptyArg, m_scriptState->context()->Global(), m_scriptState->isolate());
   v8::Local<v8::Value> stringArgHandle = v8String(m_scriptState->isolate(), stringArg);
   v8::Local<v8::Value> argv[] = { testInterfaceEmptyArgHandle, stringArgHandle };
 
-  V8ScriptRunner::callFunction(m_callback.newLocal(m_scriptState->isolate()), m_scriptState->getExecutionContext(), v8::Undefined(m_scriptState->isolate()), 2, argv, m_scriptState->isolate());
+  v8::Isolate* isolate = m_scriptState->isolate();
+  V8ScriptRunner::callFunction(m_callback.newLocal(isolate),
+                               m_scriptState->getExecutionContext(),
+                               v8::Undefined(isolate),
+                               2,
+                               argv,
+                               isolate);
 }
 
 void V8TestCallbackInterface::callbackWithThisValueVoidMethodStringArg(ScriptValue thisValue, const String& stringArg) {
@@ -141,12 +197,20 @@
     return;
   if (!m_scriptState->contextIsValid())
     return;
+
   ScriptState::Scope scope(m_scriptState.get());
   v8::Local<v8::Value> thisHandle = thisValue.v8Value();
+
   v8::Local<v8::Value> stringArgHandle = v8String(m_scriptState->isolate(), stringArg);
   v8::Local<v8::Value> argv[] = { stringArgHandle };
 
-  V8ScriptRunner::callFunction(m_callback.newLocal(m_scriptState->isolate()), m_scriptState->getExecutionContext(), thisHandle, 1, argv, m_scriptState->isolate());
+  v8::Isolate* isolate = m_scriptState->isolate();
+  V8ScriptRunner::callFunction(m_callback.newLocal(isolate),
+                               m_scriptState->getExecutionContext(),
+                               thisHandle,
+                               1,
+                               argv,
+                               isolate);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackInterface.h b/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackInterface.h
index af5a4326..26c3497 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackInterface.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackInterface.h
@@ -38,6 +38,7 @@
   void voidMethodTestInterfaceEmptyStringArg(TestInterfaceEmpty* testInterfaceEmptyArg, const String& stringArg) override;
   void callbackWithThisValueVoidMethodStringArg(ScriptValue thisValue, const String& stringArg) override;
   void customVoidMethodTestInterfaceEmptyArg(TestInterfaceEmpty* testInterfaceEmptyArg) override;
+
  private:
   CORE_EXPORT V8TestCallbackInterface(v8::Local<v8::Function>, ScriptState*);
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestDictionary.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestDictionary.cpp
index 77c16c242..20a79e9 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestDictionary.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestDictionary.cpp
@@ -12,12 +12,10 @@
 #include "V8TestDictionary.h"
 
 #include "bindings/core/v8/Dictionary.h"
-#include "bindings/core/v8/DoubleOrString.h"
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/IDLTypes.h"
 #include "bindings/core/v8/NativeValueTraitsImpl.h"
 #include "bindings/core/v8/ScriptValue.h"
-#include "bindings/core/v8/TestInterface2OrUint8Array.h"
 #include "bindings/core/v8/V8ArrayBufferView.h"
 #include "bindings/core/v8/V8Element.h"
 #include "bindings/core/v8/V8EventTarget.h"
@@ -594,6 +592,21 @@
     impl.setUint8ArrayMember(uint8ArrayMember);
   }
 
+  v8::Local<v8::Value> unionWithTypedefsValue;
+  if (!v8Object->Get(isolate->GetCurrentContext(), v8AtomicString(isolate, "unionWithTypedefs")).ToLocal(&unionWithTypedefsValue)) {
+    exceptionState.rethrowV8Exception(block.Exception());
+    return;
+  }
+  if (unionWithTypedefsValue.IsEmpty() || unionWithTypedefsValue->IsUndefined()) {
+    // Do nothing.
+  } else {
+    FloatOrBoolean unionWithTypedefs;
+    V8FloatOrBoolean::toImpl(isolate, unionWithTypedefsValue, unionWithTypedefs, UnionTypeConversionMode::NotNullable, exceptionState);
+    if (exceptionState.hadException())
+      return;
+    impl.setUnionWithTypedefs(unionWithTypedefs);
+  }
+
   v8::Local<v8::Value> unrestrictedDoubleMemberValue;
   if (!v8Object->Get(isolate->GetCurrentContext(), v8AtomicString(isolate, "unrestrictedDoubleMember")).ToLocal(&unrestrictedDoubleMemberValue)) {
     exceptionState.rethrowV8Exception(block.Exception());
@@ -841,6 +854,11 @@
       return false;
   }
 
+  if (impl.hasUnionWithTypedefs()) {
+    if (!v8CallBoolean(dictionary->CreateDataProperty(isolate->GetCurrentContext(), v8AtomicString(isolate, "unionWithTypedefs"), ToV8(impl.unionWithTypedefs(), creationContext, isolate))))
+      return false;
+  }
+
   if (impl.hasUnrestrictedDoubleMember()) {
     if (!v8CallBoolean(dictionary->CreateDataProperty(isolate->GetCurrentContext(), v8AtomicString(isolate, "unrestrictedDoubleMember"), v8::Number::New(isolate, impl.unrestrictedDoubleMember()))))
       return false;
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor.cpp
index be04ade..013b701 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor.cpp
@@ -14,7 +14,6 @@
 #include "bindings/core/v8/Dictionary.h"
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/IDLTypes.h"
-#include "bindings/core/v8/LongOrTestDictionary.h"
 #include "bindings/core/v8/NativeValueTraitsImpl.h"
 #include "bindings/core/v8/V8DOMConfiguration.h"
 #include "bindings/core/v8/V8ObjectConstructor.h"
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
index 23b58c13..4775774 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
@@ -11,12 +11,8 @@
 // clang-format off
 #include "V8TestObject.h"
 
-#include "bindings/core/v8/ArrayBufferOrArrayBufferViewOrDictionary.h"
 #include "bindings/core/v8/BindingSecurity.h"
-#include "bindings/core/v8/BooleanOrStringOrUnrestrictedDouble.h"
 #include "bindings/core/v8/Dictionary.h"
-#include "bindings/core/v8/DoubleOrString.h"
-#include "bindings/core/v8/DoubleOrStringOrDoubleOrStringSequence.h"
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/IDLTypes.h"
 #include "bindings/core/v8/NativeValueTraitsImpl.h"
@@ -26,13 +22,7 @@
 #include "bindings/core/v8/ScriptValue.h"
 #include "bindings/core/v8/SerializedScriptValue.h"
 #include "bindings/core/v8/SerializedScriptValueFactory.h"
-#include "bindings/core/v8/StringOrArrayBufferOrArrayBufferView.h"
-#include "bindings/core/v8/StringOrStringSequence.h"
-#include "bindings/core/v8/TestEnumOrDouble.h"
-#include "bindings/core/v8/TestInterfaceGarbageCollectedOrString.h"
-#include "bindings/core/v8/TestInterfaceOrLong.h"
 #include "bindings/core/v8/Transferables.h"
-#include "bindings/core/v8/UnrestrictedDoubleOrString.h"
 #include "bindings/core/v8/V8AbstractEventListener.h"
 #include "bindings/core/v8/V8ArrayBuffer.h"
 #include "bindings/core/v8/V8ArrayBufferView.h"
@@ -4780,6 +4770,60 @@
   impl->voidMethodArrayBufferOrArrayBufferViewOrDictionaryArg(arg);
 }
 
+static void voidMethodBooleanOrElementSequenceArgMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  ExceptionState exceptionState(info.GetIsolate(), ExceptionState::ExecutionContext, "TestObject", "voidMethodBooleanOrElementSequenceArg");
+
+  TestObject* impl = V8TestObject::toImpl(info.Holder());
+
+  if (UNLIKELY(info.Length() < 1)) {
+    exceptionState.throwTypeError(ExceptionMessages::notEnoughArguments(1, info.Length()));
+    return;
+  }
+
+  BooleanOrElementSequence arg;
+  V8BooleanOrElementSequence::toImpl(info.GetIsolate(), info[0], arg, UnionTypeConversionMode::NotNullable, exceptionState);
+  if (exceptionState.hadException())
+    return;
+
+  impl->voidMethodBooleanOrElementSequenceArg(arg);
+}
+
+static void voidMethodDoubleOrLongOrBooleanSequenceArgMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  ExceptionState exceptionState(info.GetIsolate(), ExceptionState::ExecutionContext, "TestObject", "voidMethodDoubleOrLongOrBooleanSequenceArg");
+
+  TestObject* impl = V8TestObject::toImpl(info.Holder());
+
+  if (UNLIKELY(info.Length() < 1)) {
+    exceptionState.throwTypeError(ExceptionMessages::notEnoughArguments(1, info.Length()));
+    return;
+  }
+
+  DoubleOrLongOrBooleanSequence arg;
+  V8DoubleOrLongOrBooleanSequence::toImpl(info.GetIsolate(), info[0], arg, UnionTypeConversionMode::NotNullable, exceptionState);
+  if (exceptionState.hadException())
+    return;
+
+  impl->voidMethodDoubleOrLongOrBooleanSequenceArg(arg);
+}
+
+static void voidMethodElementSequenceOrByteStringDoubleOrStringRecordMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  ExceptionState exceptionState(info.GetIsolate(), ExceptionState::ExecutionContext, "TestObject", "voidMethodElementSequenceOrByteStringDoubleOrStringRecord");
+
+  TestObject* impl = V8TestObject::toImpl(info.Holder());
+
+  if (UNLIKELY(info.Length() < 1)) {
+    exceptionState.throwTypeError(ExceptionMessages::notEnoughArguments(1, info.Length()));
+    return;
+  }
+
+  ElementSequenceOrByteStringDoubleOrStringRecord arg;
+  V8ElementSequenceOrByteStringDoubleOrStringRecord::toImpl(info.GetIsolate(), info[0], arg, UnionTypeConversionMode::NotNullable, exceptionState);
+  if (exceptionState.hadException())
+    return;
+
+  impl->voidMethodElementSequenceOrByteStringDoubleOrStringRecord(arg);
+}
+
 static void voidMethodArrayOfDoubleOrDOMStringArgMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
   ExceptionState exceptionState(info.GetIsolate(), ExceptionState::ExecutionContext, "TestObject", "voidMethodArrayOfDoubleOrDOMStringArg");
 
@@ -10555,6 +10599,18 @@
   TestObjectV8Internal::voidMethodArrayBufferOrArrayBufferViewOrDictionaryArgMethod(info);
 }
 
+void V8TestObject::voidMethodBooleanOrElementSequenceArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  TestObjectV8Internal::voidMethodBooleanOrElementSequenceArgMethod(info);
+}
+
+void V8TestObject::voidMethodDoubleOrLongOrBooleanSequenceArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  TestObjectV8Internal::voidMethodDoubleOrLongOrBooleanSequenceArgMethod(info);
+}
+
+void V8TestObject::voidMethodElementSequenceOrByteStringDoubleOrStringRecordMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  TestObjectV8Internal::voidMethodElementSequenceOrByteStringDoubleOrStringRecordMethod(info);
+}
+
 void V8TestObject::voidMethodArrayOfDoubleOrDOMStringArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
   TestObjectV8Internal::voidMethodArrayOfDoubleOrDOMStringArgMethod(info);
 }
@@ -11516,6 +11572,9 @@
     {"voidMethodDoubleOrNullOrDOMStringArg", V8TestObject::voidMethodDoubleOrNullOrDOMStringArgMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
     {"voidMethodDOMStringOrArrayBufferOrArrayBufferViewArg", V8TestObject::voidMethodDOMStringOrArrayBufferOrArrayBufferViewArgMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
     {"voidMethodArrayBufferOrArrayBufferViewOrDictionaryArg", V8TestObject::voidMethodArrayBufferOrArrayBufferViewOrDictionaryArgMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
+    {"voidMethodBooleanOrElementSequenceArg", V8TestObject::voidMethodBooleanOrElementSequenceArgMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
+    {"voidMethodDoubleOrLongOrBooleanSequenceArg", V8TestObject::voidMethodDoubleOrLongOrBooleanSequenceArgMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
+    {"voidMethodElementSequenceOrByteStringDoubleOrStringRecord", V8TestObject::voidMethodElementSequenceOrByteStringDoubleOrStringRecordMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
     {"voidMethodArrayOfDoubleOrDOMStringArg", V8TestObject::voidMethodArrayOfDoubleOrDOMStringArgMethodCallback, nullptr, 0, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
     {"voidMethodTestInterfaceEmptyOrNullArg", V8TestObject::voidMethodTestInterfaceEmptyOrNullArgMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
     {"voidMethodTestCallbackInterfaceArg", V8TestObject::voidMethodTestCallbackInterfaceArgMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.h b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.h
index 205626c..ca99306 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.h
@@ -13,9 +13,12 @@
 #define V8TestObject_h
 
 #include "bindings/core/v8/ArrayBufferOrArrayBufferViewOrDictionary.h"
+#include "bindings/core/v8/BooleanOrElementSequence.h"
 #include "bindings/core/v8/BooleanOrStringOrUnrestrictedDouble.h"
+#include "bindings/core/v8/DoubleOrLongOrBooleanSequence.h"
 #include "bindings/core/v8/DoubleOrString.h"
 #include "bindings/core/v8/DoubleOrStringOrDoubleOrStringSequence.h"
+#include "bindings/core/v8/ElementSequenceOrByteStringDoubleOrStringRecord.h"
 #include "bindings/core/v8/GeneratedCodeHelper.h"
 #include "bindings/core/v8/NativeValueTraits.h"
 #include "bindings/core/v8/ScriptWrappable.h"
@@ -462,6 +465,9 @@
   CORE_EXPORT static void voidMethodDoubleOrNullOrDOMStringArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void voidMethodDOMStringOrArrayBufferOrArrayBufferViewArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void voidMethodArrayBufferOrArrayBufferViewOrDictionaryArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
+  CORE_EXPORT static void voidMethodBooleanOrElementSequenceArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
+  CORE_EXPORT static void voidMethodDoubleOrLongOrBooleanSequenceArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
+  CORE_EXPORT static void voidMethodElementSequenceOrByteStringDoubleOrStringRecordMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void voidMethodArrayOfDoubleOrDOMStringArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void voidMethodTestInterfaceEmptyOrNullArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void voidMethodTestCallbackInterfaceArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperations.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperations.cpp
index aeb9413..56fef453 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperations.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperations.cpp
@@ -14,7 +14,6 @@
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/IDLTypes.h"
 #include "bindings/core/v8/NativeValueTraitsImpl.h"
-#include "bindings/core/v8/NodeOrNodeList.h"
 #include "bindings/core/v8/V8DOMConfiguration.h"
 #include "bindings/core/v8/V8Node.h"
 #include "bindings/core/v8/V8NodeList.h"
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.cpp
index f8c99941..2d7e39d 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.cpp
@@ -11,15 +11,9 @@
 // clang-format off
 #include "V8TestTypedefs.h"
 
-#include "bindings/core/v8/ByteStringOrNodeList.h"
-#include "bindings/core/v8/ByteStringSequenceSequenceOrByteStringByteStringRecord.h"
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/IDLTypes.h"
-#include "bindings/core/v8/LongSequenceOrEvent.h"
 #include "bindings/core/v8/NativeValueTraitsImpl.h"
-#include "bindings/core/v8/NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord.h"
-#include "bindings/core/v8/StringOrDouble.h"
-#include "bindings/core/v8/TestInterfaceOrTestInterfaceEmpty.h"
 #include "bindings/core/v8/V8DOMConfiguration.h"
 #include "bindings/core/v8/V8Event.h"
 #include "bindings/core/v8/V8Node.h"
@@ -30,7 +24,6 @@
 #include "bindings/core/v8/V8TestInterfaceEmpty.h"
 #include "bindings/core/v8/V8TestObject.h"
 #include "bindings/core/v8/V8XMLHttpRequest.h"
-#include "bindings/core/v8/XMLHttpRequestOrString.h"
 #include "core/dom/Document.h"
 #include "core/dom/NameNodeList.h"
 #include "core/dom/NodeList.h"
@@ -334,6 +327,24 @@
   impl->voidMethodNestedUnionType(arg);
 }
 
+static void voidMethodUnionWithTypedefMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  ExceptionState exceptionState(info.GetIsolate(), ExceptionState::ExecutionContext, "TestTypedefs", "voidMethodUnionWithTypedef");
+
+  TestTypedefs* impl = V8TestTypedefs::toImpl(info.Holder());
+
+  if (UNLIKELY(info.Length() < 1)) {
+    exceptionState.throwTypeError(ExceptionMessages::notEnoughArguments(1, info.Length()));
+    return;
+  }
+
+  UnsignedLongLongOrBooleanOrTestCallbackInterface arg;
+  V8UnsignedLongLongOrBooleanOrTestCallbackInterface::toImpl(info.GetIsolate(), info[0], arg, UnionTypeConversionMode::NotNullable, exceptionState);
+  if (exceptionState.hadException())
+    return;
+
+  impl->voidMethodUnionWithTypedef(arg);
+}
+
 static void constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
   if (UNLIKELY(info.Length() < 1)) {
     V8ThrowException::throwTypeError(info.GetIsolate(), ExceptionMessages::failedToConstruct("TestTypedefs", ExceptionMessages::notEnoughArguments(1, info.Length())));
@@ -425,6 +436,10 @@
   TestTypedefsV8Internal::voidMethodNestedUnionTypeMethod(info);
 }
 
+void V8TestTypedefs::voidMethodUnionWithTypedefMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  TestTypedefsV8Internal::voidMethodUnionWithTypedefMethod(info);
+}
+
 // Suppress warning: global constructors, because AttributeConfiguration is trivial
 // and does not depend on another global objects.
 #if defined(COMPONENT_BUILD) && defined(WIN32) && COMPILER(CLANG)
@@ -457,6 +472,7 @@
     {"unionWithRecordMethod", V8TestTypedefs::unionWithRecordMethodMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
     {"methodThatReturnsRecord", V8TestTypedefs::methodThatReturnsRecordMethodCallback, nullptr, 0, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
     {"voidMethodNestedUnionType", V8TestTypedefs::voidMethodNestedUnionTypeMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
+    {"voidMethodUnionWithTypedef", V8TestTypedefs::voidMethodUnionWithTypedefMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
 };
 
 void V8TestTypedefs::constructorCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.h b/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.h
index f070985..fff6bbc7 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.h
@@ -20,6 +20,7 @@
 #include "bindings/core/v8/StringOrDouble.h"
 #include "bindings/core/v8/TestInterfaceOrTestInterfaceEmpty.h"
 #include "bindings/core/v8/ToV8.h"
+#include "bindings/core/v8/UnsignedLongLongOrBooleanOrTestCallbackInterface.h"
 #include "bindings/core/v8/V8Binding.h"
 #include "bindings/core/v8/V8DOMWrapper.h"
 #include "bindings/core/v8/WrapperTypeInfo.h"
@@ -69,6 +70,7 @@
   CORE_EXPORT static void unionWithRecordMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void methodThatReturnsRecordMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void voidMethodNestedUnionTypeMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
+  CORE_EXPORT static void voidMethodUnionWithTypedefMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
 };
 
 template <>
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/XMLHttpRequestOrString.cpp b/third_party/WebKit/Source/bindings/tests/results/core/XMLHttpRequestOrString.cpp
index cb4aa5f..7eac9bb 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/XMLHttpRequestOrString.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/XMLHttpRequestOrString.cpp
@@ -11,7 +11,6 @@
 // clang-format off
 #include "XMLHttpRequestOrString.h"
 
-#include "bindings/core/v8/ArrayBufferOrArrayBufferViewOrBlobOrDocumentOrStringOrFormDataOrURLSearchParams.h"
 #include "bindings/core/v8/IDLTypes.h"
 #include "bindings/core/v8/NativeValueTraitsImpl.h"
 #include "bindings/core/v8/ToV8.h"
diff --git a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.cpp b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.cpp
index 9c15a99..b05b93f 100644
--- a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.cpp
@@ -17,8 +17,6 @@
 #include "bindings/core/v8/V8DOMConfiguration.h"
 #include "bindings/core/v8/V8ObjectConstructor.h"
 #include "bindings/core/v8/V8TestInterfaceEmpty.h"
-#include "bindings/modules/v8/BooleanOrString.h"
-#include "bindings/modules/v8/DoubleOrString.h"
 #include "bindings/modules/v8/V8TestInterface5.h"
 #include "bindings/modules/v8/VoidCallbackFunctionModules.h"
 #include "core/dom/Document.h"
diff --git a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterfacePartial.cpp b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterfacePartial.cpp
index 49c48ba2..0dc9ef59 100644
--- a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterfacePartial.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterfacePartial.cpp
@@ -20,6 +20,7 @@
 #include "bindings/core/v8/V8Document.h"
 #include "bindings/core/v8/V8Node.h"
 #include "bindings/core/v8/V8ObjectConstructor.h"
+#include "bindings/core/v8/V8TestCallbackInterface.h"
 #include "bindings/core/v8/V8TestInterface.h"
 #include "bindings/tests/idls/modules/TestInterfacePartial3Implementation.h"
 #include "bindings/tests/idls/modules/TestInterfacePartial4.h"
@@ -328,6 +329,14 @@
   TestInterfacePartial3Implementation::unscopableVoidMethod(*impl);
 }
 
+static void unionWithTypedefMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  TestInterfaceImplementation* impl = V8TestInterface::toImpl(info.Holder());
+
+  UnsignedLongLongOrBooleanOrTestCallbackInterface result;
+  TestInterfacePartial3Implementation::unionWithTypedefMethod(*impl, result);
+  v8SetReturnValue(info, result);
+}
+
 static void partial4VoidMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
   TestInterfaceImplementation* impl = V8TestInterface::toImpl(info.Holder());
 
@@ -368,6 +377,10 @@
   TestInterfaceImplementationPartialV8Internal::unscopableVoidMethodMethod(info);
 }
 
+void V8TestInterfacePartial::unionWithTypedefMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  TestInterfaceImplementationPartialV8Internal::unionWithTypedefMethodMethod(info);
+}
+
 void V8TestInterfacePartial::partial4VoidMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
   TestInterfaceImplementationPartialV8Internal::partial4VoidMethodMethod(info);
 }
@@ -379,6 +392,7 @@
 const V8DOMConfiguration::MethodConfiguration V8TestInterfaceMethods[] = {
     {"partialVoidTestEnumModulesArgMethod", V8TestInterfacePartial::partialVoidTestEnumModulesArgMethodMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
     {"unscopableVoidMethod", V8TestInterfacePartial::unscopableVoidMethodMethodCallback, nullptr, 0, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
+    {"unionWithTypedefMethod", V8TestInterfacePartial::unionWithTypedefMethodMethodCallback, nullptr, 0, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
 };
 
 void V8TestInterfacePartial::installV8TestInterfaceTemplate(v8::Isolate* isolate, const DOMWrapperWorld& world, v8::Local<v8::FunctionTemplate> interfaceTemplate) {
diff --git a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterfacePartial.h b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterfacePartial.h
index c98b5558..a3d2667 100644
--- a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterfacePartial.h
+++ b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterfacePartial.h
@@ -16,6 +16,7 @@
 #include "bindings/core/v8/NativeValueTraits.h"
 #include "bindings/core/v8/ScriptWrappable.h"
 #include "bindings/core/v8/ToV8.h"
+#include "bindings/core/v8/UnsignedLongLongOrBooleanOrTestCallbackInterface.h"
 #include "bindings/core/v8/V8Binding.h"
 #include "bindings/core/v8/V8DOMWrapper.h"
 #include "bindings/core/v8/WrapperTypeInfo.h"
@@ -45,6 +46,7 @@
 
   static void partialVoidTestEnumModulesArgMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info);
   static void unscopableVoidMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void unionWithTypedefMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info);
   static void partial4VoidMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info);
   static void partial4StaticVoidMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info);
 
diff --git a/third_party/WebKit/Source/core/dom/Element.cpp b/third_party/WebKit/Source/core/dom/Element.cpp
index 73df10b..8043437 100644
--- a/third_party/WebKit/Source/core/dom/Element.cpp
+++ b/third_party/WebKit/Source/core/dom/Element.cpp
@@ -1328,12 +1328,6 @@
 
   invalidateNodeListCachesInAncestors(&name, this);
 
-  // If there is currently no StyleResolver, we can't be sure that this
-  // attribute change won't affect style.
-  if (!document().styleResolver())
-    setNeedsStyleRecalc(SubtreeStyleChange,
-                        StyleChangeReasonForTracing::fromAttribute(name));
-
   if (isConnected()) {
     if (AXObjectCache* cache = document().existingAXObjectCache())
       cache->handleAttributeChanged(name, this);
@@ -2355,9 +2349,6 @@
     return;
   if (!inActiveDocument())
     return;
-  if (!document().styleResolver())
-    return;
-
   if (!style ||
       (styleAffectedByEmpty() && (!style->emptyState() || hasChildren())))
     pseudoStateChanged(CSSSelector::PseudoEmpty);
diff --git a/third_party/WebKit/Source/core/dom/StyleEngine.cpp b/third_party/WebKit/Source/core/dom/StyleEngine.cpp
index 36e75476..779ffa0 100644
--- a/third_party/WebKit/Source/core/dom/StyleEngine.cpp
+++ b/third_party/WebKit/Source/core/dom/StyleEngine.cpp
@@ -606,7 +606,7 @@
 }
 
 bool StyleEngine::shouldSkipInvalidationFor(const Element& element) const {
-  if (!resolver())
+  if (document().getStyleChangeType() >= SubtreeStyleChange)
     return true;
   if (!element.inActiveDocument())
     return true;
diff --git a/third_party/WebKit/Source/core/dom/StyleEngineTest.cpp b/third_party/WebKit/Source/core/dom/StyleEngineTest.cpp
index 60f4c16..097aeee 100644
--- a/third_party/WebKit/Source/core/dom/StyleEngineTest.cpp
+++ b/third_party/WebKit/Source/core/dom/StyleEngineTest.cpp
@@ -450,4 +450,40 @@
             t1->computedStyle()->visitedDependentColor(CSSPropertyColor));
 }
 
+TEST_F(StyleEngineTest, ScheduleInvaldationAfterSubtreeRecalc) {
+  document().body()->setInnerHTML(
+      "<style>"
+      "  .t1 span { color: green }x"
+      "  .t2 span { color: green }"
+      "</style>"
+      "<div id='t1'></div>"
+      "<div id='t2'></div>");
+  document().view()->updateAllLifecyclePhases();
+
+  Element* t1 = document().getElementById("t1");
+  Element* t2 = document().getElementById("t1");
+  ASSERT_TRUE(t1);
+  ASSERT_TRUE(t2);
+
+  // Sanity test.
+  t1->setAttribute(blink::HTMLNames::classAttr, "t1");
+  EXPECT_FALSE(document().needsStyleInvalidation());
+  EXPECT_TRUE(document().childNeedsStyleInvalidation());
+
+  document().view()->updateAllLifecyclePhases();
+
+  // platformColorsChanged() triggers SubtreeStyleChange on document(). If that
+  // for some reason should change, this test will start failing and the
+  // SubtreeStyleChange must be set another way.
+  // Calling setNeedsStyleRecalc() explicitly with an arbitrary reason instead
+  // requires us to CORE_EXPORT the reason strings.
+  styleEngine().platformColorsChanged();
+
+  // Check that no invalidations sets are scheduled when the document node is
+  // already SubtreeStyleChange.
+  t2->setAttribute(blink::HTMLNames::classAttr, "t2");
+  EXPECT_FALSE(document().needsStyleInvalidation());
+  EXPECT_FALSE(document().childNeedsStyleInvalidation());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/Editor.cpp b/third_party/WebKit/Source/core/editing/Editor.cpp
index f1e1a4f..5d0c85686 100644
--- a/third_party/WebKit/Source/core/editing/Editor.cpp
+++ b/third_party/WebKit/Source/core/editing/Editor.cpp
@@ -181,13 +181,9 @@
   if (textControlOfTarget &&
       (selection.start().isNull() ||
        textControlOfTarget != textControlOfSelectionStart)) {
-    if (Range* range = textControlOfTarget->selection()) {
-      return createVisibleSelection(
-          SelectionInDOMTree::Builder()
-              .setBaseAndExtent(EphemeralRange(range))
-              .setIsDirectional(selection.isDirectional())
-              .build());
-    }
+    const SelectionInDOMTree& select = textControlOfTarget->selection();
+    if (!select.isNone())
+      return createVisibleSelection(select);
   }
   return selection;
 }
diff --git a/third_party/WebKit/Source/core/editing/SelectionController.cpp b/third_party/WebKit/Source/core/editing/SelectionController.cpp
index b53e47f..c253d60a8 100644
--- a/third_party/WebKit/Source/core/editing/SelectionController.cpp
+++ b/third_party/WebKit/Source/core/editing/SelectionController.cpp
@@ -387,6 +387,9 @@
   } else {
     basePosition = selection().computeVisibleSelectionInFlatTree().base();
   }
+  if (basePosition.isNull())
+    return;
+
   const SelectionInFlatTree& appliedSelection = applySelectAll(
       basePosition, targetPosition.deepEquivalent(), mousePressNode,
       dragStartPos, target, hitTestResult.localPoint());
diff --git a/third_party/WebKit/Source/core/editing/SelectionEditor.cpp b/third_party/WebKit/Source/core/editing/SelectionEditor.cpp
index 14f7509..615c5d7 100644
--- a/third_party/WebKit/Source/core/editing/SelectionEditor.cpp
+++ b/third_party/WebKit/Source/core/editing/SelectionEditor.cpp
@@ -370,7 +370,13 @@
   if (!needsUpdateVisibleSelection())
     return;
 
+  m_styleVersion = document().styleVersion();
+  m_cacheIsDirty = false;
   m_cachedVisibleSelectionInDOMTree = createVisibleSelection(m_selection);
+  if (m_cachedVisibleSelectionInDOMTree.isNone()) {
+    m_cachedVisibleSelectionInFlatTree = VisibleSelectionInFlatTree();
+    return;
+  }
   m_cachedVisibleSelectionInFlatTree = createVisibleSelection(
       SelectionInFlatTree::Builder()
           .setBaseAndExtent(toPositionInFlatTree(m_selection.base()),
@@ -380,8 +386,6 @@
           .setGranularity(m_selection.granularity())
           .setIsDirectional(m_selection.isDirectional())
           .build());
-  m_styleVersion = document().styleVersion();
-  m_cacheIsDirty = false;
 }
 
 void SelectionEditor::cacheRangeOfDocument(Range* range) {
diff --git a/third_party/WebKit/Source/core/editing/VisibleUnits.cpp b/third_party/WebKit/Source/core/editing/VisibleUnits.cpp
index d35c225..251dd1f 100644
--- a/third_party/WebKit/Source/core/editing/VisibleUnits.cpp
+++ b/third_party/WebKit/Source/core/editing/VisibleUnits.cpp
@@ -1831,70 +1831,71 @@
   PositionAnchorType candidateType = position.anchorType();
   int candidateOffset = position.computeEditingOffset();
 
-  Node* prevousNodeIterator = startNode;
-  while (prevousNodeIterator) {
+  Node* previousNodeIterator = startNode;
+  while (previousNodeIterator) {
     if (boundaryCrossingRule == CannotCrossEditingBoundary &&
-        !nodeIsUserSelectAll(prevousNodeIterator) &&
-        hasEditableStyle(*prevousNodeIterator) != startNodeIsEditable)
+        !nodeIsUserSelectAll(previousNodeIterator) &&
+        hasEditableStyle(*previousNodeIterator) != startNodeIsEditable)
       break;
     if (boundaryCrossingRule == CanSkipOverEditingBoundary) {
-      while (prevousNodeIterator &&
-             hasEditableStyle(*prevousNodeIterator) != startNodeIsEditable)
-        prevousNodeIterator =
-            Strategy::previousPostOrder(*prevousNodeIterator, startBlock);
-      if (!prevousNodeIterator ||
-          !prevousNodeIterator->isDescendantOf(highestRoot))
+      while (previousNodeIterator &&
+             hasEditableStyle(*previousNodeIterator) != startNodeIsEditable) {
+        previousNodeIterator =
+            Strategy::previousPostOrder(*previousNodeIterator, startBlock);
+      }
+      if (!previousNodeIterator ||
+          !previousNodeIterator->isDescendantOf(highestRoot))
         break;
     }
 
     const LayoutItem layoutItem =
-        LayoutItem(prevousNodeIterator->layoutObject());
+        LayoutItem(previousNodeIterator->layoutObject());
     if (layoutItem.isNull()) {
-      prevousNodeIterator =
-          Strategy::previousPostOrder(*prevousNodeIterator, startBlock);
+      previousNodeIterator =
+          Strategy::previousPostOrder(*previousNodeIterator, startBlock);
       continue;
     }
     const ComputedStyle& style = layoutItem.styleRef();
     if (style.visibility() != EVisibility::kVisible) {
-      prevousNodeIterator =
-          Strategy::previousPostOrder(*prevousNodeIterator, startBlock);
+      previousNodeIterator =
+          Strategy::previousPostOrder(*previousNodeIterator, startBlock);
       continue;
     }
 
-    if (layoutItem.isBR() || isEnclosingBlock(prevousNodeIterator))
+    if (layoutItem.isBR() || isEnclosingBlock(previousNodeIterator))
       break;
 
     if (layoutItem.isText() &&
-        toLayoutText(prevousNodeIterator->layoutObject())
+        toLayoutText(previousNodeIterator->layoutObject())
             ->resolvedTextLength()) {
-      SECURITY_DCHECK(prevousNodeIterator->isTextNode());
+      SECURITY_DCHECK(previousNodeIterator->isTextNode());
       if (style.preserveNewline()) {
-        LayoutText* text = toLayoutText(prevousNodeIterator->layoutObject());
+        LayoutText* text = toLayoutText(previousNodeIterator->layoutObject());
         int index = text->textLength();
-        if (prevousNodeIterator == startNode && candidateOffset < index)
+        if (previousNodeIterator == startNode && candidateOffset < index)
           index = max(0, candidateOffset);
         while (--index >= 0) {
           if ((*text)[index] == '\n')
-            return PositionTemplate<Strategy>(toText(prevousNodeIterator),
+            return PositionTemplate<Strategy>(toText(previousNodeIterator),
                                               index + 1);
         }
       }
-      candidateNode = prevousNodeIterator;
+      candidateNode = previousNodeIterator;
       candidateType = PositionAnchorType::OffsetInAnchor;
       candidateOffset = 0;
-      prevousNodeIterator =
-          Strategy::previousPostOrder(*prevousNodeIterator, startBlock);
-    } else if (editingIgnoresContent(*prevousNodeIterator) ||
-               isDisplayInsideTable(prevousNodeIterator)) {
-      candidateNode = prevousNodeIterator;
+      previousNodeIterator =
+          Strategy::previousPostOrder(*previousNodeIterator, startBlock);
+    } else if (editingIgnoresContent(*previousNodeIterator) ||
+               isDisplayInsideTable(previousNodeIterator)) {
+      candidateNode = previousNodeIterator;
       candidateType = PositionAnchorType::BeforeAnchor;
-      prevousNodeIterator =
-          prevousNodeIterator->previousSibling()
-              ? prevousNodeIterator->previousSibling()
-              : Strategy::previousPostOrder(*prevousNodeIterator, startBlock);
+      previousNodeIterator =
+          previousNodeIterator->previousSibling()
+              ? previousNodeIterator->previousSibling()
+              : Strategy::previousPostOrder(*previousNodeIterator, startBlock);
     } else {
-      prevousNodeIterator =
-          Strategy::previousPostOrder(*prevousNodeIterator, startBlock);
+      previousNodeIterator =
+          Strategy::previousPostOrder(*previousNodeIterator, startBlock);
     }
   }
 
@@ -1948,63 +1949,63 @@
   PositionAnchorType candidateType = position.anchorType();
   int candidateOffset = position.computeEditingOffset();
 
-  Node* nextNodeItreator = startNode;
-  while (nextNodeItreator) {
+  Node* nextNodeIterator = startNode;
+  while (nextNodeIterator) {
     if (boundaryCrossingRule == CannotCrossEditingBoundary &&
-        !nodeIsUserSelectAll(nextNodeItreator) &&
-        hasEditableStyle(*nextNodeItreator) != startNodeIsEditable)
+        !nodeIsUserSelectAll(nextNodeIterator) &&
+        hasEditableStyle(*nextNodeIterator) != startNodeIsEditable)
       break;
     if (boundaryCrossingRule == CanSkipOverEditingBoundary) {
-      while (nextNodeItreator &&
-             hasEditableStyle(*nextNodeItreator) != startNodeIsEditable)
-        nextNodeItreator = Strategy::next(*nextNodeItreator, startBlock);
-      if (!nextNodeItreator || !nextNodeItreator->isDescendantOf(highestRoot))
+      while (nextNodeIterator &&
+             hasEditableStyle(*nextNodeIterator) != startNodeIsEditable)
+        nextNodeIterator = Strategy::next(*nextNodeIterator, startBlock);
+      if (!nextNodeIterator || !nextNodeIterator->isDescendantOf(highestRoot))
         break;
     }
 
-    LayoutObject* const layoutObject = nextNodeItreator->layoutObject();
+    LayoutObject* const layoutObject = nextNodeIterator->layoutObject();
     if (!layoutObject) {
-      nextNodeItreator = Strategy::next(*nextNodeItreator, startBlock);
+      nextNodeIterator = Strategy::next(*nextNodeIterator, startBlock);
       continue;
     }
     const ComputedStyle& style = layoutObject->styleRef();
     if (style.visibility() != EVisibility::kVisible) {
-      nextNodeItreator = Strategy::next(*nextNodeItreator, startBlock);
+      nextNodeIterator = Strategy::next(*nextNodeIterator, startBlock);
       continue;
     }
 
-    if (layoutObject->isBR() || isEnclosingBlock(nextNodeItreator))
+    if (layoutObject->isBR() || isEnclosingBlock(nextNodeIterator))
       break;
 
     // FIXME: We avoid returning a position where the layoutObject can't accept
     // the caret.
     if (layoutObject->isText() &&
         toLayoutText(layoutObject)->resolvedTextLength()) {
-      SECURITY_DCHECK(nextNodeItreator->isTextNode());
+      SECURITY_DCHECK(nextNodeIterator->isTextNode());
       LayoutText* const text = toLayoutText(layoutObject);
       if (style.preserveNewline()) {
         const int length = toLayoutText(layoutObject)->textLength();
-        for (int i = (nextNodeItreator == startNode ? candidateOffset : 0);
+        for (int i = (nextNodeIterator == startNode ? candidateOffset : 0);
              i < length; ++i) {
           if ((*text)[i] == '\n')
-            return PositionTemplate<Strategy>(toText(nextNodeItreator),
+            return PositionTemplate<Strategy>(toText(nextNodeIterator),
                                               i + text->textStartOffset());
         }
       }
 
-      candidateNode = nextNodeItreator;
+      candidateNode = nextNodeIterator;
       candidateType = PositionAnchorType::OffsetInAnchor;
       candidateOffset =
           layoutObject->caretMaxOffset() + text->textStartOffset();
-      nextNodeItreator = Strategy::next(*nextNodeItreator, startBlock);
-    } else if (editingIgnoresContent(*nextNodeItreator) ||
-               isDisplayInsideTable(nextNodeItreator)) {
-      candidateNode = nextNodeItreator;
+      nextNodeIterator = Strategy::next(*nextNodeIterator, startBlock);
+    } else if (editingIgnoresContent(*nextNodeIterator) ||
+               isDisplayInsideTable(nextNodeIterator)) {
+      candidateNode = nextNodeIterator;
       candidateType = PositionAnchorType::AfterAnchor;
-      nextNodeItreator =
-          Strategy::nextSkippingChildren(*nextNodeItreator, startBlock);
+      nextNodeIterator =
+          Strategy::nextSkippingChildren(*nextNodeIterator, startBlock);
     } else {
-      nextNodeItreator = Strategy::next(*nextNodeItreator, startBlock);
+      nextNodeIterator = Strategy::next(*nextNodeIterator, startBlock);
     }
   }
 
diff --git a/third_party/WebKit/Source/core/html/TextControlElement.cpp b/third_party/WebKit/Source/core/html/TextControlElement.cpp
index fabc2fd..07b02c6 100644
--- a/third_party/WebKit/Source/core/html/TextControlElement.cpp
+++ b/third_party/WebKit/Source/core/html/TextControlElement.cpp
@@ -609,9 +609,9 @@
   }
 }
 
-Range* TextControlElement::selection() const {
+SelectionInDOMTree TextControlElement::selection() const {
   if (!layoutObject() || !isTextControl())
-    return nullptr;
+    return SelectionInDOMTree();
 
   int start = m_cachedSelectionStart;
   int end = m_cachedSelectionEnd;
@@ -619,10 +619,14 @@
   DCHECK_LE(start, end);
   HTMLElement* innerText = innerEditorElement();
   if (!innerText)
-    return nullptr;
+    return SelectionInDOMTree();
 
-  if (!innerText->hasChildren())
-    return Range::create(document(), innerText, 0, innerText, 0);
+  if (!innerText->hasChildren()) {
+    return SelectionInDOMTree::Builder()
+        .collapse(Position(innerText, 0))
+        .setIsDirectional(selectionDirection() != "none")
+        .build();
+  }
 
   int offset = 0;
   Node* startNode = 0;
@@ -644,9 +648,12 @@
   }
 
   if (!startNode || !endNode)
-    return nullptr;
+    return SelectionInDOMTree();
 
-  return Range::create(document(), startNode, start, endNode, end);
+  return SelectionInDOMTree::Builder()
+      .setBaseAndExtent(Position(startNode, start), Position(endNode, end))
+      .setIsDirectional(selectionDirection() != "none")
+      .build();
 }
 
 const AtomicString& TextControlElement::autocapitalize() const {
diff --git a/third_party/WebKit/Source/core/html/TextControlElement.h b/third_party/WebKit/Source/core/html/TextControlElement.h
index 8667c1f..b40cb00 100644
--- a/third_party/WebKit/Source/core/html/TextControlElement.h
+++ b/third_party/WebKit/Source/core/html/TextControlElement.h
@@ -28,13 +28,13 @@
 
 #include "base/gtest_prod_util.h"
 #include "core/CoreExport.h"
+#include "core/editing/SelectionTemplate.h"
 #include "core/editing/VisiblePosition.h"
 #include "core/html/HTMLFormControlElementWithState.h"
 
 namespace blink {
 
 class ExceptionState;
-class Range;
 
 enum TextFieldSelectionDirection {
   SelectionHasNoDirection,
@@ -100,7 +100,7 @@
   bool setSelectionRange(unsigned start,
                          unsigned end,
                          TextFieldSelectionDirection = SelectionHasNoDirection);
-  Range* selection() const;
+  SelectionInDOMTree selection() const;
 
   virtual bool supportsAutocapitalize() const = 0;
   virtual const AtomicString& defaultAutocapitalize() const = 0;
diff --git a/third_party/brotli/BUILD.gn b/third_party/brotli/BUILD.gn
index 2acb54b..4391133 100644
--- a/third_party/brotli/BUILD.gn
+++ b/third_party/brotli/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//testing/libfuzzer/fuzzer_test.gni")
+
 if (is_win) {
   import("//build/config/win/visual_studio_version.gni")
 }
@@ -128,3 +130,13 @@
     }
   }
 }
+
+fuzzer_test("brotli_fuzzer") {
+  sources = [
+    "fuzz/decode_fuzzer.cc",
+  ]
+  deps = [
+    ":dec",
+  ]
+  libfuzzer_options = [ "max_len=1280" ]
+}
diff --git a/third_party/brotli/README.chromium b/third_party/brotli/README.chromium
index 0c51a386..d8980318 100644
--- a/third_party/brotli/README.chromium
+++ b/third_party/brotli/README.chromium
@@ -11,9 +11,10 @@
 to decode WOFF 2.0 fonts.
 
 Local Modifications:
-- This only includes the enc/, dec/ and tools/ directories, the README.md and
-  the LICENSE files, removing unneeded directories such as docs, tests, and
-  tools.
+- This only includes the common/, dec/, enc/, fuzz/ and tools/ directories, the
+  README.md and the LICENSE files, removing unneeded directories such as docs,
+  java, and tests.
+- Auxiliary fuzzer runners removed from fuzz/
 - Line 19 of enc/cluster_inc.h was modified to eliminate build error on Windows.
 - BUILD.gn: Added.
 - brotli.gyp: Added.
diff --git a/third_party/brotli/fuzz/DEPS b/third_party/brotli/fuzz/DEPS
new file mode 100644
index 0000000..2e39551
--- /dev/null
+++ b/third_party/brotli/fuzz/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+third_party/brotli/dec",
+]
diff --git a/testing/libfuzzer/fuzzers/brotli_fuzzer.cc b/third_party/brotli/fuzz/decode_fuzzer.cc
similarity index 78%
rename from testing/libfuzzer/fuzzers/brotli_fuzzer.cc
rename to third_party/brotli/fuzz/decode_fuzzer.cc
index 4689416a..60c6f8e4 100644
--- a/testing/libfuzzer/fuzzers/brotli_fuzzer.cc
+++ b/third_party/brotli/fuzz/decode_fuzzer.cc
@@ -5,7 +5,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include "third_party/brotli/include/brotli/decode.h"
+#include <brotli/decode.h>
 
 // Entry point for LibFuzzer.
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
@@ -16,6 +16,11 @@
 
   const int kBufferSize = 1024;
   uint8_t* buffer = new uint8_t[kBufferSize];
+  /* The biggest "magic number" in brotli is 16MiB - 16, so no need to check
+     the cases with much longer output. */
+  const size_t total_out_limit = (addend == 0) ? (1 << 26) : (1 << 24);
+  size_t total_out = 0;
+
   BrotliDecoderState* state = BrotliDecoderCreateInstance(0, 0, 0);
 
   if (addend == 0)
@@ -31,10 +36,13 @@
     while (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
       size_t avail_out = kBufferSize;
       uint8_t* next_out = buffer;
-      size_t total_out;
       result = BrotliDecoderDecompressStream(
           state, &avail_in, &next_in, &avail_out, &next_out, &total_out);
+      if (total_out > total_out_limit)
+        break;
     }
+    if (total_out > total_out_limit)
+      break;
     if (result != BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT)
       break;
   }
diff --git a/third_party/polymer/v1_0/bower.json b/third_party/polymer/v1_0/bower.json
index 977ad9d..e82828e 100644
--- a/third_party/polymer/v1_0/bower.json
+++ b/third_party/polymer/v1_0/bower.json
@@ -18,7 +18,7 @@
     "iron-iconset-svg": "PolymerElements/iron-iconset-svg#1.0.11",
     "iron-icons": "PolymerElements/iron-icons#1.1.3",
     "iron-input": "PolymerElements/iron-input#1.0.10",
-    "iron-list": "PolymerElements/iron-list#1.3.12",
+    "iron-list": "PolymerElements/iron-list#1.4.4",
     "iron-location": "PolymerElements/iron-location#0.8.8",
     "iron-media-query": "PolymerElements/iron-media-query#1.0.8",
     "iron-menu-behavior": "PolymerElements/iron-menu-behavior#1.1.10",
diff --git a/third_party/polymer/v1_0/components-chromium/iron-list/bower.json b/third_party/polymer/v1_0/components-chromium/iron-list/bower.json
index 92d98b1..b6ad0999 100644
--- a/third_party/polymer/v1_0/components-chromium/iron-list/bower.json
+++ b/third_party/polymer/v1_0/components-chromium/iron-list/bower.json
@@ -7,7 +7,7 @@
     "list",
     "virtual-list"
   ],
-  "version": "1.3.12",
+  "version": "1.4.4",
   "homepage": "https://github.com/PolymerElements/iron-list",
   "authors": [
     "The Polymer Authors"
@@ -21,8 +21,8 @@
   "ignore": [],
   "dependencies": {
     "polymer": "Polymer/polymer#^1.1.0",
-    "iron-resizable-behavior": "polymerelements/iron-resizable-behavior#^1.0.0",
-    "iron-a11y-keys-behavior": "polymerelements/iron-a11y-keys-behavior#^1.0.0",
+    "iron-resizable-behavior": "PolymerElements/iron-resizable-behavior#^1.0.0",
+    "iron-a11y-keys-behavior": "PolymerElements/iron-a11y-keys-behavior#^1.0.0",
     "iron-scroll-target-behavior": "PolymerElements/iron-scroll-target-behavior#^1.1.0"
   },
   "devDependencies": {
diff --git a/third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js b/third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js
index 92a3550..9e9ec02 100644
--- a/third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js
+++ b/third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js
@@ -112,6 +112,20 @@
       multiSelection: {
         type: Boolean,
         value: false
+      },
+
+      /**
+       * The offset top from the scrolling element to the iron-list element.
+       * This value can be computed using the position returned by `getBoundingClientRect()`
+       * although it's preferred to use a constant value when possible.
+       *
+       * This property is useful when an external scrolling element is used and there's
+       * some offset between the scrolling element and the list.
+       * For example: a header is placed above the list.
+       */
+      scrollOffset: {
+        type: Number,
+        value: 0
       }
     },
 
@@ -119,7 +133,7 @@
       '_itemsChanged(items.*)',
       '_selectionEnabledChanged(selectionEnabled)',
       '_multiSelectionChanged(multiSelection)',
-      '_setOverflow(scrollTarget)'
+      '_setOverflow(scrollTarget, scrollOffset)'
     ],
 
     behaviors: [
@@ -311,10 +325,17 @@
     },
 
     /**
+     * The parent node for the _userTemplate.
+     */
+    get _itemsParent() {
+      return Polymer.dom(Polymer.dom(this._userTemplate).parentNode);
+    },
+
+    /**
      * The maximum scroll top value.
      */
     get _maxScrollTop() {
-      return this._estScrollHeight - this._viewportHeight + this._scrollerPaddingTop;
+      return this._estScrollHeight - this._viewportHeight + this._scrollOffset;
     },
 
     /**
@@ -335,7 +356,11 @@
     _virtualStartVal: 0,
 
     set _virtualStart(val) {
-      this._virtualStartVal = Math.min(this._maxVirtualStart, Math.max(this._minVirtualStart, val));
+      val = Math.min(this._maxVirtualStart, Math.max(this._minVirtualStart, val));
+      if (this.grid) {
+        val = val - (val % this._itemsPerRow);
+      }
+      this._virtualStartVal = val;
     },
 
     get _virtualStart() {
@@ -348,10 +373,14 @@
     _physicalStartVal: 0,
 
     set _physicalStart(val) {
-      this._physicalStartVal = val % this._physicalCount;
-      if (this._physicalStartVal < 0) {
-        this._physicalStartVal = this._physicalCount + this._physicalStartVal;
+      val = val % this._physicalCount;
+      if (val < 0) {
+        val = this._physicalCount + val;
       }
+      if (this.grid) {
+        val = val - (val % this._itemsPerRow);
+      }
+      this._physicalStartVal = val;
       this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % this._physicalCount;
     },
 
@@ -389,7 +418,7 @@
       if (this.grid) {
         return this._estRowsInView * this._rowHeight * this._maxPages;
       }
-      return this._viewportHeight * this._maxPages;
+      return this._viewportHeight === 0 ? Infinity : this._viewportHeight * this._maxPages;
     },
 
    /**
@@ -405,23 +434,24 @@
      * @type {number}
      */
     get firstVisibleIndex() {
-      if (this._firstVisibleIndexVal === null) {
-        var physicalOffset = Math.floor(this._physicalTop + this._scrollerPaddingTop);
+      var idx = this._firstVisibleIndexVal;
+      if (idx == null) {
+        var physicalOffset = this._physicalTop + this._scrollOffset;
 
-        this._firstVisibleIndexVal = this._iterateItems(
-          function(pidx, vidx) {
-            physicalOffset += this._getPhysicalSizeIncrement(pidx);
+        idx = this._iterateItems(function(pidx, vidx) {
+          physicalOffset += this._getPhysicalSizeIncrement(pidx);
 
-            if (physicalOffset > this._scrollPosition) {
-              return this.grid ? vidx - (vidx % this._itemsPerRow) : vidx;
-            }
-            // Handle a partially rendered final row in grid mode
-            if (this.grid && this._virtualCount - 1 === vidx) {
-              return vidx - (vidx % this._itemsPerRow);
-            }
-          }) || 0;
+          if (physicalOffset > this._scrollPosition) {
+            return this.grid ? vidx - (vidx % this._itemsPerRow) : vidx;
+          }
+          // Handle a partially rendered final row in grid mode
+          if (this.grid && this._virtualCount - 1 === vidx) {
+            return vidx - (vidx % this._itemsPerRow);
+          }
+        }) || 0;
+        this._firstVisibleIndexVal = idx;
       }
-      return this._firstVisibleIndexVal;
+      return idx;
     },
 
     /**
@@ -430,24 +460,23 @@
      * @type {number}
      */
     get lastVisibleIndex() {
-      if (this._lastVisibleIndexVal === null) {
+      var idx = this._lastVisibleIndexVal;
+      if (idx == null) {
         if (this.grid) {
-          var lastIndex = this.firstVisibleIndex + this._estRowsInView * this._itemsPerRow - 1;
-          this._lastVisibleIndexVal = Math.min(this._virtualCount, lastIndex);
+          idx = Math.min(this._virtualCount,
+              this.firstVisibleIndex + this._estRowsInView * this._itemsPerRow - 1);
         } else {
-          var physicalOffset = this._physicalTop;
+          var physicalOffset = this._physicalTop + this._scrollOffset;
           this._iterateItems(function(pidx, vidx) {
             if (physicalOffset < this._scrollBottom) {
-              this._lastVisibleIndexVal = vidx;
-            } else {
-              // Break _iterateItems
-              return true;
+              idx = vidx;
             }
             physicalOffset += this._getPhysicalSizeIncrement(pidx);
           });
         }
+        this._lastVisibleIndexVal = idx;
       }
-      return this._lastVisibleIndexVal;
+      return idx;
     },
 
     get _defaultScrollTarget() {
@@ -466,6 +495,10 @@
       return Math.ceil(this._physicalCount / this._itemsPerRow);
     },
 
+    get _scrollOffset() {
+      return this._scrollerPaddingTop + this.scrollOffset;
+    },
+
     ready: function() {
       this.addEventListener('focus', this._didFocus.bind(this), true);
     },
@@ -489,6 +522,10 @@
     _setOverflow: function(scrollTarget) {
       this.style.webkitOverflowScrolling = scrollTarget === this ? 'touch' : '';
       this.style.overflow = scrollTarget === this ? 'auto' : '';
+      // Clear cache.
+      this._lastVisibleIndexVal = null;
+      this._firstVisibleIndexVal = null;
+      this._debounceTemplate(this._render);
     },
 
     /**
@@ -498,8 +535,10 @@
      * @method updateViewportBoundaries
      */
     updateViewportBoundaries: function() {
+      var styles = window.getComputedStyle(this);
       this._scrollerPaddingTop = this.scrollTarget === this ? 0 :
-          parseInt(window.getComputedStyle(this)['padding-top'], 10);
+          parseInt(styles['padding-top'], 10);
+      this._isRTL = Boolean(styles.direction === 'rtl');
       this._viewportWidth = this.$.items.offsetWidth;
       this._viewportHeight = this._scrollTargetHeight;
       this.grid && this._updateGridMetrics();
@@ -520,7 +559,8 @@
 
       // Random access.
       if (Math.abs(delta) > this._physicalSize) {
-        var idxAdjustment =  Math.round(delta / this._physicalAverage) * this._itemsPerRow
+        delta = delta - this._scrollOffset;
+        var idxAdjustment = Math.round(delta / this._physicalAverage) * this._itemsPerRow;
         this._physicalTop = this._physicalTop + delta;
         this._virtualStart = this._virtualStart + idxAdjustment;
         this._physicalStart = this._physicalStart + idxAdjustment;
@@ -556,18 +596,19 @@
       var virtualStart = this._virtualStart;
       var virtualEnd = this._virtualEnd;
       var physicalCount = this._physicalCount;
-      var physicalTop = this._physicalTop + this._scrollerPaddingTop;
+      var top = this._physicalTop + this._scrollOffset;
+      var bottom = this._physicalBottom + this._scrollOffset;
       var scrollTop = this._scrollTop;
       var scrollBottom = this._scrollBottom;
 
       if (fromTop) {
         ith = this._physicalStart;
         lastIth = this._physicalEnd;
-        offsetContent = scrollTop - physicalTop;
+        offsetContent = scrollTop - top;
       } else {
         ith = this._physicalEnd;
         lastIth = this._physicalStart;
-        offsetContent = this._physicalBottom - scrollBottom;
+        offsetContent = bottom - scrollBottom;
       }
       while (true) {
         physicalItemHeight = this._getPhysicalSizeIncrement(ith);
@@ -581,11 +622,11 @@
             break;
           }
           // Check that the index is not visible.
-          if (physicalTop + physicalItemHeight >= scrollTop) {
+          if (top + physicalItemHeight >= scrollTop - this._scrollOffset) {
             break;
           }
           idxs.push(ith);
-          physicalTop = physicalTop + physicalItemHeight;
+          top = top + physicalItemHeight;
           ith = (ith + 1) % physicalCount;
         } else {
           // Check that index is within the valid range.
@@ -593,15 +634,15 @@
             break;
           }
           // Check that the index is not visible.
-          if (physicalTop + this._physicalSize - physicalItemHeight <= scrollBottom) {
+          if (top + this._physicalSize - physicalItemHeight <= scrollBottom) {
             break;
           }
           idxs.push(ith);
-          physicalTop = physicalTop - physicalItemHeight;
+          top = top - physicalItemHeight;
           ith = (ith === 0) ? physicalCount - 1 : ith - 1;
         }
       }
-      return { indexes: idxs, physicalTop: physicalTop - this._scrollerPaddingTop };
+      return { indexes: idxs, physicalTop: top - this._scrollOffset };
     },
 
     /**
@@ -643,7 +684,7 @@
         // First element child is item; Safari doesn't support children[0]
         // on a doc fragment.
         physicalItems[i] = inst.root.querySelector('*');
-        Polymer.dom(this).appendChild(inst.root);
+        this._itemsParent.appendChild(inst.root);
       }
       return physicalItems;
     },
@@ -654,15 +695,10 @@
      * @return {boolean} True if the pool was increased.
      */
     _increasePoolIfNeeded: function() {
-      // Base case 1: the list has no height.
-      if (this._viewportHeight === 0) {
-        return false;
-      }
       var self = this;
-      var isClientFull = this._physicalBottom >= this._scrollBottom &&
-          this._physicalTop <= this._scrollPosition;
-
-      // Base case 2: if the physical size is optimal and the list's client height is full
+      var isClientFull = this._physicalBottom + this._scrollOffset >= this._scrollBottom &&
+          this._physicalTop - this._scrollOffset <= this._scrollPosition;
+      // Base case 1: if the physical size is optimal and the list's client height is full
       // with physical items, don't increase the pool.
       if (this._physicalSize >= this._optPhysicalSize && isClientFull) {
         return false;
@@ -756,7 +792,7 @@
         props[this.selectedAs] = true;
         props.tabIndex = true;
         this._instanceProps = props;
-        this._userTemplate = Polymer.dom(this).querySelector('template');
+        this._userTemplate = this.queryEffectiveChildren('template');
 
         if (this._userTemplate) {
           this.templatize(this._userTemplate);
@@ -791,11 +827,13 @@
      * notifying parent path change on each row.
      */
     _forwardParentProp: function(prop, value) {
-      if (this._physicalItems) {
-        this._physicalItems.forEach(function(item) {
-          item._templateInstance[prop] = value;
-        }, this);
-      }
+      (this._physicalItems || [])
+        .concat([this._offscreenFocusedItem, this._focusBackfillItem])
+        .forEach(function(item) {
+          if (item) {
+            item._templateInstance[prop] = value;
+          }
+        });
     },
 
     /**
@@ -804,11 +842,13 @@
      * notifying parent.<path> path change on each row.
      */
     _forwardParentPath: function(path, value) {
-      if (this._physicalItems) {
-        this._physicalItems.forEach(function(item) {
-          item._templateInstance.notifyPath(path, value, true);
-        }, this);
-      }
+      (this._physicalItems || [])
+        .concat([this._offscreenFocusedItem, this._focusBackfillItem])
+        .forEach(function(item) {
+          if (item) {
+            item._templateInstance.notifyPath(path, value, true);
+          }
+        });
     },
 
     /**
@@ -866,7 +906,9 @@
         this._physicalItems = this._physicalItems || [];
         this._physicalSizes = this._physicalSizes || [];
         this._physicalStart = 0;
-        this._resetScrollPosition(0);
+        if (this._scrollTop > this._scrollOffset) {
+          this._resetScrollPosition(0);
+        }
         this._removeFocusedItem();
         this._debounceTemplate(this._render);
 
@@ -875,7 +917,6 @@
         this._virtualCount = this.items ? this.items.length : 0;
 
         this._debounceTemplate(this._render);
-
       } else {
         this._forwardItemPath(change.path.split('.').slice(1).join('.'), change.value);
       }
@@ -1040,6 +1081,9 @@
         this._iterateItems(function(pidx, vidx) {
           var modulus = vidx % this._itemsPerRow;
           var x = Math.floor((modulus * this._itemWidth) + rowOffset);
+          if (this._isRTL) {
+            x = x * -1;
+          }
           this.translate3d(x + 'px', y + 'px', 0, this._physicalItems[pidx]);
           if (this._shouldRenderNextRow(vidx)) {
             y += this._rowHeight;
@@ -1081,12 +1125,13 @@
     _adjustScrollPosition: function() {
       var deltaHeight = this._virtualStart === 0 ? this._physicalTop :
           Math.min(this._scrollPosition + this._physicalTop, 0);
-
-      if (deltaHeight) {
+      // Note: the delta can be positive or negative.
+      if (deltaHeight !== 0) {
         this._physicalTop = this._physicalTop - deltaHeight;
+        var scrollTop = this._scrollTop;
         // juking scroll position during interial scrolling on iOS is no bueno
-        if (!IOS_TOUCH_SCROLLING && this._physicalTop !== 0) {
-          this._resetScrollPosition(this._scrollTop - deltaHeight);
+        if (!IOS_TOUCH_SCROLLING && scrollTop > 0) {
+          this._resetScrollPosition(scrollTop - deltaHeight);
         }
       }
     },
@@ -1095,7 +1140,7 @@
      * Sets the position of the scroll.
      */
     _resetScrollPosition: function(pos) {
-      if (this.scrollTarget) {
+      if (this.scrollTarget && pos >= 0) {
         this._scrollTop = pos;
         this._scrollPosition = this._scrollTop;
       }
@@ -1147,7 +1192,6 @@
       if (typeof idx !== 'number' || idx < 0 || idx > this.items.length - 1) {
         return;
       }
-
       Polymer.dom.flush();
       // Items should have been rendered prior scrolling to an index.
       if (this._physicalCount === 0) {
@@ -1176,7 +1220,7 @@
       }
       this._updateScrollerSize(true);
       this._positionItems();
-      this._resetScrollPosition(this._physicalTop + this._scrollerPaddingTop + targetOffsetTop);
+      this._resetScrollPosition(this._physicalTop + this._scrollOffset + targetOffsetTop);
       this._increasePoolIfNeeded();
       // clear cached visible index.
       this._firstVisibleIndexVal = null;
@@ -1196,27 +1240,23 @@
      * when the element is resized.
      */
     _resizeHandler: function() {
-      // iOS fires the resize event when the address bar slides up
-      var delta = Math.abs(this._viewportHeight - this._scrollTargetHeight);
-      if (IOS && delta > 0 && delta < 100) {
-        return;
-      }
-      // In Desktop Safari 9.0.3, if the scroll bars are always shown,
-      // changing the scroll position from a resize handler would result in
-      // the scroll position being reset. Waiting 1ms fixes the issue.
-      Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', function() {
+      this._debounceTemplate(function() {
+        // Skip the resize event on touch devices when the address bar slides up.
+        var delta = Math.abs(this._viewportHeight - this._scrollTargetHeight);
         this.updateViewportBoundaries();
-        this._render();
+        if (('ontouchstart' in window || navigator.maxTouchPoints > 0) && delta > 0 && delta < 100) {
+          return;
+        }
         if (this._isVisible) {
+          // Reinstall the scroll event listener.
           this.toggleScrollListener(true);
-          if (this._physicalCount > 0) {
-            this._resetAverage();
-            this.scrollToIndex(this.firstVisibleIndex);
-          }
+          this._resetAverage();
+          this._render();
         } else {
+          // Uninstall the scroll event listener.
           this.toggleScrollListener(false);
         }
-      }.bind(this), 1));
+      }.bind(this));
     },
 
     _getModelFromItem: function(item) {
@@ -1343,7 +1383,8 @@
       }
       var modelTabIndex, activeElTabIndex;
       var target = Polymer.dom(e).path[0];
-      var activeEl = Polymer.dom(this.domHost ? this.domHost.root : document).activeElement;
+      var itemsHost = this._itemsParent.node.domHost;
+      var activeEl = Polymer.dom(itemsHost ? itemsHost.root : document).activeElement;
       var physicalItem = this._physicalItems[this._getPhysicalIndex(model[this.indexAs])];
       // Safari does not focus certain form controls via mouse
       // https://bugs.webkit.org/show_bug.cgi?id=118043
@@ -1456,7 +1497,7 @@
 
     _removeFocusedItem: function() {
       if (this._offscreenFocusedItem) {
-        Polymer.dom(this).removeChild(this._offscreenFocusedItem);
+        this._itemsParent.removeChild(this._offscreenFocusedItem);
       }
       this._offscreenFocusedItem = null;
       this._focusBackfillItem = null;
@@ -1475,7 +1516,7 @@
         // Create a physical item.
         var stampedTemplate = this.stamp(null);
         this._focusBackfillItem = stampedTemplate.root.querySelector('*');
-        Polymer.dom(this).appendChild(stampedTemplate.root);
+        this._itemsParent.appendChild(stampedTemplate.root);
       }
       // Set the offcreen focused physical item.
       this._offscreenFocusedItem = this._physicalItems[pidx];
@@ -1496,17 +1537,27 @@
       // Get the new physical index for the focused index.
       pidx = this._getPhysicalIndex(fidx);
 
-      if (pidx != null) {
+      var onScreenItem = this._physicalItems[pidx];
+      if (!onScreenItem) {
+        return;
+      }
+      var onScreenInstance = onScreenItem._templateInstance;
+      var offScreenInstance = this._offscreenFocusedItem._templateInstance;
+      // Restores the physical item only when it has the same model 
+      // as the offscreen one. Use key for comparison since users can set
+      // a new item via set('items.idx').
+      if (onScreenInstance.__key__ === offScreenInstance.__key__) {
         // Flip the focus backfill.
-        this._focusBackfillItem = this._physicalItems[pidx];
-        this._focusBackfillItem._templateInstance.tabIndex = -1;
+        this._focusBackfillItem = onScreenItem;
+        onScreenInstance.tabIndex = -1;
         // Restore the focused physical item.
         this._physicalItems[pidx] = this._offscreenFocusedItem;
-        // Reset the offscreen focused item.
-        this._offscreenFocusedItem = null;
         // Hide the physical item that backfills.
         this.translate3d(0, HIDDEN_Y, 0, this._focusBackfillItem);
+      } else {
+        this._focusBackfillItem = null;
       }
+      this._offscreenFocusedItem = null;
     },
 
     _didFocus: function(e) {
diff --git a/third_party/polymer/v1_0/components-chromium/iron-list/iron-list.html b/third_party/polymer/v1_0/components-chromium/iron-list/iron-list.html
index 33e8bad..a13d8b08 100644
--- a/third_party/polymer/v1_0/components-chromium/iron-list/iron-list.html
+++ b/third_party/polymer/v1_0/components-chromium/iron-list/iron-list.html
@@ -48,11 +48,12 @@
       flex: 1 1 auto;
     }
   </style>
-
   <app-toolbar>App name</app-toolbar>
   <iron-list items="[[items]]">
     <template>
-      ...
+      <div>
+        ...
+      </div>
     </template>
   </iron-list>
 </template>
@@ -71,7 +72,9 @@
   </style>
   <iron-list items="[[items]]">
     <template>
-      ...
+      <div>
+        ...
+      </div>
     </template>
   </iron-list>
 </template>
@@ -103,15 +106,20 @@
 <body>
   <template is="dom-bind">
     <app-toolbar>App name</app-toolbar>
-    <iron-list target="document" items="[[items]]">
+    <iron-list scroll-target="document" items="[[items]]">
       <template>
-        ...
+        <div>
+          ...
+        </div>
       </template>
     </iron-list>
   </template>
 </body>
 ```
 
+`iron-list` must be given a `<template>` which contains exactly one element. In the examples
+above we used a `<div>`, but you can provide any element (including custom elements).
+
 ### Template model
 
 List item templates should bind to template models of the following structure:
@@ -210,13 +218,13 @@
 ### When should `<iron-list>` be used?
 
 `iron-list` should be used when a page has significantly more DOM nodes than the ones
-visible on the screen. e.g. the page has 500 nodes, but only 20 are visible at the time.
+visible on the screen. e.g. the page has 500 nodes, but only 20 are visible at a time.
 This is why we refer to it as a `virtual` list. In this case, a `dom-repeat` will still
 create 500 nodes which could slow down the web app, but `iron-list` will only create 20.
 
 However, having an `iron-list` does not mean that you can load all the data at once.
-Say, you have a million records in the database, you want to split the data into pages
-so you can bring a page at the time. The page could contain 500 items, and iron-list
+Say you have a million records in the database, you want to split the data into pages
+so you can bring in a page at the time. The page could contain 500 items, and iron-list
 will only render 20.
 
 @group Iron Element
@@ -230,7 +238,6 @@
     <style>
       :host {
         display: block;
-        position: relative;
       }
 
       @media only screen and (-webkit-max-device-pixel-ratio: 1) {
@@ -246,7 +253,7 @@
 
       :host(:not([grid])) #items > ::content > * {
         width: 100%;
-      };
+      }
 
       #items > ::content > * {
         box-sizing: border-box;
diff --git a/third_party/polymer/v1_0/components_summary.txt b/third_party/polymer/v1_0/components_summary.txt
index a4e7b0be..6ef41c5a 100644
--- a/third_party/polymer/v1_0/components_summary.txt
+++ b/third_party/polymer/v1_0/components_summary.txt
@@ -96,9 +96,9 @@
 
 Name: iron-list
 Repository: https://github.com/PolymerElements/iron-list.git
-Tree: v1.3.12
-Revision: 856ebcf90b2834aae728fc295b5dd5aa5ebc18ca
-Tree link: https://github.com/PolymerElements/iron-list/tree/v1.3.12
+Tree: v1.4.4
+Revision: 93490bcb5baba88d642d1f550dad3ed52b2c864f
+Tree link: https://github.com/PolymerElements/iron-list/tree/v1.4.4
 
 Name: iron-location
 Repository: https://github.com/PolymerElements/iron-location.git