diff --git a/base/BUILD.gn b/base/BUILD.gn
index 69dfcc5..24caaa5 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -1961,6 +1961,7 @@
     "memory/aligned_memory_unittest.cc",
     "memory/discardable_shared_memory_unittest.cc",
     "memory/linked_ptr_unittest.cc",
+    "memory/memory_coordinator_client_registry_unittest.cc",
     "memory/memory_pressure_listener_unittest.cc",
     "memory/memory_pressure_monitor_chromeos_unittest.cc",
     "memory/memory_pressure_monitor_mac_unittest.cc",
diff --git a/base/memory/memory_coordinator_client.h b/base/memory/memory_coordinator_client.h
index 148f4c1..d24d3e7c 100644
--- a/base/memory/memory_coordinator_client.h
+++ b/base/memory/memory_coordinator_client.h
@@ -12,16 +12,22 @@
 // OVERVIEW:
 //
 // MemoryCoordinatorClient is an interface which a component can implement to
-// respond to memory state changes. Unlike MemoryPressureListener, this is a
-// stateful mechanism and clients receive notifications only when memory states
-// are changed. State transitions are throttled to avoid thrashing; the exact
-// throttling period is platform dependent, but will be at least 5-10 seconds.
-// Clients are expected to make changes in memory usage that persist for the
-// duration of the memory state.
+// adjust "future allocation" and "existing allocation". For "future allocation"
+// it provides a callback to observe memory state changes, and for "existing
+// allocation" it provides a callback to purge memory.
+//
+// Unlike MemoryPressureListener, memory state changes are stateful. State
+// transitions are throttled to avoid thrashing; the exact throttling period is
+// platform dependent, but will be at least 5-10 seconds. When a state change
+// notification is dispatched, clients are expected to update their allocation
+// policies (e.g. setting cache limit) that persist for the duration of the
+// memory state. Note that clients aren't expected to free up memory on memory
+// state changes. Clients should wait for a separate purge request to free up
+// memory. Purging requests will be throttled as well.
 
 // MemoryState is an indicator that processes can use to guide their memory
-// allocation policies. For example, a process that receives the suspended
-// state can use that as as signal to drop memory caches.
+// allocation policies. For example, a process that receives the throttled
+// state can use that as as signal to decrease memory cache limits.
 // NOTE: This enum is used to back an UMA histogram, and therefore should be
 // treated as append-only.
 enum class MemoryState : int {
@@ -29,14 +35,13 @@
   UNKNOWN = -1,
   // No memory constraints.
   NORMAL = 0,
-  // Running and interactive but allocation should be throttled.
-  // Clients should free up any memory that is used as an optimization but
-  // that is not necessary for the process to run (e.g. caches).
+  // Running and interactive but memory allocation should be throttled.
+  // Clients should set lower budget for any memory that is used as an
+  // optimization but that is not necessary for the process to run.
+  // (e.g. caches)
   THROTTLED = 1,
   // Still resident in memory but core processing logic has been suspended.
-  // Clients should free up any memory that is used as an optimization, or
-  // any memory whose contents can be reproduced when transitioning out of
-  // the suspended state (e.g. parsed resource that can be reloaded from disk).
+  // In most cases, OnPurgeMemory() will be called before entering this state.
   SUSPENDED = 2,
 };
 
@@ -54,11 +59,18 @@
   // UNKNOWN. General guidelines are:
   //  * NORMAL:    Restore the default settings for memory allocation/usage if
   //               it has changed.
-  //  * THROTTLED: Use smaller limits for memory allocations and caches.
-  //  * SUSPENDED: Purge memory.
-  virtual void OnMemoryStateChange(MemoryState state) = 0;
+  //  * THROTTLED: Use smaller limits for future memory allocations. You don't
+  //               need to take any action on existing allocations.
+  //  * SUSPENDED: Use much smaller limits for future memory allocations. You
+  //               don't need to take any action on existing allocations.
+  virtual void OnMemoryStateChange(MemoryState state) {}
 
-protected:
+  // Called to purge memory.
+  // This callback should free up any memory that is used as an optimization, or
+  // any memory whose contents can be reproduced.
+  virtual void OnPurgeMemory() {}
+
+ protected:
   virtual ~MemoryCoordinatorClient() {}
 };
 
diff --git a/base/memory/memory_coordinator_client_registry.cc b/base/memory/memory_coordinator_client_registry.cc
index 57eedc6..6706458 100644
--- a/base/memory/memory_coordinator_client_registry.cc
+++ b/base/memory/memory_coordinator_client_registry.cc
@@ -34,4 +34,8 @@
                    &base::MemoryCoordinatorClient::OnMemoryStateChange, state);
 }
 
+void MemoryCoordinatorClientRegistry::PurgeMemory() {
+  clients_->Notify(FROM_HERE, &base::MemoryCoordinatorClient::OnPurgeMemory);
+}
+
 }  // namespace base
diff --git a/base/memory/memory_coordinator_client_registry.h b/base/memory/memory_coordinator_client_registry.h
index 8b5e7b5..e2c81b7 100644
--- a/base/memory/memory_coordinator_client_registry.h
+++ b/base/memory/memory_coordinator_client_registry.h
@@ -39,6 +39,9 @@
   // Notify clients of a memory state change.
   void Notify(MemoryState state);
 
+  // Requests purging memory.
+  void PurgeMemory();
+
  private:
   friend struct DefaultSingletonTraits<MemoryCoordinatorClientRegistry>;
 
diff --git a/base/memory/memory_coordinator_client_registry_unittest.cc b/base/memory/memory_coordinator_client_registry_unittest.cc
new file mode 100644
index 0000000..37ed7673d
--- /dev/null
+++ b/base/memory/memory_coordinator_client_registry_unittest.cc
@@ -0,0 +1,58 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/memory_coordinator_client_registry.h"
+
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class TestMemoryCoordinatorClient : public MemoryCoordinatorClient {
+ public:
+  void OnMemoryStateChange(MemoryState state) override { state_ = state; }
+
+  void OnPurgeMemory() override { ++purge_count_; }
+
+  MemoryState state() const { return state_; }
+  size_t purge_count() const { return purge_count_; }
+
+ private:
+  MemoryState state_ = MemoryState::UNKNOWN;
+  size_t purge_count_ = 0;
+};
+
+void RunUntilIdle() {
+  base::RunLoop loop;
+  loop.RunUntilIdle();
+}
+
+TEST(MemoryCoordinatorClientRegistryTest, NotifyStateChange) {
+  MessageLoop loop;
+  auto* registry = MemoryCoordinatorClientRegistry::GetInstance();
+  TestMemoryCoordinatorClient client;
+  registry->Register(&client);
+  registry->Notify(MemoryState::THROTTLED);
+  RunUntilIdle();
+  ASSERT_EQ(MemoryState::THROTTLED, client.state());
+  registry->Unregister(&client);
+}
+
+TEST(MemoryCoordinatorClientRegistryTest, PurgeMemory) {
+  MessageLoop loop;
+  auto* registry = MemoryCoordinatorClientRegistry::GetInstance();
+  TestMemoryCoordinatorClient client;
+  registry->Register(&client);
+  registry->PurgeMemory();
+  RunUntilIdle();
+  ASSERT_EQ(1u, client.purge_count());
+  registry->Unregister(&client);
+}
+
+}  // namespace
+
+}  // namespace base
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 822a2c3..d2403d2 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -178,19 +178,19 @@
   <message name="IDS_FILE_BROWSER_STATUS_COLUMN_LABEL" desc="Status column label.  This column reflects the cloud import status of files.  The status is an icon depicting whether a file has been imported, and if so, where to - for example, a Drive icon if a file has been imported to Google Drive.  For unimported files, this column is blank. ">
     Status
   </message>
-  <message name="IDS_FILE_BROWSER_TOTAL_FILE_COUNT_LABEL" desc="Total count of files that user selects in Files app">
+  <message name="IDS_FILE_BROWSER_TOTAL_FILE_COUNT_LABEL" desc="Total count of files that user selects in the Files app">
     Total file count
   </message>
-  <message name="IDS_FILE_BROWSER_TOTAL_FILE_SIZE_LABEL" desc="Total size of files that user selects in Files app">
+  <message name="IDS_FILE_BROWSER_TOTAL_FILE_SIZE_LABEL" desc="Total size of files that user selects in the Files app">
     Total file size
   </message>
-  <message name="IDS_FILE_BROWSER_IMAGE_RESOLUTION_COLUMN_LABEL" desc="Image resolution of a file that user selects in Files app">
+  <message name="IDS_FILE_BROWSER_IMAGE_RESOLUTION_COLUMN_LABEL" desc="Image resolution of a file that user selects in the Files app">
     Image resolution
   </message>
-  <message name="IDS_FILE_BROWSER_MEDIA_TITLE_COLUMN_LABEL" desc="Artist of a music file that user selects in Files app">
+  <message name="IDS_FILE_BROWSER_MEDIA_TITLE_COLUMN_LABEL" desc="Artist of a music file that user selects in the Files app">
     Title
   </message>
-  <message name="IDS_FILE_BROWSER_MEDIA_ARTIST_COLUMN_LABEL" desc="Title of a music file that user selects in Files app">
+  <message name="IDS_FILE_BROWSER_MEDIA_ARTIST_COLUMN_LABEL" desc="Title of a music file that user selects in the Files app">
     Artist
   </message>
   <message name="IDS_FILE_BROWSER_TYPE_COLUMN_LABEL" desc="Type column label.">
@@ -268,7 +268,7 @@
   <message name="IDS_FILE_BROWSER_CLOSE_VOLUME_BUTTON_LABEL" desc="Title of the action for closing either an archive volume or a volume provided by an extension.">
     Close
   </message>
-  <message name="IDS_FILE_BROWSER_ADD_NEW_SERVICES_BUTTON_LABEL" desc="Title of the button in the left nav to add new services (file systems) to Files app.">
+  <message name="IDS_FILE_BROWSER_ADD_NEW_SERVICES_BUTTON_LABEL" desc="Title of the button in the left nav to add new services (file systems) to the Files app.">
     Add new services
   </message>
   <message name="IDS_FILE_BROWSER_INSTALL_NEW_EXTENSION_LABEL" desc="Title of the menu item for installing new extensions from the web store.">
@@ -741,7 +741,7 @@
   <message name="IDS_FILE_BROWSER_SUGGEST_DIALOG_TITLE" desc="Title of the suggest app dialog, which shows the list of the apps which supports the selected file.">
     Select an app to open this file
   </message>
-  <message name="IDS_FILE_BROWSER_SUGGEST_DIALOG_FOR_PROVIDERS_TITLE" desc="Title of the suggest app dialog, which shows the list of the apps that can be added to the left nav of Files app.">
+  <message name="IDS_FILE_BROWSER_SUGGEST_DIALOG_FOR_PROVIDERS_TITLE" desc="Title of the suggest app dialog, which shows the list of the apps that can be added to the left nav of the Files app.">
     Available services
   </message>
   <message name="IDS_FILE_BROWSER_SUGGEST_DIALOG_LINK_TO_WEBSTORE" desc="Text of the link to the app list on Chrome Webstore, which shows the more apps than in the suggest app dialog.">
@@ -1003,10 +1003,10 @@
   <message name="IDS_FILE_BROWSER_NO_TASK_FOR_DMG" desc="Message shown when user tries to open a windows executable file, which we can't handle.">
     This file is designed for a computer using Macintosh software. This is not compatible with your device which runs Chrome OS. Please search the <ph name="BEGIN_LINK">&lt;a target='_blank' href='$1'&gt;<ex>&lt;a target='_blank' href='$1'&gt;</ex></ph>Chrome Web Store<ph name="END_LINK">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph> for a suitable replacement app.<ph name="BEGIN_LINK_HELP">&lt;a target='_blank' href='$2'&gt;<ex>&lt;a target='_blank' href='$2'&gt;</ex></ph>Learn More<ph name="END_LINK_HELP">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph>
   </message>
-  <message name="IDS_FILE_BROWSER_NO_TASK_FOR_CRX_TITLE" desc="Message shown when a user tries to open a *.crx file, which we don't handle in Files.app.">
+  <message name="IDS_FILE_BROWSER_NO_TASK_FOR_CRX_TITLE" desc="Message shown when a user tries to open a *.crx file, which we don't handle in the Files app.">
     Wait just a sec
   </message>
-  <message name="IDS_FILE_BROWSER_NO_TASK_FOR_CRX" desc="Message shown when a user tries to open a *.crx file, which we don't handle in Files.app.">
+  <message name="IDS_FILE_BROWSER_NO_TASK_FOR_CRX" desc="Message shown when a user tries to open a *.crx file, which we don't handle in the Files app.">
     We're constantly looking for ways to make your browsing safer. Previously, any website could prompt you to add an extension into your browser. In the latest versions of Google Chrome, you must explicitly tell Chrome that you want to install these extensions by adding them through the Extensions page. <ph name="BEGIN_LINK">&lt;a target='_blank' href='https://support.google.com/chrome_webstore/answer/2664769?p=crx_warning&amp;rd=1'&gt;</ph>Learn more<ph name="END_LINK">&lt;/a&gt;</ph>
   </message>
 
@@ -1160,10 +1160,10 @@
   <message name="IDS_FILE_BROWSER_THUMBNAIL_VIEW_TOOLTIP" desc="Tooltip for the Thumbnail View button.">
     Thumbnail view
   </message>
-  <message name="IDS_FILE_BROWSER_SORT_BUTTON_TOOLTIP" desc="Tooltip for the button which provides sort options in Files.app.">
+  <message name="IDS_FILE_BROWSER_SORT_BUTTON_TOOLTIP" desc="Tooltip for the button which provides sort options in the Files app.">
     Sort options
   </message>
-  <message name="IDS_FILE_BROWSER_GEAR_BUTTON_TOOLTIP" desc="Tooltip for the gear button in Files.app.">
+  <message name="IDS_FILE_BROWSER_GEAR_BUTTON_TOOLTIP" desc="Tooltip for the gear button in the Files app.">
     Settings
   </message>
 
@@ -4188,13 +4188,13 @@
   <message name="IDS_REMOVABLE_DEVICE_DETECTION_TITLE" desc="Text of notification message which is shown when user inserts removable device (SD card, USB key...)">
     Removable device detected
   </message>
-  <message name="IDS_REMOVABLE_DEVICE_NAVIGATION_MESSAGE" desc="Text of notification message to navigate users to Files.app. If user clicks a button in the notification, Files.app opens.">
+  <message name="IDS_REMOVABLE_DEVICE_NAVIGATION_MESSAGE" desc="Text of notification message to navigate users to the Files app. If user clicks a button in the notification, the Files app opens.">
     Explore the device's content in the Files app.
   </message>
-  <message name="IDS_REMOVABLE_DEVICE_NAVIGATION_MESSAGE_READONLY_POLICY" desc="Text of notification message to navigate users to Files.app when attaching a removable media, but the administrator's poilcy forbids writing data to them. If user clicks a button in the notification, Files.app opens.">
+  <message name="IDS_REMOVABLE_DEVICE_NAVIGATION_MESSAGE_READONLY_POLICY" desc="Text of notification message to navigate users to the Files app when attaching a removable media, but the administrator's poilcy forbids writing data to them. If user clicks a button in the notification, the Files app opens.">
     Explore the device's content in the Files app. The content is restricted by an admin and can’t be modified.
   </message>
-  <message name="IDS_REMOVABLE_DEVICE_NAVIGATION_BUTTON_LABEL" desc="Text of a button label in a notification to navigate users to Files.app. If user clicks the button, Files.app opens.">
+  <message name="IDS_REMOVABLE_DEVICE_NAVIGATION_BUTTON_LABEL" desc="Text of a button label in a notification to navigate users to the Files app. If user clicks the button, the Files app opens.">
     Open Files app
   </message>
   <message name="IDS_REMOVABLE_DEVICE_IMPORT_MESSAGE" desc="Text of notification message to start the media import flow. If user clicks a button in the notification, the import flow begins.">
@@ -4215,7 +4215,7 @@
   <message name="IDS_DEVICE_UNKNOWN_MESSAGE" desc="Text of notification message which is shown when user inserts removable device (SD card, USB key...)">
    Sorry, the device <ph name="DEVICE_LABEL">$1<ex>My SD card</ex></ph> could not be recognized.
   </message>
-  <message name="IDS_DEVICE_UNKNOWN_BUTTON_LABEL" desc="Text of a button label in a notification to format the device. If user clicks the button, Files.app opens and focus on unknown the device.">
+  <message name="IDS_DEVICE_UNKNOWN_BUTTON_LABEL" desc="Text of a button label in a notification to format the device. If user clicks the button, the Files app opens and focus on unknown the device.">
     Format this device
   </message>
   <message name="IDS_DEVICE_UNKNOWN_DEFAULT_MESSAGE" desc="Text of notification message which is shown when user inserts removable device (SD card, USB key...)">
diff --git a/chrome/browser/chromeos/extensions/file_manager/event_router.cc b/chrome/browser/chromeos/extensions/file_manager/event_router.cc
index 7ce2f1c..61bf079 100644
--- a/chrome/browser/chromeos/extensions/file_manager/event_router.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/event_router.cc
@@ -256,7 +256,7 @@
   }
 }
 
-// Obtains whether the Files.app should handle the volume or not.
+// Obtains whether the Files app should handle the volume or not.
 bool ShouldShowNotificationForVolume(
     Profile* profile,
     const DeviceEventRouter& device_event_router,
@@ -283,7 +283,7 @@
   if (IsRecoveryToolRunning(profile))
     return false;
 
-  // If the disable-default-apps flag is on, Files.app is not opened
+  // If the disable-default-apps flag is on, the Files app is not opened
   // automatically on device mount not to obstruct the manual test.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kDisableDefaultApps)) {
@@ -421,7 +421,7 @@
 
   DLOG_IF(WARNING, !file_watchers_.empty())
       << "Not all file watchers are "
-      << "removed. This can happen when Files.app is open during shutdown.";
+      << "removed. This can happen when the Files app is open during shutdown.";
   file_watchers_.clear();
   if (!profile_) {
     NOTREACHED();
@@ -721,7 +721,7 @@
     // 1. /a/b DELETE:DIRECTORY
     // 2. /a DELETE:DIRECTORY
     //
-    // /a/b is watched, and /a is deleted from Files.app.
+    // /a/b is watched, and /a is deleted from the Files app.
     // 1. /a DELETE:DIRECTORY
     if (contains_directory_deletion) {
       // Expand the deleted directory path with watched paths.
@@ -808,7 +808,7 @@
     // Removes the detailed information, if the list size is more than
     // kDirectoryChangeEventMaxDetailInfoSize, since passing large list
     // and processing it may cause more itme.
-    // This will be invoked full-refresh in Files.app.
+    // This will be invoked full-refresh in the Files app.
     list = NULL;
   }
 
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h
index d7685c2..13be052 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h
@@ -40,7 +40,7 @@
 };
 
 // Implements the chrome.fileManagerPrivate.getPreferences method.
-// Gets settings for Files.app.
+// Gets settings for the Files app.
 class FileManagerPrivateGetPreferencesFunction
     : public UIThreadExtensionFunction {
  public:
@@ -54,7 +54,7 @@
 };
 
 // Implements the chrome.fileManagerPrivate.setPreferences method.
-// Sets settings for Files.app.
+// Sets settings for the Files app.
 class FileManagerPrivateSetPreferencesFunction
     : public UIThreadExtensionFunction {
  public:
diff --git a/chrome/browser/chromeos/file_manager/file_tasks.h b/chrome/browser/chromeos/file_manager/file_tasks.h
index 8343ed65..0e4b9c0 100644
--- a/chrome/browser/chromeos/file_manager/file_tasks.h
+++ b/chrome/browser/chromeos/file_manager/file_tasks.h
@@ -7,7 +7,7 @@
 // WHAT ARE FILE TASKS?
 //
 // File tasks are representation of actions that can be performed over the
-// currently selected files from Files.app. A task can be either of:
+// currently selected files from the Files app. A task can be either of:
 //
 // 1) Chrome extension or app, registered via "file_handlers" or
 // "file_browser_handlers" in manifest.json (ex. Text.app). This information
@@ -17,10 +17,10 @@
 // https://developer.chrome.com/extensions/manifest.html#file_handlers
 // https://developer.chrome.com/extensions/fileBrowserHandler.html
 //
-// 2) Built-in handlers provided from Files.app. Files.app provides lots of
-// file_browser_handlers, such as "play", "mount-archive".  These built-in
-// handlers are often handled in special manners inside Files.app.
-// This information also comes from FileBrowserHandler::GetHandlers().
+// 2) Built-in handlers provided from the Files app. The Files app provides
+// lots of file_browser_handlers, such as "play", "mount-archive".  These
+// built-in handlers are often handled in special manners inside the Files
+// app. This information also comes from FileBrowserHandler::GetHandlers().
 //
 // See also:
 // ui/file_manager/file_manager/manifest.json
@@ -32,7 +32,7 @@
 // See also:
 // https://chrome.google.com/webstore/category/collection/drive_apps
 //
-// For example, if the user is now selecting a JPEG file, Files.app will
+// For example, if the user is now selecting a JPEG file, the Files app will
 // receive file tasks represented as a JSON object via
 // chrome.fileManagerPrivate.getFileTasks() API, which look like:
 //
@@ -54,7 +54,7 @@
 // ]
 //
 // The first file task is a Drive app. The second file task is a built-in
-// handler from Files.app.
+// handler from the Files app.
 //
 // WHAT ARE TASK IDS?
 //
@@ -77,7 +77,7 @@
 // <app-id> is either of Chrome Extension/App ID or Drive App ID. For some
 // reason, Chrome Extension/App IDs and Drive App IDs look differently. As of
 // writing, the former looks like "hhaomjibdihmijegdhdafkllkbggdgoj"
-// (Files.app) and the latter looks like "419782477519" (Pixlr Editor).
+// (the Files app) and the latter looks like "419782477519" (Pixlr Editor).
 //
 // <task-type> is either of
 // - "file" - File browser handler - app/extension declaring
@@ -100,9 +100,9 @@
 // chrome.fileManagerPrivate.executeTasks() is used to open a file with a
 // handler (Chrome Extension/App or Drive App).
 //
-// Some built-in handlers such as "play" are handled internally in Files.app.
-// "mount-archive" is handled very differently. The task execution business
-// should be simplified: crbug.com/267313
+// Some built-in handlers such as "play" are handled internally in the Files
+// app. "mount-archive" is handled very differently. The task execution
+// business should be simplified: crbug.com/267313
 //
 // See also:
 // ui/file_manager/file_manager/foreground/js/file_tasks.js
@@ -310,11 +310,11 @@
 // |drive_app_registry| can be NULL if the drive app registry is not
 // present.
 //
-// If |entries| contains a Google document, only the internal tasks of
-// Files.app (i.e., tasks having the app ID of Files.app) are listed.
+// If |entries| contains a Google document, only the internal tasks of the
+// Files app (i.e., tasks having the app ID of the Files app) are listed.
 // This is to avoid dups between Drive app tasks and an internal handler that
-// Files.app provides, and to avoid listing normal file handler and file browser
-// handler tasks, which can handle only normal files.
+// the Files app provides, and to avoid listing normal file handler and file
+// browser handler tasks, which can handle only normal files.
 void FindAllTypesOfTasks(Profile* profile,
                          const drive::DriveAppRegistry* drive_app_registry,
                          const std::vector<extensions::EntryInfo>& entries,
diff --git a/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc b/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc
index a037ab1..f10ac18 100644
--- a/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc
@@ -313,13 +313,13 @@
   EXPECT_TRUE(tasks[1].is_default());
 }
 
-// Test that Files.app's internal file browser handler is chosen as default
-// even if nothing is set in the preferences.
+// Test that internal file browser handler of the Files app is chosen as
+// default even if nothing is set in the preferences.
 TEST(FileManagerFileTasksTest, ChooseAndSetDefaultTask_FallbackFileBrowser) {
   TestingPrefServiceSimple pref_service;
   RegisterDefaultTaskPreferences(&pref_service);
 
-  // Files.app's internal file browser handler was found for "foo.txt".
+  // The internal file browser handler of the Files app was found for "foo.txt".
   TaskDescriptor files_app_task(kFileManagerAppId,
                                 TASK_TYPE_FILE_BROWSER_HANDLER,
                                 "view-in-browser");
@@ -821,9 +821,9 @@
   bar_app.SetID(kBarId);
   extension_service_->AddExtension(bar_app.Build().get());
 
-  // Files.app can handle ".gdoc" files.
+  // The Files app can handle ".gdoc" files.
   // The ID "kFileManagerAppId" used here is precisely the one that identifies
-  // the Chrome OS Files.app application.
+  // the Chrome OS Files app application.
   extensions::ExtensionBuilder files_app;
   files_app.SetManifest(
       extensions::DictionaryBuilder()
@@ -847,7 +847,7 @@
   files_app.SetID(kFileManagerAppId);
   extension_service_->AddExtension(files_app.Build().get());
 
-  // Find apps for a ".gdoc file". Only the built-in handler of Files.apps
+  // Find apps for a ".gdoc file". Only the built-in handler of the Files apps
   // should be found.
   std::vector<extensions::EntryInfo> entries;
   std::vector<GURL> file_urls;
diff --git a/chrome/browser/chromeos/file_manager/volume_manager.cc b/chrome/browser/chromeos/file_manager/volume_manager.cc
index ab1e2166..31626509 100644
--- a/chrome/browser/chromeos/file_manager/volume_manager.cc
+++ b/chrome/browser/chromeos/file_manager/volume_manager.cc
@@ -54,9 +54,9 @@
 // Registers |path| as the "Downloads" folder to the FileSystem API backend.
 // If another folder is already mounted. It revokes and overrides the old one.
 bool RegisterDownloadsMountPoint(Profile* profile, const base::FilePath& path) {
-  // Although we show only profile's own "Downloads" folder in Files.app,
+  // Although we show only profile's own "Downloads" folder in the Files app,
   // in the backend we need to mount all profile's download directory globally.
-  // Otherwise, Files.app cannot support cross-profile file copies, etc.
+  // Otherwise, the Files app cannot support cross-profile file copies, etc.
   // For this reason, we need to register to the global GetSystemInstance().
   const std::string mount_point_name =
       file_manager::util::GetDownloadsMountPointName(profile);
diff --git a/chrome/browser/dom_distiller/dom_distiller_viewer_source_browsertest.cc b/chrome/browser/dom_distiller/dom_distiller_viewer_source_browsertest.cc
index 0628eef..a06ce1709 100644
--- a/chrome/browser/dom_distiller/dom_distiller_viewer_source_browsertest.cc
+++ b/chrome/browser/dom_distiller/dom_distiller_viewer_source_browsertest.cc
@@ -36,7 +36,7 @@
 #include "components/dom_distiller/core/url_utils.h"
 #include "components/leveldb_proto/testing/fake_db.h"
 #include "components/strings/grit/components_strings.h"
-#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/url_data_source.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -227,9 +227,9 @@
       browser()->tab_strip_model()->GetActiveWebContents();
   ASSERT_TRUE(contents_after_nav != NULL);
   EXPECT_EQ(url, contents_after_nav->GetLastCommittedURL());
-  const content::RenderViewHost* render_view_host =
-      contents_after_nav->GetRenderViewHost();
-  EXPECT_EQ(0, render_view_host->GetEnabledBindings());
+  const content::RenderFrameHost* render_frame_host =
+      contents_after_nav->GetMainFrame();
+  EXPECT_EQ(0, render_frame_host->GetEnabledBindings());
   EXPECT_EQ(expected_mime_type, contents_after_nav->GetContentsMimeType());
 }
 
diff --git a/chrome/browser/engagement/site_engagement_metrics.cc b/chrome/browser/engagement/site_engagement_metrics.cc
index 83db74ed..ab2f62b62 100644
--- a/chrome/browser/engagement/site_engagement_metrics.cc
+++ b/chrome/browser/engagement/site_engagement_metrics.cc
@@ -6,6 +6,7 @@
 
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
+#include "chrome/browser/engagement/site_engagement_score.h"
 
 namespace {
 
@@ -42,6 +43,9 @@
 const char SiteEngagementMetrics::kEngagementScoreHistogramHTTPS[] =
     "SiteEngagementService.EngagementScore.HTTPS";
 
+const char SiteEngagementMetrics::kEngagementScoreHistogramIsZero[] =
+    "SiteEngagementService.EngagementScore.IsZero";
+
 const char SiteEngagementMetrics::kOriginsWithMaxEngagementHistogram[] =
     "SiteEngagementService.OriginsWithMaxEngagement";
 
@@ -95,9 +99,11 @@
   for (size_t i = 0; i < arraysize(kEngagementBucketHistogramBuckets); ++i)
     score_buckets[kEngagementBucketHistogramBuckets[i]] = 0;
 
+  const double threshold_0 = std::numeric_limits<double>::epsilon();;
   for (const auto& value : score_map) {
     double score = value.second;
     UMA_HISTOGRAM_COUNTS_100(kEngagementScoreHistogram, score);
+    UMA_HISTOGRAM_BOOLEAN(kEngagementScoreHistogramIsZero, score < threshold_0);
     if (value.first.SchemeIs(url::kHttpsScheme)) {
       UMA_HISTOGRAM_COUNTS_100(kEngagementScoreHistogramHTTPS, score);
       https_engagement += score;
diff --git a/chrome/browser/engagement/site_engagement_metrics.h b/chrome/browser/engagement/site_engagement_metrics.h
index 5fbcccc..4d09ca1c 100644
--- a/chrome/browser/engagement/site_engagement_metrics.h
+++ b/chrome/browser/engagement/site_engagement_metrics.h
@@ -58,6 +58,7 @@
   static const char kEngagementScoreHistogram[];
   static const char kEngagementScoreHistogramHTTP[];
   static const char kEngagementScoreHistogramHTTPS[];
+  static const char kEngagementScoreHistogramIsZero[];
   static const char kOriginsWithMaxEngagementHistogram[];
   static const char kOriginsWithMaxDailyEngagementHistogram[];
   static const char kPercentOriginsWithMaxEngagementHistogram[];
diff --git a/chrome/browser/memory_details.cc b/chrome/browser/memory_details.cc
index 0da07a2..17ffc263 100644
--- a/chrome/browser/memory_details.cc
+++ b/chrome/browser/memory_details.cc
@@ -23,6 +23,7 @@
 #include "content/public/browser/child_process_data.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
@@ -297,8 +298,8 @@
           chrome_browser->site_data[contents->GetBrowserContext()];
       SiteDetails::CollectSiteInfo(contents, &site_data);
 
-      bool is_webui =
-          rvh->GetEnabledBindings() & content::BINDINGS_POLICY_WEB_UI;
+      bool is_webui = rvh->GetMainFrame()->GetEnabledBindings() &
+                      content::BINDINGS_POLICY_WEB_UI;
 
       if (is_webui) {
         process.renderer_type = ProcessMemoryInformation::RENDERER_CHROME;
diff --git a/chrome/browser/resources/md_history/app.crisper.js b/chrome/browser/resources/md_history/app.crisper.js
index f9c0038..91caeab 100644
--- a/chrome/browser/resources/md_history/app.crisper.js
+++ b/chrome/browser/resources/md_history/app.crisper.js
@@ -37,7 +37,7 @@
 // Copyright 2015 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-Polymer({is:"history-toolbar",properties:{count:{type:Number,value:0,observer:"changeToolbarView_"},itemsSelected_:{type:Boolean,value:false,reflectToAttribute:true},searchTerm:{type:String,observer:"searchTermChanged_"},spinnerActive:{type:Boolean,value:false},hasDrawer:{type:Boolean,reflectToAttribute:true},isGroupedMode:{type:Boolean,reflectToAttribute:true},groupedRange:{type:Number,reflectToAttribute:true},groupedOffset:Number,showSyncNotice:{type:Boolean,observer:"showSyncNoticeChanged_"},syncNoticeVisible_:{type:Boolean,value:false},hasMoreResults:Boolean,querying:Boolean,queryInfo:Object,showMenuPromo:Boolean},hasDismissListeners_:false,boundOnDocumentClick_:null,boundOnDocumentKeydown_:null,detached:function(){if(this.hasDismissListeners_){document.removeEventListener("click",this.boundOnDocumentClick_);document.removeEventListener("keydown",this.boundOnDocumentKeydown_)}},get searchField(){return this.$["main-toolbar"].getSearchField()},showSearchField:function(){this.searchField.showAndFocus()},changeToolbarView_:function(){this.itemsSelected_=this.count>0},searchTermChanged_:function(){if(this.searchField.getValue()!=this.searchTerm){this.searchField.showAndFocus();this.searchField.setValue(this.searchTerm)}},showSyncNoticeChanged_:function(){if(!this.showSyncNotice)this.syncNoticeVisible_=false},onSearchChanged_:function(event){this.fire("change-query",{search:event.detail})},onInfoButtonTap_:function(e){this.syncNoticeVisible_=!this.syncNoticeVisible_;e.stopPropagation();if(this.hasDismissListeners_)return;this.boundOnDocumentClick_=this.onDocumentClick_.bind(this);this.boundOnDocumentKeydown_=this.onDocumentKeydown_.bind(this);document.addEventListener("click",this.boundOnDocumentClick_);document.addEventListener("keydown",this.boundOnDocumentKeydown_);this.hasDismissListeners_=true},onDocumentClick_:function(e){if(e.path.indexOf(this.$["sync-notice"])==-1)this.syncNoticeVisible_=false},onDocumentKeydown_:function(e){if(e.key=="Escape")this.syncNoticeVisible_=false},onClearSelectionTap_:function(){this.fire("unselect-all")},onDeleteTap_:function(){this.fire("delete-selected")},deletingAllowed_:function(){return loadTimeData.getBoolean("allowDeletingHistory")},numberOfItemsSelected_:function(count){return count>0?loadTimeData.getStringF("itemsSelected",count):""},getHistoryInterval_:function(){var info=this.queryInfo;if(this.groupedRange==HistoryRange.WEEK)return info.queryInterval;if(this.groupedRange==HistoryRange.MONTH)return info.queryStartMonth},onTabSelected_:function(e){this.fire("change-query",{range:Number(e.detail.item.getAttribute("value"))})},changeOffset_:function(newOffset){if(!this.querying)this.fire("change-query",{offset:newOffset})},onTodayTap_:function(){this.changeOffset_(0)},onPrevTap_:function(){this.changeOffset_(this.groupedOffset+1)},onNextTap_:function(){this.changeOffset_(this.groupedOffset-1)},isToday_:function(){return this.groupedOffset==0}});(function(){"use strict";Polymer.IronA11yAnnouncer=Polymer({is:"iron-a11y-announcer",properties:{mode:{type:String,value:"polite"},_text:{type:String,value:""}},created:function(){if(!Polymer.IronA11yAnnouncer.instance){Polymer.IronA11yAnnouncer.instance=this}document.body.addEventListener("iron-announce",this._onIronAnnounce.bind(this))},announce:function(text){this._text="";this.async(function(){this._text=text},100)},_onIronAnnounce:function(event){if(event.detail&&event.detail.text){this.announce(event.detail.text)}}});Polymer.IronA11yAnnouncer.instance=null;Polymer.IronA11yAnnouncer.requestAvailability=function(){if(!Polymer.IronA11yAnnouncer.instance){Polymer.IronA11yAnnouncer.instance=document.createElement("iron-a11y-announcer")}document.body.appendChild(Polymer.IronA11yAnnouncer.instance)}})();(function(){var IOS=navigator.userAgent.match(/iP(?:hone|ad;(?: U;)? CPU) OS (\d+)/);var IOS_TOUCH_SCROLLING=IOS&&IOS[1]>=8;var DEFAULT_PHYSICAL_COUNT=3;var HIDDEN_Y="-10000px";var ITEM_WIDTH=0;var ITEM_HEIGHT=1;var SECRET_TABINDEX=-100;Polymer({is:"iron-list",properties:{items:{type:Array},maxPhysicalCount:{type:Number,value:500},as:{type:String,value:"item"},indexAs:{type:String,value:"index"},selectedAs:{type:String,value:"selected"},grid:{type:Boolean,value:false,reflectToAttribute:true},selectionEnabled:{type:Boolean,value:false},selectedItem:{type:Object,notify:true},selectedItems:{type:Object,notify:true},multiSelection:{type:Boolean,value:false}},observers:["_itemsChanged(items.*)","_selectionEnabledChanged(selectionEnabled)","_multiSelectionChanged(multiSelection)","_setOverflow(scrollTarget)"],behaviors:[Polymer.Templatizer,Polymer.IronResizableBehavior,Polymer.IronA11yKeysBehavior,Polymer.IronScrollTargetBehavior],keyBindings:{up:"_didMoveUp",down:"_didMoveDown",enter:"_didEnter"},_ratio:.5,_scrollerPaddingTop:0,_scrollPosition:0,_physicalSize:0,_physicalAverage:0,_physicalAverageCount:0,_physicalTop:0,_virtualCount:0,_physicalIndexForKey:null,_estScrollHeight:0,_scrollHeight:0,_viewportHeight:0,_viewportWidth:0,_physicalItems:null,_physicalSizes:null,_firstVisibleIndexVal:null,_lastVisibleIndexVal:null,_collection:null,_maxPages:2,_focusedItem:null,_focusedIndex:-1,_offscreenFocusedItem:null,_focusBackfillItem:null,_itemsPerRow:1,_itemWidth:0,_rowHeight:0,_templateCost:0,get _physicalBottom(){return this._physicalTop+this._physicalSize},get _scrollBottom(){return this._scrollPosition+this._viewportHeight},get _virtualEnd(){return this._virtualStart+this._physicalCount-1},get _hiddenContentSize(){var size=this.grid?this._physicalRows*this._rowHeight:this._physicalSize;return size-this._viewportHeight},get _maxScrollTop(){return this._estScrollHeight-this._viewportHeight+this._scrollerPaddingTop},_minVirtualStart:0,get _maxVirtualStart(){return Math.max(0,this._virtualCount-this._physicalCount)},_virtualStartVal:0,set _virtualStart(val){this._virtualStartVal=Math.min(this._maxVirtualStart,Math.max(this._minVirtualStart,val))},get _virtualStart(){return this._virtualStartVal||0},_physicalStartVal:0,set _physicalStart(val){this._physicalStartVal=val%this._physicalCount;if(this._physicalStartVal<0){this._physicalStartVal=this._physicalCount+this._physicalStartVal}this._physicalEnd=(this._physicalStart+this._physicalCount-1)%this._physicalCount},get _physicalStart(){return this._physicalStartVal||0},_physicalCountVal:0,set _physicalCount(val){this._physicalCountVal=val;this._physicalEnd=(this._physicalStart+this._physicalCount-1)%this._physicalCount},get _physicalCount(){return this._physicalCountVal},_physicalEnd:0,get _optPhysicalSize(){if(this.grid){return this._estRowsInView*this._rowHeight*this._maxPages}return this._viewportHeight*this._maxPages},get _isVisible(){return Boolean(this.offsetWidth||this.offsetHeight)},get firstVisibleIndex(){if(this._firstVisibleIndexVal===null){var physicalOffset=Math.floor(this._physicalTop+this._scrollerPaddingTop);this._firstVisibleIndexVal=this._iterateItems(function(pidx,vidx){physicalOffset+=this._getPhysicalSizeIncrement(pidx);if(physicalOffset>this._scrollPosition){return this.grid?vidx-vidx%this._itemsPerRow:vidx}if(this.grid&&this._virtualCount-1===vidx){return vidx-vidx%this._itemsPerRow}})||0}return this._firstVisibleIndexVal},get lastVisibleIndex(){if(this._lastVisibleIndexVal===null){if(this.grid){var lastIndex=this.firstVisibleIndex+this._estRowsInView*this._itemsPerRow-1;this._lastVisibleIndexVal=Math.min(this._virtualCount,lastIndex)}else{var physicalOffset=this._physicalTop;this._iterateItems(function(pidx,vidx){if(physicalOffset<this._scrollBottom){this._lastVisibleIndexVal=vidx}else{return true}physicalOffset+=this._getPhysicalSizeIncrement(pidx)})}}return this._lastVisibleIndexVal},get _defaultScrollTarget(){return this},get _virtualRowCount(){return Math.ceil(this._virtualCount/this._itemsPerRow)},get _estRowsInView(){return Math.ceil(this._viewportHeight/this._rowHeight)},get _physicalRows(){return Math.ceil(this._physicalCount/this._itemsPerRow)},ready:function(){this.addEventListener("focus",this._didFocus.bind(this),true)},attached:function(){if(this._physicalCount===0){this._debounceTemplate(this._render)}this.listen(this,"iron-resize","_resizeHandler")},detached:function(){this.unlisten(this,"iron-resize","_resizeHandler")},_setOverflow:function(scrollTarget){this.style.webkitOverflowScrolling=scrollTarget===this?"touch":"";this.style.overflow=scrollTarget===this?"auto":""},updateViewportBoundaries:function(){this._scrollerPaddingTop=this.scrollTarget===this?0:parseInt(window.getComputedStyle(this)["padding-top"],10);this._viewportWidth=this.$.items.offsetWidth;this._viewportHeight=this._scrollTargetHeight;this.grid&&this._updateGridMetrics()},_scrollHandler:function(){var scrollTop=Math.max(0,Math.min(this._maxScrollTop,this._scrollTop));var delta=scrollTop-this._scrollPosition;var isScrollingDown=delta>=0;this._scrollPosition=scrollTop;this._firstVisibleIndexVal=null;this._lastVisibleIndexVal=null;if(Math.abs(delta)>this._physicalSize){var idxAdjustment=Math.round(delta/this._physicalAverage)*this._itemsPerRow;this._physicalTop=this._physicalTop+delta;this._virtualStart=this._virtualStart+idxAdjustment;this._physicalStart=this._physicalStart+idxAdjustment;this._update()}else{var reusables=this._getReusables(isScrollingDown);if(isScrollingDown){this._physicalTop=reusables.physicalTop;this._virtualStart=this._virtualStart+reusables.indexes.length;this._physicalStart=this._physicalStart+reusables.indexes.length}else{this._virtualStart=this._virtualStart-reusables.indexes.length;this._physicalStart=this._physicalStart-reusables.indexes.length}if(reusables.indexes.length===0){this._increasePoolIfNeeded()}else{this._update(reusables.indexes,isScrollingDown?null:reusables.indexes)}}},_getReusables:function(fromTop){var ith,lastIth,offsetContent,physicalItemHeight;var idxs=[];var protectedOffsetContent=this._hiddenContentSize*this._ratio;var virtualStart=this._virtualStart;var virtualEnd=this._virtualEnd;var physicalCount=this._physicalCount;var physicalTop=this._physicalTop+this._scrollerPaddingTop;var scrollTop=this._scrollTop;var scrollBottom=this._scrollBottom;if(fromTop){ith=this._physicalStart;lastIth=this._physicalEnd;offsetContent=scrollTop-physicalTop}else{ith=this._physicalEnd;lastIth=this._physicalStart;offsetContent=this._physicalBottom-scrollBottom}while(true){physicalItemHeight=this._getPhysicalSizeIncrement(ith);offsetContent=offsetContent-physicalItemHeight;if(idxs.length>=physicalCount||offsetContent<=protectedOffsetContent){break}if(fromTop){if(virtualEnd+idxs.length+1>=this._virtualCount){break}if(physicalTop+physicalItemHeight>=scrollTop){break}idxs.push(ith);physicalTop=physicalTop+physicalItemHeight;ith=(ith+1)%physicalCount}else{if(virtualStart-idxs.length<=0){break}if(physicalTop+this._physicalSize-physicalItemHeight<=scrollBottom){break}idxs.push(ith);physicalTop=physicalTop-physicalItemHeight;ith=ith===0?physicalCount-1:ith-1}}return{indexes:idxs,physicalTop:physicalTop-this._scrollerPaddingTop}},_update:function(itemSet,movingUp){if(itemSet&&itemSet.length===0){return}this._manageFocus();this._assignModels(itemSet);this._updateMetrics(itemSet);if(movingUp){while(movingUp.length){var idx=movingUp.pop();this._physicalTop-=this._getPhysicalSizeIncrement(idx)}}this._positionItems();this._updateScrollerSize();this._increasePoolIfNeeded()},_createPool:function(size){var physicalItems=new Array(size);this._ensureTemplatized();for(var i=0;i<size;i++){var inst=this.stamp(null);physicalItems[i]=inst.root.querySelector("*");Polymer.dom(this).appendChild(inst.root)}return physicalItems},_increasePoolIfNeeded:function(){if(this._viewportHeight===0){return false}var self=this;var isClientFull=this._physicalBottom>=this._scrollBottom&&this._physicalTop<=this._scrollPosition;if(this._physicalSize>=this._optPhysicalSize&&isClientFull){return false}var maxPoolSize=Math.round(this._physicalCount*.5);if(!isClientFull){this._debounceTemplate(this._increasePool.bind(this,maxPoolSize));return true}this._yield(function(){self._increasePool(Math.min(maxPoolSize,Math.max(1,Math.round(50/self._templateCost))))});return true},_yield:function(cb){var g=window;var handle=g.requestIdleCallback?g.requestIdleCallback(cb):g.setTimeout(cb,16);Polymer.dom.addDebouncer({complete:function(){g.cancelIdleCallback?g.cancelIdleCallback(handle):g.clearTimeout(handle);cb()}})},_increasePool:function(missingItems){var nextPhysicalCount=Math.min(this._physicalCount+missingItems,this._virtualCount-this._virtualStart,Math.max(this.maxPhysicalCount,DEFAULT_PHYSICAL_COUNT));var prevPhysicalCount=this._physicalCount;var delta=nextPhysicalCount-prevPhysicalCount;var ts=window.performance.now();if(delta<=0){return}[].push.apply(this._physicalItems,this._createPool(delta));[].push.apply(this._physicalSizes,new Array(delta));this._physicalCount=prevPhysicalCount+delta;if(this._physicalStart>this._physicalEnd&&this._isIndexRendered(this._focusedIndex)&&this._getPhysicalIndex(this._focusedIndex)<this._physicalEnd){this._physicalStart=this._physicalStart+delta}this._update();this._templateCost=(window.performance.now()-ts)/delta},_render:function(){if(this.isAttached&&this._isVisible){if(this._physicalCount===0){this.updateViewportBoundaries();this._increasePool(DEFAULT_PHYSICAL_COUNT)}else{var reusables=this._getReusables(true);this._physicalTop=reusables.physicalTop;this._virtualStart=this._virtualStart+reusables.indexes.length;this._physicalStart=this._physicalStart+reusables.indexes.length;this._update(reusables.indexes);this._update()}}},_ensureTemplatized:function(){if(!this.ctor){var props={};props.__key__=true;props[this.as]=true;props[this.indexAs]=true;props[this.selectedAs]=true;props.tabIndex=true;this._instanceProps=props;this._userTemplate=Polymer.dom(this).querySelector("template");if(this._userTemplate){this.templatize(this._userTemplate)}else{console.warn("iron-list requires a template to be provided in light-dom")}}},_getStampedChildren:function(){return this._physicalItems},_forwardInstancePath:function(inst,path,value){if(path.indexOf(this.as+".")===0){this.notifyPath("items."+inst.__key__+"."+path.slice(this.as.length+1),value)}},_forwardParentProp:function(prop,value){if(this._physicalItems){this._physicalItems.forEach(function(item){item._templateInstance[prop]=value},this)}},_forwardParentPath:function(path,value){if(this._physicalItems){this._physicalItems.forEach(function(item){item._templateInstance.notifyPath(path,value,true)},this)}},_forwardItemPath:function(path,value){if(!this._physicalIndexForKey){return}var dot=path.indexOf(".");var key=path.substring(0,dot<0?path.length:dot);var idx=this._physicalIndexForKey[key];var offscreenItem=this._offscreenFocusedItem;var el=offscreenItem&&offscreenItem._templateInstance.__key__===key?offscreenItem:this._physicalItems[idx];if(!el||el._templateInstance.__key__!==key){return}if(dot>=0){path=this.as+"."+path.substring(dot+1);el._templateInstance.notifyPath(path,value,true)}else{var currentItem=el._templateInstance[this.as];if(Array.isArray(this.selectedItems)){for(var i=0;i<this.selectedItems.length;i++){if(this.selectedItems[i]===currentItem){this.set("selectedItems."+i,value);break}}}else if(this.selectedItem===currentItem){this.set("selectedItem",value)}el._templateInstance[this.as]=value}},_itemsChanged:function(change){if(change.path==="items"){this._virtualStart=0;this._physicalTop=0;this._virtualCount=this.items?this.items.length:0;this._collection=this.items?Polymer.Collection.get(this.items):null;this._physicalIndexForKey={};this._firstVisibleIndexVal=null;this._lastVisibleIndexVal=null;this._physicalCount=this._physicalCount||0;this._physicalItems=this._physicalItems||[];this._physicalSizes=this._physicalSizes||[];this._physicalStart=0;this._resetScrollPosition(0);this._removeFocusedItem();this._debounceTemplate(this._render)}else if(change.path==="items.splices"){this._adjustVirtualIndex(change.value.indexSplices);this._virtualCount=this.items?this.items.length:0;this._debounceTemplate(this._render)}else{this._forwardItemPath(change.path.split(".").slice(1).join("."),change.value)}},_adjustVirtualIndex:function(splices){splices.forEach(function(splice){splice.removed.forEach(this._removeItem,this);if(splice.index<this._virtualStart){var delta=Math.max(splice.addedCount-splice.removed.length,splice.index-this._virtualStart);this._virtualStart=this._virtualStart+delta;if(this._focusedIndex>=0){this._focusedIndex=this._focusedIndex+delta}}},this)},_removeItem:function(item){this.$.selector.deselect(item);if(this._focusedItem&&this._focusedItem._templateInstance[this.as]===item){this._removeFocusedItem()}},_iterateItems:function(fn,itemSet){var pidx,vidx,rtn,i;if(arguments.length===2&&itemSet){for(i=0;i<itemSet.length;i++){pidx=itemSet[i];vidx=this._computeVidx(pidx);if((rtn=fn.call(this,pidx,vidx))!=null){return rtn}}}else{pidx=this._physicalStart;vidx=this._virtualStart;for(;pidx<this._physicalCount;pidx++,vidx++){if((rtn=fn.call(this,pidx,vidx))!=null){return rtn}}for(pidx=0;pidx<this._physicalStart;pidx++,vidx++){if((rtn=fn.call(this,pidx,vidx))!=null){return rtn}}}},_computeVidx:function(pidx){if(pidx>=this._physicalStart){return this._virtualStart+(pidx-this._physicalStart)}return this._virtualStart+(this._physicalCount-this._physicalStart)+pidx},_assignModels:function(itemSet){this._iterateItems(function(pidx,vidx){var el=this._physicalItems[pidx];var inst=el._templateInstance;var item=this.items&&this.items[vidx];if(item!=null){inst[this.as]=item;inst.__key__=this._collection.getKey(item);inst[this.selectedAs]=this.$.selector.isSelected(item);inst[this.indexAs]=vidx;inst.tabIndex=this._focusedIndex===vidx?0:-1;this._physicalIndexForKey[inst.__key__]=pidx;el.removeAttribute("hidden")}else{inst.__key__=null;el.setAttribute("hidden","")}},itemSet)},_updateMetrics:function(itemSet){Polymer.dom.flush();var newPhysicalSize=0;var oldPhysicalSize=0;var prevAvgCount=this._physicalAverageCount;var prevPhysicalAvg=this._physicalAverage;this._iterateItems(function(pidx,vidx){oldPhysicalSize+=this._physicalSizes[pidx]||0;this._physicalSizes[pidx]=this._physicalItems[pidx].offsetHeight;newPhysicalSize+=this._physicalSizes[pidx];this._physicalAverageCount+=this._physicalSizes[pidx]?1:0},itemSet);if(this.grid){this._updateGridMetrics();this._physicalSize=Math.ceil(this._physicalCount/this._itemsPerRow)*this._rowHeight}else{this._physicalSize=this._physicalSize+newPhysicalSize-oldPhysicalSize}if(this._physicalAverageCount!==prevAvgCount){this._physicalAverage=Math.round((prevPhysicalAvg*prevAvgCount+newPhysicalSize)/this._physicalAverageCount)}},_updateGridMetrics:function(){this._itemWidth=this._physicalCount>0?this._physicalItems[0].getBoundingClientRect().width:200;this._rowHeight=this._physicalCount>0?this._physicalItems[0].offsetHeight:200;this._itemsPerRow=this._itemWidth?Math.floor(this._viewportWidth/this._itemWidth):this._itemsPerRow},_positionItems:function(){this._adjustScrollPosition();var y=this._physicalTop;if(this.grid){var totalItemWidth=this._itemsPerRow*this._itemWidth;var rowOffset=(this._viewportWidth-totalItemWidth)/2;this._iterateItems(function(pidx,vidx){var modulus=vidx%this._itemsPerRow;var x=Math.floor(modulus*this._itemWidth+rowOffset);this.translate3d(x+"px",y+"px",0,this._physicalItems[pidx]);if(this._shouldRenderNextRow(vidx)){y+=this._rowHeight}})}else{this._iterateItems(function(pidx,vidx){this.translate3d(0,y+"px",0,this._physicalItems[pidx]);y+=this._physicalSizes[pidx]})}},_getPhysicalSizeIncrement:function(pidx){if(!this.grid){return this._physicalSizes[pidx]}if(this._computeVidx(pidx)%this._itemsPerRow!==this._itemsPerRow-1){return 0}return this._rowHeight},_shouldRenderNextRow:function(vidx){return vidx%this._itemsPerRow===this._itemsPerRow-1},_adjustScrollPosition:function(){var deltaHeight=this._virtualStart===0?this._physicalTop:Math.min(this._scrollPosition+this._physicalTop,0);if(deltaHeight){this._physicalTop=this._physicalTop-deltaHeight;if(!IOS_TOUCH_SCROLLING&&this._physicalTop!==0){this._resetScrollPosition(this._scrollTop-deltaHeight)}}},_resetScrollPosition:function(pos){if(this.scrollTarget){this._scrollTop=pos;this._scrollPosition=this._scrollTop}},_updateScrollerSize:function(forceUpdate){if(this.grid){this._estScrollHeight=this._virtualRowCount*this._rowHeight}else{this._estScrollHeight=this._physicalBottom+Math.max(this._virtualCount-this._physicalCount-this._virtualStart,0)*this._physicalAverage}forceUpdate=forceUpdate||this._scrollHeight===0;forceUpdate=forceUpdate||this._scrollPosition>=this._estScrollHeight-this._physicalSize;forceUpdate=forceUpdate||this.grid&&this.$.items.style.height<this._estScrollHeight;if(forceUpdate||Math.abs(this._estScrollHeight-this._scrollHeight)>=this._optPhysicalSize){this.$.items.style.height=this._estScrollHeight+"px";this._scrollHeight=this._estScrollHeight}},scrollToItem:function(item){return this.scrollToIndex(this.items.indexOf(item))},scrollToIndex:function(idx){if(typeof idx!=="number"||idx<0||idx>this.items.length-1){return}Polymer.dom.flush();if(this._physicalCount===0){return}idx=Math.min(Math.max(idx,0),this._virtualCount-1);if(!this._isIndexRendered(idx)||idx>=this._maxVirtualStart){this._virtualStart=this.grid?idx-this._itemsPerRow*2:idx-1}this._manageFocus();this._assignModels();this._updateMetrics();this._physicalTop=Math.floor(this._virtualStart/this._itemsPerRow)*this._physicalAverage;var currentTopItem=this._physicalStart;var currentVirtualItem=this._virtualStart;var targetOffsetTop=0;var hiddenContentSize=this._hiddenContentSize;while(currentVirtualItem<idx&&targetOffsetTop<=hiddenContentSize){targetOffsetTop=targetOffsetTop+this._getPhysicalSizeIncrement(currentTopItem);currentTopItem=(currentTopItem+1)%this._physicalCount;currentVirtualItem++}this._updateScrollerSize(true);this._positionItems();this._resetScrollPosition(this._physicalTop+this._scrollerPaddingTop+targetOffsetTop);this._increasePoolIfNeeded();this._firstVisibleIndexVal=null;this._lastVisibleIndexVal=null},_resetAverage:function(){this._physicalAverage=0;this._physicalAverageCount=0},_resizeHandler:function(){var delta=Math.abs(this._viewportHeight-this._scrollTargetHeight);if(IOS&&delta>0&&delta<100){return}Polymer.dom.addDebouncer(this.debounce("_debounceTemplate",function(){this.updateViewportBoundaries();this._render();if(this._isVisible){this.toggleScrollListener(true);if(this._physicalCount>0){this._resetAverage();this.scrollToIndex(this.firstVisibleIndex)}}else{this.toggleScrollListener(false)}}.bind(this),1))},_getModelFromItem:function(item){var key=this._collection.getKey(item);var pidx=this._physicalIndexForKey[key];if(pidx!=null){return this._physicalItems[pidx]._templateInstance}return null},_getNormalizedItem:function(item){if(this._collection.getKey(item)===undefined){if(typeof item==="number"){item=this.items[item];if(!item){throw new RangeError("<item> not found")}return item}throw new TypeError("<item> should be a valid item")}return item},selectItem:function(item){item=this._getNormalizedItem(item);var model=this._getModelFromItem(item);if(!this.multiSelection&&this.selectedItem){this.deselectItem(this.selectedItem)}if(model){model[this.selectedAs]=true}this.$.selector.select(item);this.updateSizeForItem(item)},deselectItem:function(item){item=this._getNormalizedItem(item);var model=this._getModelFromItem(item);if(model){model[this.selectedAs]=false}this.$.selector.deselect(item);this.updateSizeForItem(item)},toggleSelectionForItem:function(item){item=this._getNormalizedItem(item);if(this.$.selector.isSelected(item)){this.deselectItem(item)}else{this.selectItem(item)}},clearSelection:function(){function unselect(item){var model=this._getModelFromItem(item);if(model){model[this.selectedAs]=false}}if(Array.isArray(this.selectedItems)){this.selectedItems.forEach(unselect,this)}else if(this.selectedItem){unselect.call(this,this.selectedItem)}this.$.selector.clearSelection()},_selectionEnabledChanged:function(selectionEnabled){var handler=selectionEnabled?this.listen:this.unlisten;handler.call(this,this,"tap","_selectionHandler")},_selectionHandler:function(e){var model=this.modelForElement(e.target);if(!model){return}var modelTabIndex,activeElTabIndex;var target=Polymer.dom(e).path[0];var activeEl=Polymer.dom(this.domHost?this.domHost.root:document).activeElement;var physicalItem=this._physicalItems[this._getPhysicalIndex(model[this.indexAs])];if(target.localName==="input"||target.localName==="button"||target.localName==="select"){return}modelTabIndex=model.tabIndex;model.tabIndex=SECRET_TABINDEX;activeElTabIndex=activeEl?activeEl.tabIndex:-1;model.tabIndex=modelTabIndex;if(activeEl&&physicalItem!==activeEl&&physicalItem.contains(activeEl)&&activeElTabIndex!==SECRET_TABINDEX){return}this.toggleSelectionForItem(model[this.as])},_multiSelectionChanged:function(multiSelection){this.clearSelection();this.$.selector.multi=multiSelection},updateSizeForItem:function(item){item=this._getNormalizedItem(item);var key=this._collection.getKey(item);var pidx=this._physicalIndexForKey[key];if(pidx!=null){this._updateMetrics([pidx]);this._positionItems()}},_manageFocus:function(){var fidx=this._focusedIndex;if(fidx>=0&&fidx<this._virtualCount){if(this._isIndexRendered(fidx)){this._restoreFocusedItem()}else{this._createFocusBackfillItem()}}else if(this._virtualCount>0&&this._physicalCount>0){this._focusedIndex=this._virtualStart;this._focusedItem=this._physicalItems[this._physicalStart]}},_isIndexRendered:function(idx){return idx>=this._virtualStart&&idx<=this._virtualEnd},_isIndexVisible:function(idx){return idx>=this.firstVisibleIndex&&idx<=this.lastVisibleIndex},_getPhysicalIndex:function(idx){return this._physicalIndexForKey[this._collection.getKey(this._getNormalizedItem(idx))]},_focusPhysicalItem:function(idx){if(idx<0||idx>=this._virtualCount){return}this._restoreFocusedItem();if(!this._isIndexRendered(idx)){this.scrollToIndex(idx)}var physicalItem=this._physicalItems[this._getPhysicalIndex(idx)];var model=physicalItem._templateInstance;var focusable;model.tabIndex=SECRET_TABINDEX;if(physicalItem.tabIndex===SECRET_TABINDEX){focusable=physicalItem}if(!focusable){focusable=Polymer.dom(physicalItem).querySelector('[tabindex="'+SECRET_TABINDEX+'"]')}model.tabIndex=0;this._focusedIndex=idx;focusable&&focusable.focus()},_removeFocusedItem:function(){if(this._offscreenFocusedItem){Polymer.dom(this).removeChild(this._offscreenFocusedItem)}this._offscreenFocusedItem=null;this._focusBackfillItem=null;this._focusedItem=null;this._focusedIndex=-1},_createFocusBackfillItem:function(){var fidx=this._focusedIndex;var pidx=this._getPhysicalIndex(fidx);if(this._offscreenFocusedItem||pidx==null||fidx<0){return}if(!this._focusBackfillItem){var stampedTemplate=this.stamp(null);this._focusBackfillItem=stampedTemplate.root.querySelector("*");Polymer.dom(this).appendChild(stampedTemplate.root)}this._offscreenFocusedItem=this._physicalItems[pidx];this._offscreenFocusedItem._templateInstance.tabIndex=0;this._physicalItems[pidx]=this._focusBackfillItem;this.translate3d(0,HIDDEN_Y,0,this._offscreenFocusedItem)},_restoreFocusedItem:function(){var pidx,fidx=this._focusedIndex;if(!this._offscreenFocusedItem||this._focusedIndex<0){return}this._assignModels();pidx=this._getPhysicalIndex(fidx);if(pidx!=null){this._focusBackfillItem=this._physicalItems[pidx];this._focusBackfillItem._templateInstance.tabIndex=-1;this._physicalItems[pidx]=this._offscreenFocusedItem;this._offscreenFocusedItem=null;this.translate3d(0,HIDDEN_Y,0,this._focusBackfillItem)}},_didFocus:function(e){var targetModel=this.modelForElement(e.target);var focusedModel=this._focusedItem?this._focusedItem._templateInstance:null;var hasOffscreenFocusedItem=this._offscreenFocusedItem!==null;var fidx=this._focusedIndex;if(!targetModel||!focusedModel){return}if(focusedModel===targetModel){if(!this._isIndexVisible(fidx)){this.scrollToIndex(fidx)}}else{this._restoreFocusedItem();focusedModel.tabIndex=-1;targetModel.tabIndex=0;fidx=targetModel[this.indexAs];this._focusedIndex=fidx;this._focusedItem=this._physicalItems[this._getPhysicalIndex(fidx)];if(hasOffscreenFocusedItem&&!this._offscreenFocusedItem){this._update()}}},_didMoveUp:function(){this._focusPhysicalItem(this._focusedIndex-1)},_didMoveDown:function(e){e.detail.keyboardEvent.preventDefault();this._focusPhysicalItem(this._focusedIndex+1)},_didEnter:function(e){this._focusPhysicalItem(this._focusedIndex);this._selectionHandler(e.detail.keyboardEvent)}})})();Polymer({is:"iron-scroll-threshold",properties:{upperThreshold:{type:Number,value:100},lowerThreshold:{type:Number,value:100},upperTriggered:{type:Boolean,value:false,notify:true,readOnly:true},lowerTriggered:{type:Boolean,value:false,notify:true,readOnly:true},horizontal:{type:Boolean,value:false}},behaviors:[Polymer.IronScrollTargetBehavior],observers:["_setOverflow(scrollTarget)","_initCheck(horizontal, isAttached)"],get _defaultScrollTarget(){return this},_setOverflow:function(scrollTarget){this.style.overflow=scrollTarget===this?"auto":""},_scrollHandler:function(){var THROTTLE_THRESHOLD=200;if(!this.isDebouncerActive("_checkTheshold")){this.debounce("_checkTheshold",function(){this.checkScrollThesholds()},THROTTLE_THRESHOLD)}},_initCheck:function(horizontal,isAttached){if(isAttached){this.debounce("_init",function(){this.clearTriggers();this.checkScrollThesholds()})}},checkScrollThesholds:function(){if(!this.scrollTarget||this.lowerTriggered&&this.upperTriggered){return}var upperScrollValue=this.horizontal?this._scrollLeft:this._scrollTop;var lowerScrollValue=this.horizontal?this.scrollTarget.scrollWidth-this._scrollTargetWidth-this._scrollLeft:this.scrollTarget.scrollHeight-this._scrollTargetHeight-this._scrollTop;if(upperScrollValue<=this.upperThreshold&&!this.upperTriggered){this._setUpperTriggered(true);this.fire("upper-threshold")}if(lowerScrollValue<=this.lowerThreshold&&!this.lowerTriggered){this._setLowerTriggered(true);this.fire("lower-threshold")}},clearTriggers:function(){this._setUpperTriggered(false);this._setLowerTriggered(false)}});
+Polymer({is:"history-toolbar",properties:{count:{type:Number,value:0,observer:"changeToolbarView_"},itemsSelected_:{type:Boolean,value:false,reflectToAttribute:true},searchTerm:{type:String,observer:"searchTermChanged_"},spinnerActive:{type:Boolean,value:false},hasDrawer:{type:Boolean,reflectToAttribute:true},isGroupedMode:{type:Boolean,reflectToAttribute:true},groupedRange:{type:Number,reflectToAttribute:true},groupedOffset:Number,showSyncNotice:{type:Boolean,observer:"showSyncNoticeChanged_"},syncNoticeVisible_:{type:Boolean,value:false},hasMoreResults:Boolean,querying:Boolean,queryInfo:Object,showMenuPromo:Boolean},hasDismissListeners_:false,boundOnDocumentClick_:null,boundOnDocumentKeydown_:null,detached:function(){if(this.hasDismissListeners_){document.removeEventListener("click",this.boundOnDocumentClick_);document.removeEventListener("keydown",this.boundOnDocumentKeydown_)}},get searchField(){return this.$["main-toolbar"].getSearchField()},showSearchField:function(){this.searchField.showAndFocus()},changeToolbarView_:function(){this.itemsSelected_=this.count>0},searchTermChanged_:function(){if(this.searchField.getValue()!=this.searchTerm){this.searchField.showAndFocus();this.searchField.setValue(this.searchTerm)}},showSyncNoticeChanged_:function(){if(!this.showSyncNotice)this.syncNoticeVisible_=false},onSearchChanged_:function(event){this.fire("change-query",{search:event.detail})},onInfoButtonTap_:function(e){this.syncNoticeVisible_=!this.syncNoticeVisible_;e.stopPropagation();if(this.hasDismissListeners_)return;this.boundOnDocumentClick_=this.onDocumentClick_.bind(this);this.boundOnDocumentKeydown_=this.onDocumentKeydown_.bind(this);document.addEventListener("click",this.boundOnDocumentClick_);document.addEventListener("keydown",this.boundOnDocumentKeydown_);this.hasDismissListeners_=true},onDocumentClick_:function(e){if(e.path.indexOf(this.$["sync-notice"])==-1)this.syncNoticeVisible_=false},onDocumentKeydown_:function(e){if(e.key=="Escape")this.syncNoticeVisible_=false},onClearSelectionTap_:function(){this.fire("unselect-all")},onDeleteTap_:function(){this.fire("delete-selected")},deletingAllowed_:function(){return loadTimeData.getBoolean("allowDeletingHistory")},numberOfItemsSelected_:function(count){return count>0?loadTimeData.getStringF("itemsSelected",count):""},getHistoryInterval_:function(){var info=this.queryInfo;if(!info)return;if(this.groupedRange==HistoryRange.WEEK)return info.queryInterval;if(this.groupedRange==HistoryRange.MONTH)return info.queryStartMonth},onTabSelected_:function(e){this.fire("change-query",{range:Number(e.detail.item.getAttribute("value"))})},changeOffset_:function(newOffset){if(!this.querying)this.fire("change-query",{offset:newOffset})},onTodayTap_:function(){this.changeOffset_(0)},onPrevTap_:function(){this.changeOffset_(this.groupedOffset+1)},onNextTap_:function(){this.changeOffset_(this.groupedOffset-1)},isToday_:function(){return this.groupedOffset==0}});(function(){"use strict";Polymer.IronA11yAnnouncer=Polymer({is:"iron-a11y-announcer",properties:{mode:{type:String,value:"polite"},_text:{type:String,value:""}},created:function(){if(!Polymer.IronA11yAnnouncer.instance){Polymer.IronA11yAnnouncer.instance=this}document.body.addEventListener("iron-announce",this._onIronAnnounce.bind(this))},announce:function(text){this._text="";this.async(function(){this._text=text},100)},_onIronAnnounce:function(event){if(event.detail&&event.detail.text){this.announce(event.detail.text)}}});Polymer.IronA11yAnnouncer.instance=null;Polymer.IronA11yAnnouncer.requestAvailability=function(){if(!Polymer.IronA11yAnnouncer.instance){Polymer.IronA11yAnnouncer.instance=document.createElement("iron-a11y-announcer")}document.body.appendChild(Polymer.IronA11yAnnouncer.instance)}})();(function(){var IOS=navigator.userAgent.match(/iP(?:hone|ad;(?: U;)? CPU) OS (\d+)/);var IOS_TOUCH_SCROLLING=IOS&&IOS[1]>=8;var DEFAULT_PHYSICAL_COUNT=3;var HIDDEN_Y="-10000px";var ITEM_WIDTH=0;var ITEM_HEIGHT=1;var SECRET_TABINDEX=-100;Polymer({is:"iron-list",properties:{items:{type:Array},maxPhysicalCount:{type:Number,value:500},as:{type:String,value:"item"},indexAs:{type:String,value:"index"},selectedAs:{type:String,value:"selected"},grid:{type:Boolean,value:false,reflectToAttribute:true},selectionEnabled:{type:Boolean,value:false},selectedItem:{type:Object,notify:true},selectedItems:{type:Object,notify:true},multiSelection:{type:Boolean,value:false}},observers:["_itemsChanged(items.*)","_selectionEnabledChanged(selectionEnabled)","_multiSelectionChanged(multiSelection)","_setOverflow(scrollTarget)"],behaviors:[Polymer.Templatizer,Polymer.IronResizableBehavior,Polymer.IronA11yKeysBehavior,Polymer.IronScrollTargetBehavior],keyBindings:{up:"_didMoveUp",down:"_didMoveDown",enter:"_didEnter"},_ratio:.5,_scrollerPaddingTop:0,_scrollPosition:0,_physicalSize:0,_physicalAverage:0,_physicalAverageCount:0,_physicalTop:0,_virtualCount:0,_physicalIndexForKey:null,_estScrollHeight:0,_scrollHeight:0,_viewportHeight:0,_viewportWidth:0,_physicalItems:null,_physicalSizes:null,_firstVisibleIndexVal:null,_lastVisibleIndexVal:null,_collection:null,_maxPages:2,_focusedItem:null,_focusedIndex:-1,_offscreenFocusedItem:null,_focusBackfillItem:null,_itemsPerRow:1,_itemWidth:0,_rowHeight:0,_templateCost:0,get _physicalBottom(){return this._physicalTop+this._physicalSize},get _scrollBottom(){return this._scrollPosition+this._viewportHeight},get _virtualEnd(){return this._virtualStart+this._physicalCount-1},get _hiddenContentSize(){var size=this.grid?this._physicalRows*this._rowHeight:this._physicalSize;return size-this._viewportHeight},get _maxScrollTop(){return this._estScrollHeight-this._viewportHeight+this._scrollerPaddingTop},_minVirtualStart:0,get _maxVirtualStart(){return Math.max(0,this._virtualCount-this._physicalCount)},_virtualStartVal:0,set _virtualStart(val){this._virtualStartVal=Math.min(this._maxVirtualStart,Math.max(this._minVirtualStart,val))},get _virtualStart(){return this._virtualStartVal||0},_physicalStartVal:0,set _physicalStart(val){this._physicalStartVal=val%this._physicalCount;if(this._physicalStartVal<0){this._physicalStartVal=this._physicalCount+this._physicalStartVal}this._physicalEnd=(this._physicalStart+this._physicalCount-1)%this._physicalCount},get _physicalStart(){return this._physicalStartVal||0},_physicalCountVal:0,set _physicalCount(val){this._physicalCountVal=val;this._physicalEnd=(this._physicalStart+this._physicalCount-1)%this._physicalCount},get _physicalCount(){return this._physicalCountVal},_physicalEnd:0,get _optPhysicalSize(){if(this.grid){return this._estRowsInView*this._rowHeight*this._maxPages}return this._viewportHeight*this._maxPages},get _isVisible(){return Boolean(this.offsetWidth||this.offsetHeight)},get firstVisibleIndex(){if(this._firstVisibleIndexVal===null){var physicalOffset=Math.floor(this._physicalTop+this._scrollerPaddingTop);this._firstVisibleIndexVal=this._iterateItems(function(pidx,vidx){physicalOffset+=this._getPhysicalSizeIncrement(pidx);if(physicalOffset>this._scrollPosition){return this.grid?vidx-vidx%this._itemsPerRow:vidx}if(this.grid&&this._virtualCount-1===vidx){return vidx-vidx%this._itemsPerRow}})||0}return this._firstVisibleIndexVal},get lastVisibleIndex(){if(this._lastVisibleIndexVal===null){if(this.grid){var lastIndex=this.firstVisibleIndex+this._estRowsInView*this._itemsPerRow-1;this._lastVisibleIndexVal=Math.min(this._virtualCount,lastIndex)}else{var physicalOffset=this._physicalTop;this._iterateItems(function(pidx,vidx){if(physicalOffset<this._scrollBottom){this._lastVisibleIndexVal=vidx}else{return true}physicalOffset+=this._getPhysicalSizeIncrement(pidx)})}}return this._lastVisibleIndexVal},get _defaultScrollTarget(){return this},get _virtualRowCount(){return Math.ceil(this._virtualCount/this._itemsPerRow)},get _estRowsInView(){return Math.ceil(this._viewportHeight/this._rowHeight)},get _physicalRows(){return Math.ceil(this._physicalCount/this._itemsPerRow)},ready:function(){this.addEventListener("focus",this._didFocus.bind(this),true)},attached:function(){if(this._physicalCount===0){this._debounceTemplate(this._render)}this.listen(this,"iron-resize","_resizeHandler")},detached:function(){this.unlisten(this,"iron-resize","_resizeHandler")},_setOverflow:function(scrollTarget){this.style.webkitOverflowScrolling=scrollTarget===this?"touch":"";this.style.overflow=scrollTarget===this?"auto":""},updateViewportBoundaries:function(){this._scrollerPaddingTop=this.scrollTarget===this?0:parseInt(window.getComputedStyle(this)["padding-top"],10);this._viewportWidth=this.$.items.offsetWidth;this._viewportHeight=this._scrollTargetHeight;this.grid&&this._updateGridMetrics()},_scrollHandler:function(){var scrollTop=Math.max(0,Math.min(this._maxScrollTop,this._scrollTop));var delta=scrollTop-this._scrollPosition;var isScrollingDown=delta>=0;this._scrollPosition=scrollTop;this._firstVisibleIndexVal=null;this._lastVisibleIndexVal=null;if(Math.abs(delta)>this._physicalSize){var idxAdjustment=Math.round(delta/this._physicalAverage)*this._itemsPerRow;this._physicalTop=this._physicalTop+delta;this._virtualStart=this._virtualStart+idxAdjustment;this._physicalStart=this._physicalStart+idxAdjustment;this._update()}else{var reusables=this._getReusables(isScrollingDown);if(isScrollingDown){this._physicalTop=reusables.physicalTop;this._virtualStart=this._virtualStart+reusables.indexes.length;this._physicalStart=this._physicalStart+reusables.indexes.length}else{this._virtualStart=this._virtualStart-reusables.indexes.length;this._physicalStart=this._physicalStart-reusables.indexes.length}if(reusables.indexes.length===0){this._increasePoolIfNeeded()}else{this._update(reusables.indexes,isScrollingDown?null:reusables.indexes)}}},_getReusables:function(fromTop){var ith,lastIth,offsetContent,physicalItemHeight;var idxs=[];var protectedOffsetContent=this._hiddenContentSize*this._ratio;var virtualStart=this._virtualStart;var virtualEnd=this._virtualEnd;var physicalCount=this._physicalCount;var physicalTop=this._physicalTop+this._scrollerPaddingTop;var scrollTop=this._scrollTop;var scrollBottom=this._scrollBottom;if(fromTop){ith=this._physicalStart;lastIth=this._physicalEnd;offsetContent=scrollTop-physicalTop}else{ith=this._physicalEnd;lastIth=this._physicalStart;offsetContent=this._physicalBottom-scrollBottom}while(true){physicalItemHeight=this._getPhysicalSizeIncrement(ith);offsetContent=offsetContent-physicalItemHeight;if(idxs.length>=physicalCount||offsetContent<=protectedOffsetContent){break}if(fromTop){if(virtualEnd+idxs.length+1>=this._virtualCount){break}if(physicalTop+physicalItemHeight>=scrollTop){break}idxs.push(ith);physicalTop=physicalTop+physicalItemHeight;ith=(ith+1)%physicalCount}else{if(virtualStart-idxs.length<=0){break}if(physicalTop+this._physicalSize-physicalItemHeight<=scrollBottom){break}idxs.push(ith);physicalTop=physicalTop-physicalItemHeight;ith=ith===0?physicalCount-1:ith-1}}return{indexes:idxs,physicalTop:physicalTop-this._scrollerPaddingTop}},_update:function(itemSet,movingUp){if(itemSet&&itemSet.length===0){return}this._manageFocus();this._assignModels(itemSet);this._updateMetrics(itemSet);if(movingUp){while(movingUp.length){var idx=movingUp.pop();this._physicalTop-=this._getPhysicalSizeIncrement(idx)}}this._positionItems();this._updateScrollerSize();this._increasePoolIfNeeded()},_createPool:function(size){var physicalItems=new Array(size);this._ensureTemplatized();for(var i=0;i<size;i++){var inst=this.stamp(null);physicalItems[i]=inst.root.querySelector("*");Polymer.dom(this).appendChild(inst.root)}return physicalItems},_increasePoolIfNeeded:function(){if(this._viewportHeight===0){return false}var self=this;var isClientFull=this._physicalBottom>=this._scrollBottom&&this._physicalTop<=this._scrollPosition;if(this._physicalSize>=this._optPhysicalSize&&isClientFull){return false}var maxPoolSize=Math.round(this._physicalCount*.5);if(!isClientFull){this._debounceTemplate(this._increasePool.bind(this,maxPoolSize));return true}this._yield(function(){self._increasePool(Math.min(maxPoolSize,Math.max(1,Math.round(50/self._templateCost))))});return true},_yield:function(cb){var g=window;var handle=g.requestIdleCallback?g.requestIdleCallback(cb):g.setTimeout(cb,16);Polymer.dom.addDebouncer({complete:function(){g.cancelIdleCallback?g.cancelIdleCallback(handle):g.clearTimeout(handle);cb()}})},_increasePool:function(missingItems){var nextPhysicalCount=Math.min(this._physicalCount+missingItems,this._virtualCount-this._virtualStart,Math.max(this.maxPhysicalCount,DEFAULT_PHYSICAL_COUNT));var prevPhysicalCount=this._physicalCount;var delta=nextPhysicalCount-prevPhysicalCount;var ts=window.performance.now();if(delta<=0){return}[].push.apply(this._physicalItems,this._createPool(delta));[].push.apply(this._physicalSizes,new Array(delta));this._physicalCount=prevPhysicalCount+delta;if(this._physicalStart>this._physicalEnd&&this._isIndexRendered(this._focusedIndex)&&this._getPhysicalIndex(this._focusedIndex)<this._physicalEnd){this._physicalStart=this._physicalStart+delta}this._update();this._templateCost=(window.performance.now()-ts)/delta},_render:function(){if(this.isAttached&&this._isVisible){if(this._physicalCount===0){this.updateViewportBoundaries();this._increasePool(DEFAULT_PHYSICAL_COUNT)}else{var reusables=this._getReusables(true);this._physicalTop=reusables.physicalTop;this._virtualStart=this._virtualStart+reusables.indexes.length;this._physicalStart=this._physicalStart+reusables.indexes.length;this._update(reusables.indexes);this._update()}}},_ensureTemplatized:function(){if(!this.ctor){var props={};props.__key__=true;props[this.as]=true;props[this.indexAs]=true;props[this.selectedAs]=true;props.tabIndex=true;this._instanceProps=props;this._userTemplate=Polymer.dom(this).querySelector("template");if(this._userTemplate){this.templatize(this._userTemplate)}else{console.warn("iron-list requires a template to be provided in light-dom")}}},_getStampedChildren:function(){return this._physicalItems},_forwardInstancePath:function(inst,path,value){if(path.indexOf(this.as+".")===0){this.notifyPath("items."+inst.__key__+"."+path.slice(this.as.length+1),value)}},_forwardParentProp:function(prop,value){if(this._physicalItems){this._physicalItems.forEach(function(item){item._templateInstance[prop]=value},this)}},_forwardParentPath:function(path,value){if(this._physicalItems){this._physicalItems.forEach(function(item){item._templateInstance.notifyPath(path,value,true)},this)}},_forwardItemPath:function(path,value){if(!this._physicalIndexForKey){return}var dot=path.indexOf(".");var key=path.substring(0,dot<0?path.length:dot);var idx=this._physicalIndexForKey[key];var offscreenItem=this._offscreenFocusedItem;var el=offscreenItem&&offscreenItem._templateInstance.__key__===key?offscreenItem:this._physicalItems[idx];if(!el||el._templateInstance.__key__!==key){return}if(dot>=0){path=this.as+"."+path.substring(dot+1);el._templateInstance.notifyPath(path,value,true)}else{var currentItem=el._templateInstance[this.as];if(Array.isArray(this.selectedItems)){for(var i=0;i<this.selectedItems.length;i++){if(this.selectedItems[i]===currentItem){this.set("selectedItems."+i,value);break}}}else if(this.selectedItem===currentItem){this.set("selectedItem",value)}el._templateInstance[this.as]=value}},_itemsChanged:function(change){if(change.path==="items"){this._virtualStart=0;this._physicalTop=0;this._virtualCount=this.items?this.items.length:0;this._collection=this.items?Polymer.Collection.get(this.items):null;this._physicalIndexForKey={};this._firstVisibleIndexVal=null;this._lastVisibleIndexVal=null;this._physicalCount=this._physicalCount||0;this._physicalItems=this._physicalItems||[];this._physicalSizes=this._physicalSizes||[];this._physicalStart=0;this._resetScrollPosition(0);this._removeFocusedItem();this._debounceTemplate(this._render)}else if(change.path==="items.splices"){this._adjustVirtualIndex(change.value.indexSplices);this._virtualCount=this.items?this.items.length:0;this._debounceTemplate(this._render)}else{this._forwardItemPath(change.path.split(".").slice(1).join("."),change.value)}},_adjustVirtualIndex:function(splices){splices.forEach(function(splice){splice.removed.forEach(this._removeItem,this);if(splice.index<this._virtualStart){var delta=Math.max(splice.addedCount-splice.removed.length,splice.index-this._virtualStart);this._virtualStart=this._virtualStart+delta;if(this._focusedIndex>=0){this._focusedIndex=this._focusedIndex+delta}}},this)},_removeItem:function(item){this.$.selector.deselect(item);if(this._focusedItem&&this._focusedItem._templateInstance[this.as]===item){this._removeFocusedItem()}},_iterateItems:function(fn,itemSet){var pidx,vidx,rtn,i;if(arguments.length===2&&itemSet){for(i=0;i<itemSet.length;i++){pidx=itemSet[i];vidx=this._computeVidx(pidx);if((rtn=fn.call(this,pidx,vidx))!=null){return rtn}}}else{pidx=this._physicalStart;vidx=this._virtualStart;for(;pidx<this._physicalCount;pidx++,vidx++){if((rtn=fn.call(this,pidx,vidx))!=null){return rtn}}for(pidx=0;pidx<this._physicalStart;pidx++,vidx++){if((rtn=fn.call(this,pidx,vidx))!=null){return rtn}}}},_computeVidx:function(pidx){if(pidx>=this._physicalStart){return this._virtualStart+(pidx-this._physicalStart)}return this._virtualStart+(this._physicalCount-this._physicalStart)+pidx},_assignModels:function(itemSet){this._iterateItems(function(pidx,vidx){var el=this._physicalItems[pidx];var inst=el._templateInstance;var item=this.items&&this.items[vidx];if(item!=null){inst[this.as]=item;inst.__key__=this._collection.getKey(item);inst[this.selectedAs]=this.$.selector.isSelected(item);inst[this.indexAs]=vidx;inst.tabIndex=this._focusedIndex===vidx?0:-1;this._physicalIndexForKey[inst.__key__]=pidx;el.removeAttribute("hidden")}else{inst.__key__=null;el.setAttribute("hidden","")}},itemSet)},_updateMetrics:function(itemSet){Polymer.dom.flush();var newPhysicalSize=0;var oldPhysicalSize=0;var prevAvgCount=this._physicalAverageCount;var prevPhysicalAvg=this._physicalAverage;this._iterateItems(function(pidx,vidx){oldPhysicalSize+=this._physicalSizes[pidx]||0;this._physicalSizes[pidx]=this._physicalItems[pidx].offsetHeight;newPhysicalSize+=this._physicalSizes[pidx];this._physicalAverageCount+=this._physicalSizes[pidx]?1:0},itemSet);if(this.grid){this._updateGridMetrics();this._physicalSize=Math.ceil(this._physicalCount/this._itemsPerRow)*this._rowHeight}else{this._physicalSize=this._physicalSize+newPhysicalSize-oldPhysicalSize}if(this._physicalAverageCount!==prevAvgCount){this._physicalAverage=Math.round((prevPhysicalAvg*prevAvgCount+newPhysicalSize)/this._physicalAverageCount)}},_updateGridMetrics:function(){this._itemWidth=this._physicalCount>0?this._physicalItems[0].getBoundingClientRect().width:200;this._rowHeight=this._physicalCount>0?this._physicalItems[0].offsetHeight:200;this._itemsPerRow=this._itemWidth?Math.floor(this._viewportWidth/this._itemWidth):this._itemsPerRow},_positionItems:function(){this._adjustScrollPosition();var y=this._physicalTop;if(this.grid){var totalItemWidth=this._itemsPerRow*this._itemWidth;var rowOffset=(this._viewportWidth-totalItemWidth)/2;this._iterateItems(function(pidx,vidx){var modulus=vidx%this._itemsPerRow;var x=Math.floor(modulus*this._itemWidth+rowOffset);this.translate3d(x+"px",y+"px",0,this._physicalItems[pidx]);if(this._shouldRenderNextRow(vidx)){y+=this._rowHeight}})}else{this._iterateItems(function(pidx,vidx){this.translate3d(0,y+"px",0,this._physicalItems[pidx]);y+=this._physicalSizes[pidx]})}},_getPhysicalSizeIncrement:function(pidx){if(!this.grid){return this._physicalSizes[pidx]}if(this._computeVidx(pidx)%this._itemsPerRow!==this._itemsPerRow-1){return 0}return this._rowHeight},_shouldRenderNextRow:function(vidx){return vidx%this._itemsPerRow===this._itemsPerRow-1},_adjustScrollPosition:function(){var deltaHeight=this._virtualStart===0?this._physicalTop:Math.min(this._scrollPosition+this._physicalTop,0);if(deltaHeight){this._physicalTop=this._physicalTop-deltaHeight;if(!IOS_TOUCH_SCROLLING&&this._physicalTop!==0){this._resetScrollPosition(this._scrollTop-deltaHeight)}}},_resetScrollPosition:function(pos){if(this.scrollTarget){this._scrollTop=pos;this._scrollPosition=this._scrollTop}},_updateScrollerSize:function(forceUpdate){if(this.grid){this._estScrollHeight=this._virtualRowCount*this._rowHeight}else{this._estScrollHeight=this._physicalBottom+Math.max(this._virtualCount-this._physicalCount-this._virtualStart,0)*this._physicalAverage}forceUpdate=forceUpdate||this._scrollHeight===0;forceUpdate=forceUpdate||this._scrollPosition>=this._estScrollHeight-this._physicalSize;forceUpdate=forceUpdate||this.grid&&this.$.items.style.height<this._estScrollHeight;if(forceUpdate||Math.abs(this._estScrollHeight-this._scrollHeight)>=this._optPhysicalSize){this.$.items.style.height=this._estScrollHeight+"px";this._scrollHeight=this._estScrollHeight}},scrollToItem:function(item){return this.scrollToIndex(this.items.indexOf(item))},scrollToIndex:function(idx){if(typeof idx!=="number"||idx<0||idx>this.items.length-1){return}Polymer.dom.flush();if(this._physicalCount===0){return}idx=Math.min(Math.max(idx,0),this._virtualCount-1);if(!this._isIndexRendered(idx)||idx>=this._maxVirtualStart){this._virtualStart=this.grid?idx-this._itemsPerRow*2:idx-1}this._manageFocus();this._assignModels();this._updateMetrics();this._physicalTop=Math.floor(this._virtualStart/this._itemsPerRow)*this._physicalAverage;var currentTopItem=this._physicalStart;var currentVirtualItem=this._virtualStart;var targetOffsetTop=0;var hiddenContentSize=this._hiddenContentSize;while(currentVirtualItem<idx&&targetOffsetTop<=hiddenContentSize){targetOffsetTop=targetOffsetTop+this._getPhysicalSizeIncrement(currentTopItem);currentTopItem=(currentTopItem+1)%this._physicalCount;currentVirtualItem++}this._updateScrollerSize(true);this._positionItems();this._resetScrollPosition(this._physicalTop+this._scrollerPaddingTop+targetOffsetTop);this._increasePoolIfNeeded();this._firstVisibleIndexVal=null;this._lastVisibleIndexVal=null},_resetAverage:function(){this._physicalAverage=0;this._physicalAverageCount=0},_resizeHandler:function(){var delta=Math.abs(this._viewportHeight-this._scrollTargetHeight);if(IOS&&delta>0&&delta<100){return}Polymer.dom.addDebouncer(this.debounce("_debounceTemplate",function(){this.updateViewportBoundaries();this._render();if(this._isVisible){this.toggleScrollListener(true);if(this._physicalCount>0){this._resetAverage();this.scrollToIndex(this.firstVisibleIndex)}}else{this.toggleScrollListener(false)}}.bind(this),1))},_getModelFromItem:function(item){var key=this._collection.getKey(item);var pidx=this._physicalIndexForKey[key];if(pidx!=null){return this._physicalItems[pidx]._templateInstance}return null},_getNormalizedItem:function(item){if(this._collection.getKey(item)===undefined){if(typeof item==="number"){item=this.items[item];if(!item){throw new RangeError("<item> not found")}return item}throw new TypeError("<item> should be a valid item")}return item},selectItem:function(item){item=this._getNormalizedItem(item);var model=this._getModelFromItem(item);if(!this.multiSelection&&this.selectedItem){this.deselectItem(this.selectedItem)}if(model){model[this.selectedAs]=true}this.$.selector.select(item);this.updateSizeForItem(item)},deselectItem:function(item){item=this._getNormalizedItem(item);var model=this._getModelFromItem(item);if(model){model[this.selectedAs]=false}this.$.selector.deselect(item);this.updateSizeForItem(item)},toggleSelectionForItem:function(item){item=this._getNormalizedItem(item);if(this.$.selector.isSelected(item)){this.deselectItem(item)}else{this.selectItem(item)}},clearSelection:function(){function unselect(item){var model=this._getModelFromItem(item);if(model){model[this.selectedAs]=false}}if(Array.isArray(this.selectedItems)){this.selectedItems.forEach(unselect,this)}else if(this.selectedItem){unselect.call(this,this.selectedItem)}this.$.selector.clearSelection()},_selectionEnabledChanged:function(selectionEnabled){var handler=selectionEnabled?this.listen:this.unlisten;handler.call(this,this,"tap","_selectionHandler")},_selectionHandler:function(e){var model=this.modelForElement(e.target);if(!model){return}var modelTabIndex,activeElTabIndex;var target=Polymer.dom(e).path[0];var activeEl=Polymer.dom(this.domHost?this.domHost.root:document).activeElement;var physicalItem=this._physicalItems[this._getPhysicalIndex(model[this.indexAs])];if(target.localName==="input"||target.localName==="button"||target.localName==="select"){return}modelTabIndex=model.tabIndex;model.tabIndex=SECRET_TABINDEX;activeElTabIndex=activeEl?activeEl.tabIndex:-1;model.tabIndex=modelTabIndex;if(activeEl&&physicalItem!==activeEl&&physicalItem.contains(activeEl)&&activeElTabIndex!==SECRET_TABINDEX){return}this.toggleSelectionForItem(model[this.as])},_multiSelectionChanged:function(multiSelection){this.clearSelection();this.$.selector.multi=multiSelection},updateSizeForItem:function(item){item=this._getNormalizedItem(item);var key=this._collection.getKey(item);var pidx=this._physicalIndexForKey[key];if(pidx!=null){this._updateMetrics([pidx]);this._positionItems()}},_manageFocus:function(){var fidx=this._focusedIndex;if(fidx>=0&&fidx<this._virtualCount){if(this._isIndexRendered(fidx)){this._restoreFocusedItem()}else{this._createFocusBackfillItem()}}else if(this._virtualCount>0&&this._physicalCount>0){this._focusedIndex=this._virtualStart;this._focusedItem=this._physicalItems[this._physicalStart]}},_isIndexRendered:function(idx){return idx>=this._virtualStart&&idx<=this._virtualEnd},_isIndexVisible:function(idx){return idx>=this.firstVisibleIndex&&idx<=this.lastVisibleIndex},_getPhysicalIndex:function(idx){return this._physicalIndexForKey[this._collection.getKey(this._getNormalizedItem(idx))]},_focusPhysicalItem:function(idx){if(idx<0||idx>=this._virtualCount){return}this._restoreFocusedItem();if(!this._isIndexRendered(idx)){this.scrollToIndex(idx)}var physicalItem=this._physicalItems[this._getPhysicalIndex(idx)];var model=physicalItem._templateInstance;var focusable;model.tabIndex=SECRET_TABINDEX;if(physicalItem.tabIndex===SECRET_TABINDEX){focusable=physicalItem}if(!focusable){focusable=Polymer.dom(physicalItem).querySelector('[tabindex="'+SECRET_TABINDEX+'"]')}model.tabIndex=0;this._focusedIndex=idx;focusable&&focusable.focus()},_removeFocusedItem:function(){if(this._offscreenFocusedItem){Polymer.dom(this).removeChild(this._offscreenFocusedItem)}this._offscreenFocusedItem=null;this._focusBackfillItem=null;this._focusedItem=null;this._focusedIndex=-1},_createFocusBackfillItem:function(){var fidx=this._focusedIndex;var pidx=this._getPhysicalIndex(fidx);if(this._offscreenFocusedItem||pidx==null||fidx<0){return}if(!this._focusBackfillItem){var stampedTemplate=this.stamp(null);this._focusBackfillItem=stampedTemplate.root.querySelector("*");Polymer.dom(this).appendChild(stampedTemplate.root)}this._offscreenFocusedItem=this._physicalItems[pidx];this._offscreenFocusedItem._templateInstance.tabIndex=0;this._physicalItems[pidx]=this._focusBackfillItem;this.translate3d(0,HIDDEN_Y,0,this._offscreenFocusedItem)},_restoreFocusedItem:function(){var pidx,fidx=this._focusedIndex;if(!this._offscreenFocusedItem||this._focusedIndex<0){return}this._assignModels();pidx=this._getPhysicalIndex(fidx);if(pidx!=null){this._focusBackfillItem=this._physicalItems[pidx];this._focusBackfillItem._templateInstance.tabIndex=-1;this._physicalItems[pidx]=this._offscreenFocusedItem;this._offscreenFocusedItem=null;this.translate3d(0,HIDDEN_Y,0,this._focusBackfillItem)}},_didFocus:function(e){var targetModel=this.modelForElement(e.target);var focusedModel=this._focusedItem?this._focusedItem._templateInstance:null;var hasOffscreenFocusedItem=this._offscreenFocusedItem!==null;var fidx=this._focusedIndex;if(!targetModel||!focusedModel){return}if(focusedModel===targetModel){if(!this._isIndexVisible(fidx)){this.scrollToIndex(fidx)}}else{this._restoreFocusedItem();focusedModel.tabIndex=-1;targetModel.tabIndex=0;fidx=targetModel[this.indexAs];this._focusedIndex=fidx;this._focusedItem=this._physicalItems[this._getPhysicalIndex(fidx)];if(hasOffscreenFocusedItem&&!this._offscreenFocusedItem){this._update()}}},_didMoveUp:function(){this._focusPhysicalItem(this._focusedIndex-1)},_didMoveDown:function(e){e.detail.keyboardEvent.preventDefault();this._focusPhysicalItem(this._focusedIndex+1)},_didEnter:function(e){this._focusPhysicalItem(this._focusedIndex);this._selectionHandler(e.detail.keyboardEvent)}})})();Polymer({is:"iron-scroll-threshold",properties:{upperThreshold:{type:Number,value:100},lowerThreshold:{type:Number,value:100},upperTriggered:{type:Boolean,value:false,notify:true,readOnly:true},lowerTriggered:{type:Boolean,value:false,notify:true,readOnly:true},horizontal:{type:Boolean,value:false}},behaviors:[Polymer.IronScrollTargetBehavior],observers:["_setOverflow(scrollTarget)","_initCheck(horizontal, isAttached)"],get _defaultScrollTarget(){return this},_setOverflow:function(scrollTarget){this.style.overflow=scrollTarget===this?"auto":""},_scrollHandler:function(){var THROTTLE_THRESHOLD=200;if(!this.isDebouncerActive("_checkTheshold")){this.debounce("_checkTheshold",function(){this.checkScrollThesholds()},THROTTLE_THRESHOLD)}},_initCheck:function(horizontal,isAttached){if(isAttached){this.debounce("_init",function(){this.clearTriggers();this.checkScrollThesholds()})}},checkScrollThesholds:function(){if(!this.scrollTarget||this.lowerTriggered&&this.upperTriggered){return}var upperScrollValue=this.horizontal?this._scrollLeft:this._scrollTop;var lowerScrollValue=this.horizontal?this.scrollTarget.scrollWidth-this._scrollTargetWidth-this._scrollLeft:this.scrollTarget.scrollHeight-this._scrollTargetHeight-this._scrollTop;if(upperScrollValue<=this.upperThreshold&&!this.upperTriggered){this._setUpperTriggered(true);this.fire("upper-threshold")}if(lowerScrollValue<=this.lowerThreshold&&!this.lowerTriggered){this._setLowerTriggered(true);this.fire("lower-threshold")}},clearTriggers:function(){this._setUpperTriggered(false);this._setLowerTriggered(false)}});
 // Copyright (c) 2011 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.
@@ -61,7 +61,7 @@
 // Copyright 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-var SelectionTreeNode=function(currentPath){this.currentPath=currentPath;this.leaf=false;this.indexes=[];this.children=[]};SelectionTreeNode.prototype.addChild=function(index,path){this.indexes.push(index);this.children[index]=new SelectionTreeNode(path)};var HistoryListBehavior={properties:{selectedPaths:{type:Object,value:function(){return new Set}},lastSelectedPath:String},listeners:{"history-checkbox-select":"itemSelected_"},addNewResults:function(results,incremental,finished){},hasResults:function(historyDataLength){return historyDataLength>0},noResultsMessage:function(searchedTerm,isLoading){if(isLoading)return"";var messageId=searchedTerm!==""?"noSearchResults":"noResults";return loadTimeData.getString(messageId)},unselectAllItems:function(){this.selectedPaths.forEach(function(path){this.set(path+".selected",false)}.bind(this));this.selectedPaths.clear()},deleteSelected:function(){var toBeRemoved=Array.from(this.selectedPaths.values()).map(function(path){return this.get(path)}.bind(this));md_history.BrowserService.getInstance().deleteItems(toBeRemoved).then(function(){this.removeItemsByPath(Array.from(this.selectedPaths));this.fire("unselect-all")}.bind(this))},removeItemsByPath:function(paths){if(paths.length==0)return;this.removeItemsBeneathNode_(this.buildRemovalTree_(paths))},buildRemovalTree_:function(paths){var rootNode=new SelectionTreeNode(paths[0].split(".")[0]);paths.forEach(function(path){var components=path.split(".");var node=rootNode;components.shift();while(components.length>1){var index=Number(components.shift());var arrayName=components.shift();if(!node.children[index])node.addChild(index,[node.currentPath,index,arrayName].join("."));node=node.children[index]}node.leaf=true;node.indexes.push(Number(components.shift()))});return rootNode},removeItemsBeneathNode_:function(node){var array=this.get(node.currentPath);var splices=[];node.indexes.sort(function(a,b){return b-a});node.indexes.forEach(function(index){if(node.leaf||this.removeItemsBeneathNode_(node.children[index])){var item=array.splice(index,1)[0];splices.push({index:index,removed:[item],addedCount:0,object:array,type:"splice"})}}.bind(this));if(array.length==0&&node.currentPath.indexOf(".")!=-1)return true;this.notifySplices(node.currentPath,splices);return false},itemSelected_:function(e){var item=e.detail.element;var paths=[];var itemPath=item.path;if(e.detail.shiftKey&&this.lastSelectedPath){var itemPathComponents=itemPath.split(".");var itemIndex=Number(itemPathComponents.pop());var itemArrayPath=itemPathComponents.join(".");var lastItemPathComponents=this.lastSelectedPath.split(".");var lastItemIndex=Number(lastItemPathComponents.pop());if(itemArrayPath==lastItemPathComponents.join(".")){for(var i=Math.min(itemIndex,lastItemIndex);i<=Math.max(itemIndex,lastItemIndex);i++){paths.push(itemArrayPath+"."+i)}}}if(paths.length==0)paths.push(item.path);var selected=!this.selectedPaths.has(item.path);paths.forEach(function(path){this.set(path+".selected",selected);if(selected){this.selectedPaths.add(path);return}this.selectedPaths.delete(path)}.bind(this));this.lastSelectedPath=itemPath}};
+var SelectionTreeNode=function(currentPath){this.currentPath=currentPath;this.leaf=false;this.indexes=[];this.children=[]};SelectionTreeNode.prototype.addChild=function(index,path){this.indexes.push(index);this.children[index]=new SelectionTreeNode(path)};var HistoryListBehavior={properties:{initialData:Array,selectedPaths:{type:Object,value:function(){return new Set}},lastSelectedPath:String},listeners:{"history-checkbox-select":"itemSelected_"},attached:function(){if(this.initialData)this.addNewResults(this.initialData,false,false)},addNewResults:function(results,incremental,finished){},hasResults:function(historyDataLength){return historyDataLength>0},noResultsMessage:function(searchedTerm,isLoading){if(isLoading)return"";var messageId=searchedTerm!==""?"noSearchResults":"noResults";return loadTimeData.getString(messageId)},unselectAllItems:function(){this.selectedPaths.forEach(function(path){this.set(path+".selected",false)}.bind(this));this.selectedPaths.clear()},deleteSelected:function(){var toBeRemoved=Array.from(this.selectedPaths.values()).map(function(path){return this.get(path)}.bind(this));md_history.BrowserService.getInstance().deleteItems(toBeRemoved).then(function(){this.removeItemsByPath(Array.from(this.selectedPaths));this.fire("unselect-all")}.bind(this))},removeItemsByPath:function(paths){if(paths.length==0)return;this.removeItemsBeneathNode_(this.buildRemovalTree_(paths))},buildRemovalTree_:function(paths){var rootNode=new SelectionTreeNode(paths[0].split(".")[0]);paths.forEach(function(path){var components=path.split(".");var node=rootNode;components.shift();while(components.length>1){var index=Number(components.shift());var arrayName=components.shift();if(!node.children[index])node.addChild(index,[node.currentPath,index,arrayName].join("."));node=node.children[index]}node.leaf=true;node.indexes.push(Number(components.shift()))});return rootNode},removeItemsBeneathNode_:function(node){var array=this.get(node.currentPath);var splices=[];node.indexes.sort(function(a,b){return b-a});node.indexes.forEach(function(index){if(node.leaf||this.removeItemsBeneathNode_(node.children[index])){var item=array.splice(index,1)[0];splices.push({index:index,removed:[item],addedCount:0,object:array,type:"splice"})}}.bind(this));if(array.length==0&&node.currentPath.indexOf(".")!=-1)return true;this.notifySplices(node.currentPath,splices);return false},itemSelected_:function(e){var item=e.detail.element;var paths=[];var itemPath=item.path;if(e.detail.shiftKey&&this.lastSelectedPath){var itemPathComponents=itemPath.split(".");var itemIndex=Number(itemPathComponents.pop());var itemArrayPath=itemPathComponents.join(".");var lastItemPathComponents=this.lastSelectedPath.split(".");var lastItemIndex=Number(lastItemPathComponents.pop());if(itemArrayPath==lastItemPathComponents.join(".")){for(var i=Math.min(itemIndex,lastItemIndex);i<=Math.max(itemIndex,lastItemIndex);i++){paths.push(itemArrayPath+"."+i)}}}if(paths.length==0)paths.push(item.path);var selected=!this.selectedPaths.has(item.path);paths.forEach(function(path){this.set(path+".selected",selected);if(selected){this.selectedPaths.add(path);return}this.selectedPaths.delete(path)}.bind(this));this.lastSelectedPath=itemPath}};
 // Copyright 2015 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
@@ -69,15 +69,15 @@
 // Copyright 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-Polymer({is:"history-list-container",properties:{selectedPage_:{type:String,computed:"computeSelectedPage_(queryState.range)"},grouped:Boolean,queryState:Object,queryResult:Object,actionMenuModel_:Object},observers:["groupedRangeChanged_(queryState.range)"],listeners:{"open-menu":"openMenu_"},historyResult:function(info,results){this.initializeResults_(info,results);this.closeMenu_();if(info.term&&!this.queryState.incremental){Polymer.IronA11yAnnouncer.requestAvailability();this.fire("iron-announce",{text:md_history.HistoryItem.searchResultsTitle(results.length,info.term)})}var list=this.getSelectedList_();list.addNewResults(results,this.queryState.incremental,info.finished)},historyDeleted:function(){if(this.getSelectedItemCount()>0)return;this.fire("query-history",false)},getContentScrollTarget:function(){return this.getSelectedList_()},getSelectedItemCount:function(){return this.getSelectedList_().selectedPaths.size},unselectAllItems:function(count){var selectedList=this.getSelectedList_();if(selectedList)selectedList.unselectAllItems(count)},deleteSelectedWithPrompt:function(){if(!loadTimeData.getBoolean("allowDeletingHistory"))return;var browserService=md_history.BrowserService.getInstance();browserService.recordAction("RemoveSelected");if(this.queryState.searchTerm!="")browserService.recordAction("SearchResultRemove");this.$.dialog.get().showModal();this.$$(".action-button").focus()},computeSelectedPage_:function(range){return range==HistoryRange.ALL_TIME?"infinite-list":"grouped-list"},groupedRangeChanged_:function(range){if(range!=HistoryRange.ALL_TIME&&this.queryResult.info){this.set("queryResult.results",[]);this.historyResult(this.queryResult.info,[])}},initializeResults_:function(info,results){if(results.length==0)return;var currentDate=results[0].dateRelativeDay;for(var i=0;i<results.length;i++){results[i].selected=false;results[i].readableTimestamp=info.term==""?results[i].dateTimeOfDay:results[i].dateShort;if(results[i].dateRelativeDay!=currentDate){currentDate=results[i].dateRelativeDay}}},onDialogConfirmTap_:function(){md_history.BrowserService.getInstance().recordAction("ConfirmRemoveSelected");this.getSelectedList_().deleteSelected();var dialog=assert(this.$.dialog.getIfExists());dialog.close()},onDialogCancelTap_:function(){md_history.BrowserService.getInstance().recordAction("CancelRemoveSelected");var dialog=assert(this.$.dialog.getIfExists());dialog.close()},closeMenu_:function(){var menu=this.$.sharedMenu.getIfExists();if(menu&&menu.open){this.actionMenuModel_=null;menu.close()}},openMenu_:function(e){var target=e.detail.target;this.actionMenuModel_=e.detail;var menu=this.$.sharedMenu.get();menu.showAt(target)},onMoreFromSiteTap_:function(){md_history.BrowserService.getInstance().recordAction("EntryMenuShowMoreFromSite");var menu=assert(this.$.sharedMenu.getIfExists());this.fire("change-query",{search:this.actionMenuModel_.item.domain});this.actionMenuModel_=null;this.closeMenu_()},onRemoveFromHistoryTap_:function(){var browserService=md_history.BrowserService.getInstance();browserService.recordAction("EntryMenuRemoveFromHistory");var menu=assert(this.$.sharedMenu.getIfExists());var itemData=this.actionMenuModel_;browserService.deleteItems([itemData.item]).then(function(items){this.fire("unselect-all");this.getSelectedList_().removeItemsByPath([itemData.path]);var index=itemData.index;if(index==undefined)return;var browserService=md_history.BrowserService.getInstance();browserService.recordHistogram("HistoryPage.RemoveEntryPosition",Math.min(index,UMA_MAX_BUCKET_VALUE),UMA_MAX_BUCKET_VALUE);if(index<=UMA_MAX_SUBSET_BUCKET_VALUE){browserService.recordHistogram("HistoryPage.RemoveEntryPositionSubset",index,UMA_MAX_SUBSET_BUCKET_VALUE)}}.bind(this));this.closeMenu_()},getSelectedList_:function(){return this.$$("#"+this.selectedPage_)},canDeleteHistory_:function(){return loadTimeData.getBoolean("allowDeletingHistory")}});
+Polymer({is:"history-list-container",properties:{selectedPage_:{type:String,computed:"computeSelectedPage_(queryState.range)"},grouped:Boolean,queryState:Object,queryResult:Object,actionMenuModel_:Object},listeners:{"open-menu":"openMenu_"},historyResult:function(info,results){this.initializeResults_(info,results);this.closeMenu_();if(info.term&&!this.queryState.incremental){Polymer.IronA11yAnnouncer.requestAvailability();this.fire("iron-announce",{text:md_history.HistoryItem.searchResultsTitle(results.length,info.term)})}var list=this.getSelectedList_();if(Polymer.isInstance(list))list.addNewResults(results,this.queryState.incremental,info.finished);else list.initialData=results},historyDeleted:function(){if(this.getSelectedItemCount()>0)return;this.fire("query-history",false)},getContentScrollTarget:function(){return this.getSelectedList_()},getSelectedItemCount:function(){return this.getSelectedList_().selectedPaths.size},unselectAllItems:function(count){var selectedList=this.getSelectedList_();if(selectedList)selectedList.unselectAllItems(count)},deleteSelectedWithPrompt:function(){if(!loadTimeData.getBoolean("allowDeletingHistory"))return;var browserService=md_history.BrowserService.getInstance();browserService.recordAction("RemoveSelected");if(this.queryState.searchTerm!="")browserService.recordAction("SearchResultRemove");this.$.dialog.get().showModal();this.$$(".action-button").focus()},computeSelectedPage_:function(range){return range==HistoryRange.ALL_TIME?"infinite-list":"grouped-list"},initializeResults_:function(info,results){if(results.length==0)return;var currentDate=results[0].dateRelativeDay;for(var i=0;i<results.length;i++){results[i].selected=false;results[i].readableTimestamp=info.term==""?results[i].dateTimeOfDay:results[i].dateShort;if(results[i].dateRelativeDay!=currentDate){currentDate=results[i].dateRelativeDay}}},onDialogConfirmTap_:function(){md_history.BrowserService.getInstance().recordAction("ConfirmRemoveSelected");this.getSelectedList_().deleteSelected();var dialog=assert(this.$.dialog.getIfExists());dialog.close()},onDialogCancelTap_:function(){md_history.BrowserService.getInstance().recordAction("CancelRemoveSelected");var dialog=assert(this.$.dialog.getIfExists());dialog.close()},closeMenu_:function(){var menu=this.$.sharedMenu.getIfExists();if(menu&&menu.open){this.actionMenuModel_=null;menu.close()}},openMenu_:function(e){var target=e.detail.target;this.actionMenuModel_=e.detail;var menu=this.$.sharedMenu.get();menu.showAt(target)},onMoreFromSiteTap_:function(){md_history.BrowserService.getInstance().recordAction("EntryMenuShowMoreFromSite");var menu=assert(this.$.sharedMenu.getIfExists());this.fire("change-query",{search:this.actionMenuModel_.item.domain});this.actionMenuModel_=null;this.closeMenu_()},onRemoveFromHistoryTap_:function(){var browserService=md_history.BrowserService.getInstance();browserService.recordAction("EntryMenuRemoveFromHistory");var menu=assert(this.$.sharedMenu.getIfExists());var itemData=this.actionMenuModel_;browserService.deleteItems([itemData.item]).then(function(items){this.fire("unselect-all");this.getSelectedList_().removeItemsByPath([itemData.path]);var index=itemData.index;if(index==undefined)return;var browserService=md_history.BrowserService.getInstance();browserService.recordHistogram("HistoryPage.RemoveEntryPosition",Math.min(index,UMA_MAX_BUCKET_VALUE),UMA_MAX_BUCKET_VALUE);if(index<=UMA_MAX_SUBSET_BUCKET_VALUE){browserService.recordHistogram("HistoryPage.RemoveEntryPositionSubset",index,UMA_MAX_SUBSET_BUCKET_VALUE)}}.bind(this));this.closeMenu_()},getSelectedList_:function(){return this.$$("#"+this.selectedPage_)},canDeleteHistory_:function(){return loadTimeData.getBoolean("allowDeletingHistory")}});
 // 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.
-Polymer({is:"history-query-manager",properties:{queryState:{type:Object,notify:true,value:function(){return{incremental:false,querying:true,queryingDisabled:false,_range:HistoryRange.ALL_TIME,searchTerm:"",groupedOffset:0,set range(val){this._range=Number(val)},get range(){return this._range}}}},queryResult:Object},observers:["searchTermChanged_(queryState.searchTerm)"],documentListeners_:{},attached:function(){this.documentListeners_["change-query"]=this.onChangeQuery_.bind(this);this.documentListeners_["query-history"]=this.onQueryHistory_.bind(this);for(var e in this.documentListeners_)document.addEventListener(e,this.documentListeners_[e])},detached:function(){for(var e in this.documentListeners_)document.removeEventListener(e,this.documentListeners_[e])},queryHistory_:function(incremental){var queryState=this.queryState;var noResults=!this.queryResult||this.queryResult.results==null;if(queryState.queryingDisabled||!this.queryState.searchTerm&&noResults){return}this.set("queryState.querying",true);this.set("queryState.incremental",incremental);var lastVisitTime=0;if(incremental){var lastVisit=this.queryResult.results.slice(-1)[0];lastVisitTime=lastVisit?Math.floor(lastVisit.time):0}var maxResults=this.queryState.range==HistoryRange.ALL_TIME?RESULTS_PER_PAGE:0;chrome.send("queryHistory",[queryState.searchTerm,queryState.groupedOffset,queryState.range,lastVisitTime,maxResults])},onChangeQuery_:function(e){var changes=e.detail;var needsUpdate=false;if(changes.range!=null&&changes.range!=this.queryState.range){this.set("queryState.range",changes.range);needsUpdate=true;if(!changes.offset)this.set("queryState.groupedOffset",0);this.fire("history-view-changed")}if(changes.offset!=null&&changes.offset!=this.queryState.groupedOffset){this.set("queryState.groupedOffset",changes.offset);needsUpdate=true}if(changes.search!=null&&changes.search!=this.queryState.searchTerm){this.set("queryState.searchTerm",changes.search);needsUpdate=true}if(needsUpdate)this.queryHistory_(false)},onQueryHistory_:function(e){this.queryHistory_(e.detail);return false},searchTermChanged_:function(){if(this.queryState.searchTerm)md_history.BrowserService.getInstance().recordAction("Search")}});(function(){"use strict";Polymer({is:"iron-location",properties:{path:{type:String,notify:true,value:function(){return window.decodeURIComponent(window.location.pathname)}},query:{type:String,notify:true,value:function(){return window.decodeURIComponent(window.location.search.slice(1))}},hash:{type:String,notify:true,value:function(){return window.decodeURIComponent(window.location.hash.slice(1))}},dwellTime:{type:Number,value:2e3},urlSpaceRegex:{type:String,value:""},_urlSpaceRegExp:{computed:"_makeRegExp(urlSpaceRegex)"},_lastChangedAt:{type:Number},_initialized:{type:Boolean,value:false}},hostAttributes:{hidden:true},observers:["_updateUrl(path, query, hash)"],attached:function(){this.listen(window,"hashchange","_hashChanged");this.listen(window,"location-changed","_urlChanged");this.listen(window,"popstate","_urlChanged");this.listen(document.body,"click","_globalOnClick");this._lastChangedAt=window.performance.now()-(this.dwellTime-200);this._initialized=true;this._urlChanged()},detached:function(){this.unlisten(window,"hashchange","_hashChanged");this.unlisten(window,"location-changed","_urlChanged");this.unlisten(window,"popstate","_urlChanged");this.unlisten(document.body,"click","_globalOnClick");this._initialized=false},_hashChanged:function(){this.hash=window.decodeURIComponent(window.location.hash.substring(1))},_urlChanged:function(){this._dontUpdateUrl=true;this._hashChanged();this.path=window.decodeURIComponent(window.location.pathname);this.query=window.decodeURIComponent(window.location.search.substring(1));this._dontUpdateUrl=false;this._updateUrl()},_getUrl:function(){var partiallyEncodedPath=window.encodeURI(this.path).replace(/\#/g,"%23").replace(/\?/g,"%3F");var partiallyEncodedQuery="";if(this.query){partiallyEncodedQuery="?"+window.encodeURI(this.query).replace(/\#/g,"%23")}var partiallyEncodedHash="";if(this.hash){partiallyEncodedHash="#"+window.encodeURI(this.hash)}return partiallyEncodedPath+partiallyEncodedQuery+partiallyEncodedHash},_updateUrl:function(){if(this._dontUpdateUrl||!this._initialized){return}if(this.path===window.decodeURIComponent(window.location.pathname)&&this.query===window.decodeURIComponent(window.location.search.substring(1))&&this.hash===window.decodeURIComponent(window.location.hash.substring(1))){return}var newUrl=this._getUrl();var fullNewUrl=new URL(newUrl,window.location.protocol+"//"+window.location.host).href;var now=window.performance.now();var shouldReplace=this._lastChangedAt+this.dwellTime>now;this._lastChangedAt=now;if(shouldReplace){window.history.replaceState({},"",fullNewUrl)}else{window.history.pushState({},"",fullNewUrl)}this.fire("location-changed",{},{node:window})},_globalOnClick:function(event){if(event.defaultPrevented){return}var href=this._getSameOriginLinkHref(event);if(!href){return}event.preventDefault();if(href===window.location.href){return}window.history.pushState({},"",href);this.fire("location-changed",{},{node:window})},_getSameOriginLinkHref:function(event){if(event.button!==0){return null}if(event.metaKey||event.ctrlKey){return null}var eventPath=Polymer.dom(event).path;var anchor=null;for(var i=0;i<eventPath.length;i++){var element=eventPath[i];if(element.tagName==="A"&&element.href){anchor=element;break}}if(!anchor){return null}if(anchor.target==="_blank"){return null}if((anchor.target==="_top"||anchor.target==="_parent")&&window.top!==window){return null}var href=anchor.href;var url;if(document.baseURI!=null){url=new URL(href,document.baseURI)}else{url=new URL(href)}var origin;if(window.location.origin){origin=window.location.origin}else{origin=window.location.protocol+"//"+window.location.hostname;if(window.location.port){origin+=":"+window.location.port}}if(url.origin!==origin){return null}var normalizedHref=url.pathname+url.search+url.hash;if(this._urlSpaceRegExp&&!this._urlSpaceRegExp.test(normalizedHref)){return null}var fullNormalizedHref=new URL(normalizedHref,window.location.href).href;return fullNormalizedHref},_makeRegExp:function(urlSpaceRegex){return RegExp(urlSpaceRegex)}})})();"use strict";Polymer({is:"iron-query-params",properties:{paramsString:{type:String,notify:true,observer:"paramsStringChanged"},paramsObject:{type:Object,notify:true,value:function(){return{}}},_dontReact:{type:Boolean,value:false}},hostAttributes:{hidden:true},observers:["paramsObjectChanged(paramsObject.*)"],paramsStringChanged:function(){this._dontReact=true;this.paramsObject=this._decodeParams(this.paramsString);this._dontReact=false},paramsObjectChanged:function(){if(this._dontReact){return}this.paramsString=this._encodeParams(this.paramsObject)},_encodeParams:function(params){var encodedParams=[];for(var key in params){var value=params[key];if(value===""){encodedParams.push(encodeURIComponent(key))}else if(value){encodedParams.push(encodeURIComponent(key)+"="+encodeURIComponent(value.toString()))}}return encodedParams.join("&")},_decodeParams:function(paramString){var params={};paramString=(paramString||"").replace(/\+/g,"%20");var paramList=paramString.split("&");for(var i=0;i<paramList.length;i++){var param=paramList[i].split("=");if(param[0]){params[decodeURIComponent(param[0])]=decodeURIComponent(param[1]||"")}}return params}});
+Polymer({is:"history-query-manager",properties:{queryState:{type:Object,notify:true,value:function(){return{incremental:false,querying:true,queryingDisabled:false,_range:HistoryRange.ALL_TIME,searchTerm:"",groupedOffset:0,set range(val){this._range=Number(val)},get range(){return this._range}}}},queryResult:Object,router:Object},observers:["searchTermChanged_(queryState.searchTerm)"],documentListeners_:{},attached:function(){this.documentListeners_["change-query"]=this.onChangeQuery_.bind(this);this.documentListeners_["query-history"]=this.onQueryHistory_.bind(this);for(var e in this.documentListeners_)document.addEventListener(e,this.documentListeners_[e])},detached:function(){for(var e in this.documentListeners_)document.removeEventListener(e,this.documentListeners_[e])},queryHistory_:function(incremental){var queryState=this.queryState;if(queryState.queryingDisabled)return;this.set("queryState.querying",true);this.set("queryState.incremental",incremental);var lastVisitTime=0;if(incremental){var lastVisit=this.queryResult.results.slice(-1)[0];lastVisitTime=lastVisit?Math.floor(lastVisit.time):0}var maxResults=this.queryState.range==HistoryRange.ALL_TIME?RESULTS_PER_PAGE:0;chrome.send("queryHistory",[queryState.searchTerm,queryState.groupedOffset,queryState.range,lastVisitTime,maxResults])},onChangeQuery_:function(e){var changes=e.detail;var needsUpdate=false;if(changes.range!=null&&changes.range!=this.queryState.range){this.set("queryState.range",changes.range);needsUpdate=true;if(!changes.offset)this.set("queryState.groupedOffset",0);this.fire("history-view-changed")}if(changes.offset!=null&&changes.offset!=this.queryState.groupedOffset){this.set("queryState.groupedOffset",changes.offset);needsUpdate=true}if(changes.search!=null&&changes.search!=this.queryState.searchTerm){this.set("queryState.searchTerm",changes.search);needsUpdate=true}if(needsUpdate){this.queryHistory_(false);if(this.router)this.router.serializeUrl()}},onQueryHistory_:function(e){this.queryHistory_(e.detail);return false},searchTermChanged_:function(){if(this.queryState.searchTerm)md_history.BrowserService.getInstance().recordAction("Search")}});(function(){"use strict";Polymer({is:"iron-location",properties:{path:{type:String,notify:true,value:function(){return window.decodeURIComponent(window.location.pathname)}},query:{type:String,notify:true,value:function(){return window.decodeURIComponent(window.location.search.slice(1))}},hash:{type:String,notify:true,value:function(){return window.decodeURIComponent(window.location.hash.slice(1))}},dwellTime:{type:Number,value:2e3},urlSpaceRegex:{type:String,value:""},_urlSpaceRegExp:{computed:"_makeRegExp(urlSpaceRegex)"},_lastChangedAt:{type:Number},_initialized:{type:Boolean,value:false}},hostAttributes:{hidden:true},observers:["_updateUrl(path, query, hash)"],attached:function(){this.listen(window,"hashchange","_hashChanged");this.listen(window,"location-changed","_urlChanged");this.listen(window,"popstate","_urlChanged");this.listen(document.body,"click","_globalOnClick");this._lastChangedAt=window.performance.now()-(this.dwellTime-200);this._initialized=true;this._urlChanged()},detached:function(){this.unlisten(window,"hashchange","_hashChanged");this.unlisten(window,"location-changed","_urlChanged");this.unlisten(window,"popstate","_urlChanged");this.unlisten(document.body,"click","_globalOnClick");this._initialized=false},_hashChanged:function(){this.hash=window.decodeURIComponent(window.location.hash.substring(1))},_urlChanged:function(){this._dontUpdateUrl=true;this._hashChanged();this.path=window.decodeURIComponent(window.location.pathname);this.query=window.decodeURIComponent(window.location.search.substring(1));this._dontUpdateUrl=false;this._updateUrl()},_getUrl:function(){var partiallyEncodedPath=window.encodeURI(this.path).replace(/\#/g,"%23").replace(/\?/g,"%3F");var partiallyEncodedQuery="";if(this.query){partiallyEncodedQuery="?"+window.encodeURI(this.query).replace(/\#/g,"%23")}var partiallyEncodedHash="";if(this.hash){partiallyEncodedHash="#"+window.encodeURI(this.hash)}return partiallyEncodedPath+partiallyEncodedQuery+partiallyEncodedHash},_updateUrl:function(){if(this._dontUpdateUrl||!this._initialized){return}if(this.path===window.decodeURIComponent(window.location.pathname)&&this.query===window.decodeURIComponent(window.location.search.substring(1))&&this.hash===window.decodeURIComponent(window.location.hash.substring(1))){return}var newUrl=this._getUrl();var fullNewUrl=new URL(newUrl,window.location.protocol+"//"+window.location.host).href;var now=window.performance.now();var shouldReplace=this._lastChangedAt+this.dwellTime>now;this._lastChangedAt=now;if(shouldReplace){window.history.replaceState({},"",fullNewUrl)}else{window.history.pushState({},"",fullNewUrl)}this.fire("location-changed",{},{node:window})},_globalOnClick:function(event){if(event.defaultPrevented){return}var href=this._getSameOriginLinkHref(event);if(!href){return}event.preventDefault();if(href===window.location.href){return}window.history.pushState({},"",href);this.fire("location-changed",{},{node:window})},_getSameOriginLinkHref:function(event){if(event.button!==0){return null}if(event.metaKey||event.ctrlKey){return null}var eventPath=Polymer.dom(event).path;var anchor=null;for(var i=0;i<eventPath.length;i++){var element=eventPath[i];if(element.tagName==="A"&&element.href){anchor=element;break}}if(!anchor){return null}if(anchor.target==="_blank"){return null}if((anchor.target==="_top"||anchor.target==="_parent")&&window.top!==window){return null}var href=anchor.href;var url;if(document.baseURI!=null){url=new URL(href,document.baseURI)}else{url=new URL(href)}var origin;if(window.location.origin){origin=window.location.origin}else{origin=window.location.protocol+"//"+window.location.hostname;if(window.location.port){origin+=":"+window.location.port}}if(url.origin!==origin){return null}var normalizedHref=url.pathname+url.search+url.hash;if(this._urlSpaceRegExp&&!this._urlSpaceRegExp.test(normalizedHref)){return null}var fullNormalizedHref=new URL(normalizedHref,window.location.href).href;return fullNormalizedHref},_makeRegExp:function(urlSpaceRegex){return RegExp(urlSpaceRegex)}})})();"use strict";Polymer({is:"iron-query-params",properties:{paramsString:{type:String,notify:true,observer:"paramsStringChanged"},paramsObject:{type:Object,notify:true,value:function(){return{}}},_dontReact:{type:Boolean,value:false}},hostAttributes:{hidden:true},observers:["paramsObjectChanged(paramsObject.*)"],paramsStringChanged:function(){this._dontReact=true;this.paramsObject=this._decodeParams(this.paramsString);this._dontReact=false},paramsObjectChanged:function(){if(this._dontReact){return}this.paramsString=this._encodeParams(this.paramsObject)},_encodeParams:function(params){var encodedParams=[];for(var key in params){var value=params[key];if(value===""){encodedParams.push(encodeURIComponent(key))}else if(value){encodedParams.push(encodeURIComponent(key)+"="+encodeURIComponent(value.toString()))}}return encodedParams.join("&")},_decodeParams:function(paramString){var params={};paramString=(paramString||"").replace(/\+/g,"%20");var paramList=paramString.split("&");for(var i=0;i<paramList.length;i++){var param=paramList[i].split("=");if(param[0]){params[decodeURIComponent(param[0])]=decodeURIComponent(param[1]||"")}}return params}});
 // Copyright 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-Polymer({is:"history-router",properties:{selectedPage:{type:String,observer:"serializePath_",notify:true},path_:{type:String,observer:"pathChanged_"},queryState:Object,queryParams_:Object},observers:["queryParamsChanged_(queryParams_.*)","searchTermChanged_(queryState.searchTerm)"],attached:function(){if(window.location.hash){window.location.href=window.location.href.split("#")[0]+"?"+window.location.hash.substr(1)}},serializePath_:function(){var page=this.selectedPage=="history"?"":this.selectedPage;this.path_="/"+page},pathChanged_:function(){var sections=this.path_.substr(1).split("/");this.selectedPage=sections[0]||"history"},queryParamsChanged_:function(){this.fire("change-query",{search:this.queryParams_.q||""})},searchTermChanged_:function(){this.set("queryParams_.q",this.queryState.searchTerm||null)}});Polymer.IronMultiSelectableBehaviorImpl={properties:{multi:{type:Boolean,value:false,observer:"multiChanged"},selectedValues:{type:Array,notify:true},selectedItems:{type:Array,readOnly:true,notify:true}},observers:["_updateSelected(selectedValues.splices)"],select:function(value){if(this.multi){if(this.selectedValues){this._toggleSelected(value)}else{this.selectedValues=[value]}}else{this.selected=value}},multiChanged:function(multi){this._selection.multi=multi},get _shouldUpdateSelection(){return this.selected!=null||this.selectedValues!=null&&this.selectedValues.length},_updateAttrForSelected:function(){if(!this.multi){Polymer.IronSelectableBehavior._updateAttrForSelected.apply(this)}else if(this._shouldUpdateSelection){this.selectedValues=this.selectedItems.map(function(selectedItem){return this._indexToValue(this.indexOf(selectedItem))},this).filter(function(unfilteredValue){return unfilteredValue!=null},this)}},_updateSelected:function(){if(this.multi){this._selectMulti(this.selectedValues)}else{this._selectSelected(this.selected)}},_selectMulti:function(values){if(values){var selectedItems=this._valuesToItems(values);this._selection.clear(selectedItems);for(var i=0;i<selectedItems.length;i++){this._selection.setItemSelected(selectedItems[i],true)}if(this.fallbackSelection&&this.items.length&&!this._selection.get().length){var fallback=this._valueToItem(this.fallbackSelection);if(fallback){this.selectedValues=[this.fallbackSelection]}}}else{this._selection.clear()}},_selectionChange:function(){var s=this._selection.get();if(this.multi){this._setSelectedItems(s)}else{this._setSelectedItems([s]);this._setSelectedItem(s)}},_toggleSelected:function(value){var i=this.selectedValues.indexOf(value);var unselected=i<0;if(unselected){this.push("selectedValues",value)}else{this.splice("selectedValues",i,1)}},_valuesToItems:function(values){return values==null?null:values.map(function(value){return this._valueToItem(value)},this)}};Polymer.IronMultiSelectableBehavior=[Polymer.IronSelectableBehavior,Polymer.IronMultiSelectableBehaviorImpl];Polymer({is:"iron-selector",behaviors:[Polymer.IronMultiSelectableBehavior]});
+Polymer({is:"history-router",properties:{selectedPage:{type:String,notify:true,observer:"selectedPageChanged_"},queryState:Object,grouped:Boolean,path_:String,queryParams_:Object},parsing_:false,observers:["onUrlChanged_(path_, queryParams_)"],attached:function(){if(window.location.hash){window.location.href=window.location.href.split("#")[0]+"?"+window.location.hash.substr(1)}},serializeUrl:function(){var path=this.selectedPage;if(path=="history"&&this.queryState.range!=HistoryRange.ALL_TIME)path+="/"+this.rangeToString_(this.queryState.range);if(path=="history")path="";var offsetParam=null;if(this.selectedPage=="history"&&this.queryState.groupedOffset)offsetParam=this.queryState.groupedOffset;this.path_="/"+path;this.set("queryParams_.offset",offsetParam);this.set("queryParams_.q",this.queryState.searchTerm||null)},selectedPageChanged_:function(){if(!this.parsing_)this.serializeUrl()},parseUrl_:function(){this.parsing_=true;var changes={};var sections=this.path_.substr(1).split("/");var page=sections[0]||"history";if(page=="history"&&this.grouped){var range=sections.length>1?this.stringToRange_(sections[1]):HistoryRange.ALL_TIME;changes.range=range;changes.offset=Number(this.queryParams_.offset)||0}changes.search=this.queryParams_.q||"";this.selectedPage=page;this.fire("change-query",changes);this.serializeUrl();this.parsing_=false},onUrlChanged_:function(){this.debounce("parseUrl",this.parseUrl_.bind(this))},rangeToString_:function(range){switch(range){case HistoryRange.WEEK:return"week";case HistoryRange.MONTH:return"month";default:return""}},stringToRange_:function(str){switch(str){case"week":return HistoryRange.WEEK;case"month":return HistoryRange.MONTH;default:return HistoryRange.ALL_TIME}}});Polymer.IronMultiSelectableBehaviorImpl={properties:{multi:{type:Boolean,value:false,observer:"multiChanged"},selectedValues:{type:Array,notify:true},selectedItems:{type:Array,readOnly:true,notify:true}},observers:["_updateSelected(selectedValues.splices)"],select:function(value){if(this.multi){if(this.selectedValues){this._toggleSelected(value)}else{this.selectedValues=[value]}}else{this.selected=value}},multiChanged:function(multi){this._selection.multi=multi},get _shouldUpdateSelection(){return this.selected!=null||this.selectedValues!=null&&this.selectedValues.length},_updateAttrForSelected:function(){if(!this.multi){Polymer.IronSelectableBehavior._updateAttrForSelected.apply(this)}else if(this._shouldUpdateSelection){this.selectedValues=this.selectedItems.map(function(selectedItem){return this._indexToValue(this.indexOf(selectedItem))},this).filter(function(unfilteredValue){return unfilteredValue!=null},this)}},_updateSelected:function(){if(this.multi){this._selectMulti(this.selectedValues)}else{this._selectSelected(this.selected)}},_selectMulti:function(values){if(values){var selectedItems=this._valuesToItems(values);this._selection.clear(selectedItems);for(var i=0;i<selectedItems.length;i++){this._selection.setItemSelected(selectedItems[i],true)}if(this.fallbackSelection&&this.items.length&&!this._selection.get().length){var fallback=this._valueToItem(this.fallbackSelection);if(fallback){this.selectedValues=[this.fallbackSelection]}}}else{this._selection.clear()}},_selectionChange:function(){var s=this._selection.get();if(this.multi){this._setSelectedItems(s)}else{this._setSelectedItems([s]);this._setSelectedItem(s)}},_toggleSelected:function(value){var i=this.selectedValues.indexOf(value);var unselected=i<0;if(unselected){this.push("selectedValues",value)}else{this.splice("selectedValues",i,1)}},_valuesToItems:function(values){return values==null?null:values.map(function(value){return this._valueToItem(value)},this)}};Polymer.IronMultiSelectableBehavior=[Polymer.IronSelectableBehavior,Polymer.IronMultiSelectableBehaviorImpl];Polymer({is:"iron-selector",behaviors:[Polymer.IronMultiSelectableBehavior]});
 // Copyright 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
diff --git a/chrome/browser/resources/md_history/app.html b/chrome/browser/resources/md_history/app.html
index 9358a5e..4bf719c 100644
--- a/chrome/browser/resources/md_history/app.html
+++ b/chrome/browser/resources/md_history/app.html
@@ -69,9 +69,12 @@
       }
     </style>
     <history-query-manager query-state="{{queryState_}}"
-        query-result="[[queryResult_]]">
+        query-result="[[queryResult_]]"
+        router="[[$$('#router')]]">
     </history-query-manager>
-    <history-router selected-page="{{selectedPage_}}"
+    <history-router id="router"
+        selected-page="{{selectedPage_}}"
+        grouped="[[grouped_]]"
         query-state="[[queryState_]]">
     </history-router>
     <history-toolbar id="toolbar"
diff --git a/chrome/browser/resources/md_history/app.vulcanized.html b/chrome/browser/resources/md_history/app.vulcanized.html
index 9adde39b..f8800fc 100644
--- a/chrome/browser/resources/md_history/app.vulcanized.html
+++ b/chrome/browser/resources/md_history/app.vulcanized.html
@@ -2433,13 +2433,13 @@
           <span id="grouped-date">
             [[getHistoryInterval_(queryInfo)]]
           </span>
-          <button is="paper-icon-button-light" id="today-button" class="icon-button" title="$i18n{rangeToday}" on-tap="onTodayTap_" disabled="[[isToday_(groupedOffset)]]">
+          <button is="paper-icon-button-light" id="today-button" class="icon-button" title="$i18n{rangeToday}" on-click="onTodayTap_" disabled="[[isToday_(groupedOffset)]]">
             <iron-icon icon="history:today"></iron-icon>
           </button>
-          <button is="paper-icon-button-light" id="prev-button" title="$i18n{rangePrevious}" class="icon-button rtl-reversible" on-tap="onPrevTap_" disabled="[[!hasMoreResults]]">
+          <button is="paper-icon-button-light" id="prev-button" title="$i18n{rangePrevious}" class="icon-button rtl-reversible" on-click="onPrevTap_" disabled="[[!hasMoreResults]]">
             <iron-icon icon="history:chevron-left"></iron-icon>
           </button>
-          <button is="paper-icon-button-light" id="next-button" title="$i18n{rangeNext}" class="icon-button rtl-reversible" on-tap="onNextTap_" disabled="[[isToday_(groupedOffset)]]">
+          <button is="paper-icon-button-light" id="next-button" title="$i18n{rangeNext}" class="icon-button rtl-reversible" on-click="onNextTap_" disabled="[[isToday_(groupedOffset)]]">
             <iron-icon icon="cr:chevron-right"></iron-icon>
           </button>
         </div>
@@ -3281,7 +3281,7 @@
 }
 
 </style>
-    <iron-pages id="content" attr-for-selected="id" selected="[[selectedPage_]]" fallback-selection="infinite-list">
+    <iron-pages id="content" attr-for-selected="id" selected="[[selectedPage_]]">
       <history-list id="infinite-list" querying="[[queryState.querying]]" searched-term="[[queryResult.info.term]]">
       </history-list>
       <template is="dom-if" if="[[grouped]]">
@@ -3556,7 +3556,7 @@
 
 </style>
 
-    <iron-selector id="menu" selected="{{selectedPage}}" selectable=".page-item" attr-for-selected="path" fallback-selection="history" on-iron-activate="onSelectorActivate_">
+    <iron-selector id="menu" selected="{{selectedPage}}" selectable=".page-item" attr-for-selected="path" on-iron-activate="onSelectorActivate_">
       <a href="/" class="page-item" path="history" on-click="onItemClick_">
         $i18n{historyMenuItem}
         <paper-ripple></paper-ripple>
@@ -3722,9 +3722,9 @@
 }
 
 </style>
-    <history-query-manager query-state="{{queryState_}}" query-result="[[queryResult_]]">
+    <history-query-manager query-state="{{queryState_}}" query-result="[[queryResult_]]" router="[[$$('#router')]]">
     </history-query-manager>
-    <history-router selected-page="{{selectedPage_}}" query-state="[[queryState_]]">
+    <history-router id="router" selected-page="{{selectedPage_}}" grouped="[[grouped_]]" query-state="[[queryState_]]">
     </history-router>
     <history-toolbar id="toolbar" grouped-offset="[[queryState_.groupedOffset]]" grouped-range="[[queryState_.range]]" has-drawer="[[hasDrawer_]]" has-more-results="[[!queryResult_.info.finished]]" is-grouped-mode="[[grouped_]]" query-info="[[queryResult_.info]]" querying="[[queryState_.querying]]" search-term="[[queryState_.searchTerm]]" show-menu-promo="[[showMenuPromo_]]" show-sync-notice="[[showSyncNotice_(hasSyncedResults, selectedPage_)]]" spinner-active="[[shouldShowSpinner_(queryState_.querying,
                                              queryState_.incremental,
diff --git a/chrome/browser/resources/md_history/compiled_resources2.gyp b/chrome/browser/resources/md_history/compiled_resources2.gyp
index 980f8ad..964f55f 100644
--- a/chrome/browser/resources/md_history/compiled_resources2.gyp
+++ b/chrome/browser/resources/md_history/compiled_resources2.gyp
@@ -128,6 +128,7 @@
       'dependencies': [
         'browser_service',
         'externs',
+        'router',
       ],
       'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
     },
diff --git a/chrome/browser/resources/md_history/grouped_list.js b/chrome/browser/resources/md_history/grouped_list.js
index f1664bf8..44b3b94 100644
--- a/chrome/browser/resources/md_history/grouped_list.js
+++ b/chrome/browser/resources/md_history/grouped_list.js
@@ -40,7 +40,7 @@
     range: Number,
   },
 
-  observers: ['updateGroupedHistoryData_(range, historyData)'],
+  observers: ['updateGroupedHistoryData_(historyData)'],
 
   /**
    * @param {!Array<!HistoryEntry>} results
diff --git a/chrome/browser/resources/md_history/history_list_behavior.js b/chrome/browser/resources/md_history/history_list_behavior.js
index 3f871c40..7de36b9 100644
--- a/chrome/browser/resources/md_history/history_list_behavior.js
+++ b/chrome/browser/resources/md_history/history_list_behavior.js
@@ -30,6 +30,12 @@
 var HistoryListBehavior = {
   properties: {
     /**
+     * Allows data to be imported into the list as soon as it is upgraded.
+     * @type {!Array<!HistoryEntry>}
+     */
+    initialData: Array,
+
+    /**
      * Polymer paths to the history items contained in this list.
      * @type {!Set<string>} selectedPaths
      */
@@ -47,6 +53,12 @@
     'history-checkbox-select': 'itemSelected_',
   },
 
+  /** @override */
+  attached: function() {
+    if (this.initialData)
+      this.addNewResults(this.initialData, false, false);
+  },
+
   /**
    * @param {!Array<!HistoryEntry>} results
    * @param {boolean} incremental True if the results are from an incremental
diff --git a/chrome/browser/resources/md_history/history_toolbar.html b/chrome/browser/resources/md_history/history_toolbar.html
index b3cbe2f4..747d91a 100644
--- a/chrome/browser/resources/md_history/history_toolbar.html
+++ b/chrome/browser/resources/md_history/history_toolbar.html
@@ -234,7 +234,7 @@
               id="today-button"
               class="icon-button"
               title="$i18n{rangeToday}"
-              on-tap="onTodayTap_"
+              on-click="onTodayTap_"
               disabled="[[isToday_(groupedOffset)]]">
             <iron-icon icon="history:today"></iron-icon>
           </button>
@@ -242,7 +242,7 @@
               id="prev-button"
               title="$i18n{rangePrevious}"
               class="icon-button rtl-reversible"
-              on-tap="onPrevTap_"
+              on-click="onPrevTap_"
               disabled="[[!hasMoreResults]]">
             <iron-icon icon="history:chevron-left"></iron-icon>
           </button>
@@ -250,7 +250,7 @@
               id="next-button"
               title="$i18n{rangeNext}"
               class="icon-button rtl-reversible"
-              on-tap="onNextTap_"
+              on-click="onNextTap_"
               disabled="[[isToday_(groupedOffset)]]">
             <iron-icon icon="cr:chevron-right"></iron-icon>
           </button>
diff --git a/chrome/browser/resources/md_history/history_toolbar.js b/chrome/browser/resources/md_history/history_toolbar.js
index 27626da..29d065ed 100644
--- a/chrome/browser/resources/md_history/history_toolbar.js
+++ b/chrome/browser/resources/md_history/history_toolbar.js
@@ -208,6 +208,9 @@
   /** @private */
   getHistoryInterval_: function() {
     var info = this.queryInfo;
+    if (!info)
+      return;
+
     if (this.groupedRange == HistoryRange.WEEK)
       return info.queryInterval;
 
diff --git a/chrome/browser/resources/md_history/lazy_load.crisper.js b/chrome/browser/resources/md_history/lazy_load.crisper.js
index 345ad19..b446d02b 100644
--- a/chrome/browser/resources/md_history/lazy_load.crisper.js
+++ b/chrome/browser/resources/md_history/lazy_load.crisper.js
@@ -2,7 +2,7 @@
 // Copyright 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-var HistoryDomain;var HistoryGroup;Polymer({is:"history-grouped-list",behaviors:[HistoryListBehavior],properties:{searchedTerm:{type:String,value:""},groupedHistoryData_:Array,historyData:Array,queryInterval:String,range:Number},observers:["updateGroupedHistoryData_(range, historyData)"],addNewResults:function(results,incremental,finished){this.historyData=results},createHistoryDomains_:function(visits){var domainIndexes={};var domains=[];for(var i=0,visit;visit=visits[i];i++){var domain=visit.domain;if(domainIndexes[domain]==undefined){domainIndexes[domain]=domains.length;domains.push({domain:domain,visits:[],expanded:false,rendered:false})}domains[domainIndexes[domain]].visits.push(visit)}var sortByVisits=function(a,b){return b.visits.length-a.visits.length};domains.sort(sortByVisits);return domains},updateGroupedHistoryData_:function(){if(this.historyData.length==0){this.groupedHistoryData_=[];return}if(this.range==HistoryRange.WEEK){var days=[];var currentDayVisits=[this.historyData[0]];var pushCurrentDay=function(){days.push({title:this.searchedTerm?currentDayVisits[0].dateShort:currentDayVisits[0].dateRelativeDay,domains:this.createHistoryDomains_(currentDayVisits)})}.bind(this);var visitsSameDay=function(a,b){if(this.searchedTerm)return a.dateShort==b.dateShort;return a.dateRelativeDay==b.dateRelativeDay}.bind(this);for(var i=1;i<this.historyData.length;i++){var visit=this.historyData[i];if(!visitsSameDay(visit,currentDayVisits[0])){pushCurrentDay();currentDayVisits=[]}currentDayVisits.push(visit)}pushCurrentDay();this.groupedHistoryData_=days}else if(this.range==HistoryRange.MONTH){this.groupedHistoryData_=[{title:this.queryInterval,domains:this.createHistoryDomains_(this.historyData)}]}},toggleDomainExpanded_:function(e){var collapse=e.currentTarget.parentNode.querySelector("iron-collapse");e.model.set("domain.rendered",true);setTimeout(function(){collapse.toggle()},0)},needsTimeGap_:function(groupIndex,domainIndex,itemIndex){var visits=this.groupedHistoryData_[groupIndex].domains[domainIndex].visits;return md_history.HistoryItem.needsTimeGap(visits,itemIndex,this.searchedTerm)},pathForItem_:function(groupIndex,domainIndex,itemIndex){return["groupedHistoryData_",groupIndex,"domains",domainIndex,"visits",itemIndex].join(".")},getWebsiteIconStyle_:function(domain){return"background-image: "+cr.icon.getFavicon(domain.visits[0].url)},getDropdownIcon_:function(expanded){return expanded?"cr:expand-less":"cr:expand-more"}});
+var HistoryDomain;var HistoryGroup;Polymer({is:"history-grouped-list",behaviors:[HistoryListBehavior],properties:{searchedTerm:{type:String,value:""},groupedHistoryData_:Array,historyData:Array,queryInterval:String,range:Number},observers:["updateGroupedHistoryData_(historyData)"],addNewResults:function(results,incremental,finished){this.historyData=results},createHistoryDomains_:function(visits){var domainIndexes={};var domains=[];for(var i=0,visit;visit=visits[i];i++){var domain=visit.domain;if(domainIndexes[domain]==undefined){domainIndexes[domain]=domains.length;domains.push({domain:domain,visits:[],expanded:false,rendered:false})}domains[domainIndexes[domain]].visits.push(visit)}var sortByVisits=function(a,b){return b.visits.length-a.visits.length};domains.sort(sortByVisits);return domains},updateGroupedHistoryData_:function(){if(this.historyData.length==0){this.groupedHistoryData_=[];return}if(this.range==HistoryRange.WEEK){var days=[];var currentDayVisits=[this.historyData[0]];var pushCurrentDay=function(){days.push({title:this.searchedTerm?currentDayVisits[0].dateShort:currentDayVisits[0].dateRelativeDay,domains:this.createHistoryDomains_(currentDayVisits)})}.bind(this);var visitsSameDay=function(a,b){if(this.searchedTerm)return a.dateShort==b.dateShort;return a.dateRelativeDay==b.dateRelativeDay}.bind(this);for(var i=1;i<this.historyData.length;i++){var visit=this.historyData[i];if(!visitsSameDay(visit,currentDayVisits[0])){pushCurrentDay();currentDayVisits=[]}currentDayVisits.push(visit)}pushCurrentDay();this.groupedHistoryData_=days}else if(this.range==HistoryRange.MONTH){this.groupedHistoryData_=[{title:this.queryInterval,domains:this.createHistoryDomains_(this.historyData)}]}},toggleDomainExpanded_:function(e){var collapse=e.currentTarget.parentNode.querySelector("iron-collapse");e.model.set("domain.rendered",true);setTimeout(function(){collapse.toggle()},0)},needsTimeGap_:function(groupIndex,domainIndex,itemIndex){var visits=this.groupedHistoryData_[groupIndex].domains[domainIndex].visits;return md_history.HistoryItem.needsTimeGap(visits,itemIndex,this.searchedTerm)},pathForItem_:function(groupIndex,domainIndex,itemIndex){return["groupedHistoryData_",groupIndex,"domains",domainIndex,"visits",itemIndex].join(".")},getWebsiteIconStyle_:function(domain){return"background-image: "+cr.icon.getFavicon(domain.visits[0].url)},getDropdownIcon_:function(expanded){return expanded?"cr:expand-less":"cr:expand-more"}});
 // 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.
diff --git a/chrome/browser/resources/md_history/list_container.html b/chrome/browser/resources/md_history/list_container.html
index 51c2b58..9fd0601 100644
--- a/chrome/browser/resources/md_history/list_container.html
+++ b/chrome/browser/resources/md_history/list_container.html
@@ -30,8 +30,7 @@
     </style>
     <iron-pages id="content"
         attr-for-selected="id"
-        selected="[[selectedPage_]]"
-        fallback-selection="infinite-list">
+        selected="[[selectedPage_]]">
       <history-list id="infinite-list"
           querying="[[queryState.querying]]"
           searched-term="[[queryResult.info.term]]">
diff --git a/chrome/browser/resources/md_history/list_container.js b/chrome/browser/resources/md_history/list_container.js
index 4f309477..94e545e7 100644
--- a/chrome/browser/resources/md_history/list_container.js
+++ b/chrome/browser/resources/md_history/list_container.js
@@ -32,10 +32,6 @@
     actionMenuModel_: Object,
   },
 
-  observers: [
-    'groupedRangeChanged_(queryState.range)',
-  ],
-
   listeners: {
     'open-menu': 'openMenu_',
   },
@@ -58,7 +54,14 @@
     }
 
     var list = /** @type {HistoryListBehavior} */ this.getSelectedList_();
-    list.addNewResults(results, this.queryState.incremental, info.finished);
+    // It is possible for results to arrive for the grouped list before the lazy
+    // load has finished and the <history-grouped-list> element exists. In this
+    // case, add the items to a property on the unresolved element which can be
+    // read when it upgrades and is attached.
+    if (Polymer.isInstance(list))
+      list.addNewResults(results, this.queryState.incremental, info.finished);
+    else
+      list.initialData = results;
   },
 
   historyDeleted: function() {
@@ -114,19 +117,6 @@
   },
 
   /**
-   * @param {HistoryRange} range
-   * @private
-   */
-  groupedRangeChanged_: function(range) {
-    // Reset the results on range change to prevent stale results from being
-    // processed into the incoming range's UI.
-    if (range != HistoryRange.ALL_TIME && this.queryResult.info) {
-      this.set('queryResult.results', []);
-      this.historyResult(this.queryResult.info, []);
-    }
-  },
-
-  /**
    * @param {HistoryQuery} info
    * @param {!Array<HistoryEntry>} results
    * @private
diff --git a/chrome/browser/resources/md_history/query_manager.js b/chrome/browser/resources/md_history/query_manager.js
index a2a93bd7..9fbe1d4 100644
--- a/chrome/browser/resources/md_history/query_manager.js
+++ b/chrome/browser/resources/md_history/query_manager.js
@@ -33,6 +33,9 @@
 
     /** @type {QueryResult} */
     queryResult: Object,
+
+    /** @type {?HistoryRouterElement} */
+    router: Object,
   },
 
   observers: [
@@ -63,14 +66,9 @@
    */
   queryHistory_: function(incremental) {
     var queryState = this.queryState;
-    // Disable querying until the first set of results have been returned. If
-    // there is a search, query immediately to support search query params from
-    // the URL.
-    var noResults = !this.queryResult || this.queryResult.results == null;
-    if (queryState.queryingDisabled ||
-        (!this.queryState.searchTerm && noResults)) {
+
+    if (queryState.queryingDisabled)
       return;
-    }
 
     this.set('queryState.querying', true);
     this.set('queryState.incremental', incremental);
@@ -127,8 +125,11 @@
       needsUpdate = true;
     }
 
-    if (needsUpdate)
+    if (needsUpdate) {
       this.queryHistory_(false);
+      if (this.router)
+        this.router.serializeUrl();
+    }
   },
 
   /**
diff --git a/chrome/browser/resources/md_history/router.js b/chrome/browser/resources/md_history/router.js
index 986d5fb..38c5f05 100644
--- a/chrome/browser/resources/md_history/router.js
+++ b/chrome/browser/resources/md_history/router.js
@@ -8,24 +8,25 @@
   properties: {
     selectedPage: {
       type: String,
-      observer: 'serializePath_',
       notify: true,
-    },
-
-    path_: {
-      type: String,
-      observer: 'pathChanged_',
+      observer: 'selectedPageChanged_'
     },
 
     /** @type {QueryState} */
     queryState: Object,
 
+    grouped: Boolean,
+
+    path_: String,
+
     queryParams_: Object,
   },
 
+  /** @private {boolean} */
+  parsing_: false,
+
   observers: [
-    'queryParamsChanged_(queryParams_.*)',
-    'searchTermChanged_(queryState.searchTerm)',
+    'onUrlChanged_(path_, queryParams_)',
   ],
 
   /** @override */
@@ -37,25 +38,97 @@
     }
   },
 
-  /** @private */
-  serializePath_: function() {
-    var page = this.selectedPage == 'history' ? '' : this.selectedPage;
-    this.path_ = '/' + page;
-  },
+  /**
+   * Write all relevant page state to the URL.
+   */
+  serializeUrl: function() {
+    var path = this.selectedPage;
 
-  /** @private */
-  pathChanged_: function() {
-    var sections = this.path_.substr(1).split('/');
-    this.selectedPage = sections[0] || 'history';
-  },
+    if (path == 'history' && this.queryState.range != HistoryRange.ALL_TIME)
+      path += '/' + this.rangeToString_(this.queryState.range);
 
-  /** @private */
-  queryParamsChanged_: function() {
-    this.fire('change-query', {search: this.queryParams_.q || ''});
-  },
+    if (path == 'history')
+      path = '';
 
-  /** @private */
-  searchTermChanged_: function() {
+    var offsetParam = null;
+    if (this.selectedPage == 'history' && this.queryState.groupedOffset)
+      offsetParam = this.queryState.groupedOffset;
+
+    // Make all modifications at the end of the method so observers can't change
+    // the outcome.
+    this.path_ = '/' + path;
+    this.set('queryParams_.offset', offsetParam);
     this.set('queryParams_.q', this.queryState.searchTerm || null);
   },
+
+  /** @private */
+  selectedPageChanged_: function() {
+    // Update the URL if the page was changed externally, but ignore the update
+    // if it came from parseUrl_().
+    if (!this.parsing_)
+      this.serializeUrl();
+  },
+
+  /** @private */
+  parseUrl_: function() {
+    this.parsing_ = true;
+    var changes = {};
+    var sections = this.path_.substr(1).split('/');
+    var page = sections[0] || 'history';
+
+    if (page == 'history' && this.grouped) {
+      var range = sections.length > 1 ? this.stringToRange_(sections[1]) :
+                                        HistoryRange.ALL_TIME;
+      changes.range = range;
+      changes.offset = Number(this.queryParams_.offset) || 0;
+    }
+
+    changes.search = this.queryParams_.q || '';
+
+    // Must change selectedPage before `change-query`, otherwise the
+    // query-manager will call serializeUrl() with the old page.
+    this.selectedPage = page;
+    this.fire('change-query', changes);
+    this.serializeUrl();
+
+    this.parsing_ = false;
+  },
+
+  /** @private */
+  onUrlChanged_: function() {
+    // Changing the url and query parameters at the same time will cause two
+    // calls to onUrlChanged_. Debounce the actual work so that these two
+    // changes get processed together.
+    this.debounce('parseUrl', this.parseUrl_.bind(this));
+  },
+
+  /**
+   * @param {!HistoryRange} range
+   * @return {string}
+   */
+  rangeToString_: function(range) {
+    switch (range) {
+      case HistoryRange.WEEK:
+        return 'week';
+      case HistoryRange.MONTH:
+        return 'month';
+      default:
+        return '';
+    }
+  },
+
+  /**
+   * @param {string} str
+   * @return {HistoryRange}
+   */
+  stringToRange_: function(str) {
+    switch (str) {
+      case 'week':
+        return HistoryRange.WEEK;
+      case 'month':
+        return HistoryRange.MONTH;
+      default:
+        return HistoryRange.ALL_TIME;
+    }
+  }
 });
diff --git a/chrome/browser/resources/md_history/side_bar.html b/chrome/browser/resources/md_history/side_bar.html
index 4e226d2f..373009e2 100644
--- a/chrome/browser/resources/md_history/side_bar.html
+++ b/chrome/browser/resources/md_history/side_bar.html
@@ -92,7 +92,6 @@
 
     <iron-selector id="menu" selected="{{selectedPage}}"
         selectable=".page-item" attr-for-selected="path"
-        fallback-selection="history"
         on-iron-activate="onSelectorActivate_">
       <a href="/" class="page-item" path="history" on-click="onItemClick_">
         $i18n{historyMenuItem}
diff --git a/chrome/browser/sessions/session_restore_browsertest.cc b/chrome/browser/sessions/session_restore_browsertest.cc
index f749e564..c3a0a36 100644
--- a/chrome/browser/sessions/session_restore_browsertest.cc
+++ b/chrome/browser/sessions/session_restore_browsertest.cc
@@ -52,6 +52,7 @@
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
@@ -946,35 +947,35 @@
 IN_PROC_BROWSER_TEST_F(SessionRestoreTest, RestoreWebUI) {
   const GURL webui_url("chrome://omnibox");
   ui_test_utils::NavigateToURL(browser(), webui_url);
-  const content::WebContents* old_tab =
+  content::WebContents* old_tab =
       browser()->tab_strip_model()->GetActiveWebContents();
   EXPECT_EQ(content::BINDINGS_POLICY_WEB_UI,
-            old_tab->GetRenderViewHost()->GetEnabledBindings());
+            old_tab->GetMainFrame()->GetEnabledBindings());
 
   Browser* new_browser = QuitBrowserAndRestore(browser(), 1);
   ASSERT_EQ(1u, active_browser_list_->size());
-  const content::WebContents* new_tab =
+  content::WebContents* new_tab =
       new_browser->tab_strip_model()->GetActiveWebContents();
   EXPECT_EQ(webui_url, new_tab->GetURL());
   EXPECT_EQ(content::BINDINGS_POLICY_WEB_UI,
-            new_tab->GetRenderViewHost()->GetEnabledBindings());
+            new_tab->GetMainFrame()->GetEnabledBindings());
 }
 
 IN_PROC_BROWSER_TEST_F(SessionRestoreTest, RestoreWebUISettings) {
   const GURL webui_url("chrome://settings");
   ui_test_utils::NavigateToURL(browser(), webui_url);
-  const content::WebContents* old_tab =
+  content::WebContents* old_tab =
       browser()->tab_strip_model()->GetActiveWebContents();
   EXPECT_EQ(content::BINDINGS_POLICY_WEB_UI,
-            old_tab->GetRenderViewHost()->GetEnabledBindings());
+            old_tab->GetMainFrame()->GetEnabledBindings());
 
   Browser* new_browser = QuitBrowserAndRestore(browser(), 1);
   ASSERT_EQ(1u, active_browser_list_->size());
-  const content::WebContents* new_tab =
+  content::WebContents* new_tab =
       new_browser->tab_strip_model()->GetActiveWebContents();
   EXPECT_EQ(webui_url, new_tab->GetURL());
   EXPECT_EQ(content::BINDINGS_POLICY_WEB_UI,
-            new_tab->GetRenderViewHost()->GetEnabledBindings());
+            new_tab->GetMainFrame()->GetEnabledBindings());
 }
 
 IN_PROC_BROWSER_TEST_F(SessionRestoreTest, RestoresForwardAndBackwardNavs) {
diff --git a/chrome/browser/ui/browser_navigator_browsertest.cc b/chrome/browser/ui/browser_navigator_browsertest.cc
index 5424d41..37c4be3 100644
--- a/chrome/browser/ui/browser_navigator_browsertest.cc
+++ b/chrome/browser/ui/browser_navigator_browsertest.cc
@@ -1486,7 +1486,8 @@
   ASSERT_TRUE(popup);
   EXPECT_EQ(2, browser()->tab_strip_model()->count());
   content::RenderViewHost* webui_rvh = popup->GetRenderViewHost();
-  EXPECT_EQ(content::BINDINGS_POLICY_WEB_UI, webui_rvh->GetEnabledBindings());
+  content::RenderFrameHost* webui_rfh = popup->GetMainFrame();
+  EXPECT_EQ(content::BINDINGS_POLICY_WEB_UI, webui_rfh->GetEnabledBindings());
 
   // Navigate to another page in the popup.
   GURL nonwebui_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
@@ -1500,7 +1501,8 @@
   back_load_observer.Wait();
   EXPECT_EQ(webui_rvh, popup->GetRenderViewHost());
   EXPECT_TRUE(webui_rvh->IsRenderViewLive());
-  EXPECT_EQ(content::BINDINGS_POLICY_WEB_UI, webui_rvh->GetEnabledBindings());
+  EXPECT_EQ(content::BINDINGS_POLICY_WEB_UI,
+            webui_rvh->GetMainFrame()->GetEnabledBindings());
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/webui/md_history_ui.cc b/chrome/browser/ui/webui/md_history_ui.cc
index 8ddf7b9..4b5a577 100644
--- a/chrome/browser/ui/webui/md_history_ui.cc
+++ b/chrome/browser/ui/webui/md_history_ui.cc
@@ -243,8 +243,6 @@
 // static
 bool MdHistoryUI::IsEnabled(Profile* profile) {
   return base::FeatureList::IsEnabled(features::kMaterialDesignHistory) &&
-         !base::CommandLine::ForCurrentProcess()->HasSwitch(
-             switches::kHistoryEnableGroupByDomain) &&
          !profile->IsSupervised();
 }
 
diff --git a/chrome/browser/ui/webui/mojo_web_ui_controller.cc b/chrome/browser/ui/webui/mojo_web_ui_controller.cc
index 089b2e5..fc51c20e 100644
--- a/chrome/browser/ui/webui/mojo_web_ui_controller.cc
+++ b/chrome/browser/ui/webui/mojo_web_ui_controller.cc
@@ -19,6 +19,5 @@
 
 void MojoWebUIControllerBase::RenderFrameCreated(
     content::RenderFrameHost* render_frame_host) {
-  render_frame_host->GetRenderViewHost()->AllowBindings(
-      content::BINDINGS_POLICY_WEB_UI);
+  render_frame_host->AllowBindings(content::BINDINGS_POLICY_WEB_UI);
 }
diff --git a/chrome/renderer/chrome_render_view_observer.cc b/chrome/renderer/chrome_render_view_observer.cc
index f8f3574..5692c0b9 100644
--- a/chrome/renderer/chrome_render_view_observer.cc
+++ b/chrome/renderer/chrome_render_view_observer.cc
@@ -165,7 +165,9 @@
 void ChromeRenderViewObserver::DidCommitProvisionalLoad(
     blink::WebLocalFrame* frame,
     bool is_new_navigation) {
-  if ((render_view()->GetEnabledBindings() & content::BINDINGS_POLICY_WEB_UI) &&
+  auto* render_frame = content::RenderFrame::FromWebFrame(frame);
+  if (render_frame->IsMainFrame() &&
+      (render_frame->GetEnabledBindings() & content::BINDINGS_POLICY_WEB_UI) &&
       !webui_javascript_.empty()) {
     for (const auto& script : webui_javascript_)
       render_view()->GetMainRenderFrame()->ExecuteJavaScript(script);
diff --git a/chrome/test/data/chromeos/file_manager/share_dialog_mock/main.js b/chrome/test/data/chromeos/file_manager/share_dialog_mock/main.js
index 4057547..2030a576 100644
--- a/chrome/test/data/chromeos/file_manager/share_dialog_mock/main.js
+++ b/chrome/test/data/chromeos/file_manager/share_dialog_mock/main.js
@@ -6,7 +6,7 @@
 var shareDialog = {};
 
 /**
- * Origin of Files.app.
+ * Origin of the Files app.
  * @type {string}
  * @const
  */
@@ -28,8 +28,8 @@
 shareDialog.TARGET_HEIGHT = 250;
 
 /**
- * Target window of Files.app. Used to communicate over messages. Filled out
- * once the first message from the embedder arrives.
+ * Target window of the Files app. Used to communicate over messages. Filled
+ * out once the first message from the embedder arrives.
  * @type {Window}
  */
 shareDialog.embedderTarget = null;
diff --git a/chrome/test/data/webui/md_history/history_routing_test.js b/chrome/test/data/webui/md_history/history_routing_test.js
index b8f58675..2ebae99 100644
--- a/chrome/test/data/webui/md_history/history_routing_test.js
+++ b/chrome/test/data/webui/md_history/history_routing_test.js
@@ -12,13 +12,16 @@
       function navigateTo(route) {
         window.history.replaceState({}, '', route);
         window.dispatchEvent(new CustomEvent('location-changed'));
+        // Update from the URL synchronously.
+        app.$$('history-router').flushDebouncer('parseUrl');
       }
 
       setup(function() {
-        assertEquals('chrome://history/', window.location.href);
         app = replaceApp();
+        assertEquals('chrome://history/', window.location.href);
         sidebar = app.$['content-side-bar']
         toolbar = app.$['toolbar'];
+        return PolymerTest.flushTasks();
       });
 
       test('changing route changes active view', function() {
@@ -78,9 +81,32 @@
         assertEquals('chrome://history/?q=' + searchTerm, window.location.href);
       });
 
-      teardown(function() {
-        // Reset back to initial navigation state.
+      test('changing route changes grouped range and offset', function() {
+        app.grouped_ = true;
+        navigateTo('/history/week?offset=1');
+        assertEquals(HistoryRange.WEEK, app.queryState_.range);
+        assertEquals(1, app.queryState_.groupedOffset);
+
+        navigateTo('/history/month');
+        assertEquals(HistoryRange.MONTH, app.queryState_.range);
+        assertEquals(0, app.queryState_.groupedOffset);
+
         navigateTo('/');
+        assertEquals(HistoryRange.ALL_TIME, app.queryState_.range);
+      });
+
+      test('route updates from grouped range and offset', function() {
+        app.grouped_ = true;
+
+        app.fire('change-query', {range: HistoryRange.WEEK});
+        assertEquals('chrome://history/history/week', window.location.href);
+
+        app.fire('change-query', {range: HistoryRange.MONTH, offset: 5});
+        assertEquals(
+            'chrome://history/history/month?offset=5', window.location.href);
+
+        app.fire('change-query', {range: HistoryRange.ALL_TIME});
+        assertEquals('chrome://history/', window.location.href);
       });
     });
   }
@@ -105,6 +131,8 @@
       test('search initiated on load', function(done) {
         var verifyFunction = function(info) {
           assertEquals(expectedQuery, info[0]);
+          assertEquals(5, info[1]);
+          assertEquals(HistoryRange.WEEK, info[2]);
           PolymerTest.flushTasks().then(function() {
             assertEquals(
                 expectedQuery,
diff --git a/chrome/test/data/webui/md_history/md_history_browsertest.js b/chrome/test/data/webui/md_history/md_history_browsertest.js
index d0a24cc..a3d9a69 100644
--- a/chrome/test/data/webui/md_history/md_history_browsertest.js
+++ b/chrome/test/data/webui/md_history/md_history_browsertest.js
@@ -173,7 +173,12 @@
 MaterialHistoryRoutingWithQueryParamTest.prototype = {
   __proto__: MaterialHistoryRoutingTest.prototype,
 
-  browsePreload: 'chrome://history?q=query',
+  browsePreload: 'chrome://history/history/week?q=query&offset=5',
+
+  commandLineSwitches:
+      MaterialHistoryBrowserTest.prototype.commandLineSwitches.concat([
+        {switchName: 'enable-grouped-history'}
+      ]),
 
   /** @override */
   setUp: function() {
diff --git a/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc b/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc
index 76f1651..06eb3e62c 100644
--- a/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc
+++ b/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc
@@ -34,7 +34,6 @@
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "grit/components_strings.h"
@@ -144,9 +143,7 @@
     if (expected_main_view_request) {
       content::RenderFrameHost* render_frame_host =
           navigation_handle->GetRenderFrameHost();
-      content::RenderViewHost* render_view_host =
-          render_frame_host->GetRenderViewHost();
-      CHECK_EQ(0, render_view_host->GetEnabledBindings());
+      CHECK_EQ(0, render_frame_host->GetEnabledBindings());
 
       // Add mojo service for JavaScript functionality. This is the receiving
       // end of this particular service.
diff --git a/content/browser/frame_host/interstitial_page_impl.cc b/content/browser/frame_host/interstitial_page_impl.cc
index 66aab6fe..c3b0abb 100644
--- a/content/browser/frame_host/interstitial_page_impl.cc
+++ b/content/browser/frame_host/interstitial_page_impl.cc
@@ -591,7 +591,8 @@
   RenderWidgetHostViewBase* view =
       wcv->CreateViewForWidget(render_view_host_->GetWidget(), false);
   RenderWidgetHostImpl::From(render_view_host_->GetWidget())->SetView(view);
-  render_view_host_->AllowBindings(BINDINGS_POLICY_DOM_AUTOMATION);
+  render_view_host_->GetMainFrame()->AllowBindings(
+      BINDINGS_POLICY_DOM_AUTOMATION);
 
   render_view_host_->CreateRenderView(MSG_ROUTING_NONE,
                                       MSG_ROUTING_NONE,
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
index e9952e5..d361fef 100644
--- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -551,7 +551,7 @@
                    std::string(kChromeUIGpuHost));
   EXPECT_TRUE(NavigateToURL(shell(), web_ui_page));
   EXPECT_EQ(BINDINGS_POLICY_WEB_UI,
-      shell()->web_contents()->GetRenderViewHost()->GetEnabledBindings());
+            shell()->web_contents()->GetMainFrame()->GetEnabledBindings());
 
   ShellAddedObserver observer;
   std::string page_url = embedded_test_server()->GetURL(
diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc
index 5f3a55ce..32c00d1 100644
--- a/content/browser/frame_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -1049,7 +1049,7 @@
       kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
   int entry_id = controller.GetPendingEntry()->GetUniqueID();
   // Pretend it has bindings so we can tell if we incorrectly copy it.
-  main_test_rfh()->GetRenderViewHost()->AllowBindings(2);
+  main_test_rfh()->AllowBindings(2);
   main_test_rfh()->PrepareForCommit();
   main_test_rfh()->SendNavigate(entry_id, true, kExistingURL1);
   EXPECT_EQ(1U, navigation_entry_committed_counter_);
@@ -1342,7 +1342,7 @@
   int entry_id = controller.GetPendingEntry()->GetUniqueID();
   orig_rfh->PrepareForCommit();
   TestRenderFrameHost* new_rfh = contents()->GetPendingMainFrame();
-  new_rfh->GetRenderViewHost()->AllowBindings(1);
+  new_rfh->AllowBindings(1);
   new_rfh->SendNavigate(entry_id, true, url2);
 
   // The second load should be committed, and bindings should be remembered.
diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc
index 1927b1c..ce72351 100644
--- a/content/browser/frame_host/navigator_impl.cc
+++ b/content/browser/frame_host/navigator_impl.cc
@@ -111,8 +111,7 @@
 void NavigatorImpl::CheckWebUIRendererDoesNotDisplayNormalURL(
     RenderFrameHostImpl* render_frame_host,
     const GURL& url) {
-  int enabled_bindings =
-      render_frame_host->render_view_host()->GetEnabledBindings();
+  int enabled_bindings = render_frame_host->GetEnabledBindings();
   bool is_allowed_in_web_ui_renderer =
       WebUIControllerFactoryRegistry::GetInstance()->IsURLAcceptableForWebUI(
           render_frame_host->frame_tree_node()
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 01653b00..0f36645 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -82,6 +82,7 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/stream_handle.h"
 #include "content/public/browser/user_metrics.h"
+#include "content/public/common/bindings_policy.h"
 #include "content/public/common/browser_side_navigation_policy.h"
 #include "content/public/common/content_constants.h"
 #include "content/public/common/content_features.h"
@@ -360,6 +361,10 @@
     // FrameTreeNode has changed its current RenderFrameHost.
     parent_ = frame_tree_node_->parent()->current_frame_host();
 
+    // All frames in a page are expected to have the same bindings.
+    if (parent_->GetEnabledBindings())
+      enabled_bindings_ = parent_->GetEnabledBindings();
+
     // New child frames should inherit the nav_entry_id of their parent.
     set_nav_entry_id(
         frame_tree_node_->parent()->current_frame_host()->nav_entry_id());
@@ -961,6 +966,12 @@
 
   if (created && render_widget_host_)
     render_widget_host_->InitForFrame();
+
+  if (enabled_bindings_ && created) {
+    if (!frame_bindings_control_)
+      GetRemoteAssociatedInterfaces()->GetInterface(&frame_bindings_control_);
+    frame_bindings_control_->AllowBindings(enabled_bindings_);
+  }
 }
 
 void RenderFrameHostImpl::Init() {
@@ -1026,9 +1037,9 @@
   if (!is_active() || frame_tree_node_->current_frame_host() != this)
     return;
 
-  frame_tree_->AddFrame(frame_tree_node_, GetProcess()->GetID(), new_routing_id,
-                        scope, frame_name, frame_unique_name, sandbox_flags,
-                        frame_owner_properties);
+  frame_tree_->AddFrame(
+      frame_tree_node_, GetProcess()->GetID(), new_routing_id, scope,
+      frame_name, frame_unique_name, sandbox_flags, frame_owner_properties);
 }
 
 void RenderFrameHostImpl::OnCreateNewWindow(
@@ -1350,10 +1361,6 @@
   return GlobalFrameRoutingId(GetProcess()->GetID(), GetRoutingID());
 }
 
-int RenderFrameHostImpl::GetEnabledBindings() {
-  return render_view_host_->GetEnabledBindings();
-}
-
 void RenderFrameHostImpl::SetNavigationHandle(
     std::unique_ptr<NavigationHandleImpl> navigation_handle) {
   navigation_handle_ = std::move(navigation_handle);
@@ -1738,6 +1745,48 @@
   Send(new FrameMsg_FocusedFormFieldDataRequest(GetRoutingID(), request_id));
 }
 
+void RenderFrameHostImpl::AllowBindings(int bindings_flags) {
+  // Never grant any bindings to browser plugin guests.
+  if (GetProcess()->IsForGuestsOnly()) {
+    NOTREACHED() << "Never grant bindings to a guest process.";
+    return;
+  }
+
+  // Ensure we aren't granting WebUI bindings to a process that has already
+  // been used for non-privileged views.
+  if (bindings_flags & BINDINGS_POLICY_WEB_UI &&
+      GetProcess()->HasConnection() &&
+      !ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
+          GetProcess()->GetID())) {
+    // This process has no bindings yet. Make sure it does not have more
+    // than this single active view.
+    // --single-process only has one renderer.
+    if (GetProcess()->GetActiveViewCount() > 1 &&
+        !base::CommandLine::ForCurrentProcess()->HasSwitch(
+            switches::kSingleProcess))
+      return;
+  }
+
+  if (bindings_flags & BINDINGS_POLICY_WEB_UI) {
+    ChildProcessSecurityPolicyImpl::GetInstance()->GrantWebUIBindings(
+        GetProcess()->GetID());
+  }
+
+  enabled_bindings_ |= bindings_flags;
+  if (GetParent())
+    DCHECK_EQ(GetParent()->GetEnabledBindings(), GetEnabledBindings());
+
+  if (render_frame_created_) {
+    if (!frame_bindings_control_)
+      GetRemoteAssociatedInterfaces()->GetInterface(&frame_bindings_control_);
+    frame_bindings_control_->AllowBindings(enabled_bindings_);
+  }
+}
+
+int RenderFrameHostImpl::GetEnabledBindings() const {
+  return enabled_bindings_;
+}
+
 void RenderFrameHostImpl::OnFocusedFormFieldDataResponse(
     int request_id,
     const FormFieldData& field_data) {
@@ -2742,6 +2791,7 @@
 
   frame_.reset();
   frame_host_binding_.Close();
+  frame_bindings_control_.reset();
 
   // Disconnect with ImageDownloader Mojo service in RenderFrame.
   mojo_image_downloader_.reset();
@@ -2800,11 +2850,10 @@
   // Either grant or check the RenderViewHost with/for proper bindings.
   if (pending_web_ui_ && !render_view_host_->GetProcess()->IsForGuestsOnly()) {
     // If a WebUI was created for the URL and the RenderView is not in a guest
-    // process, then enable missing bindings with the RenderViewHost.
+    // process, then enable missing bindings.
     int new_bindings = pending_web_ui_->GetBindings();
-    if ((render_view_host_->GetEnabledBindings() & new_bindings) !=
-        new_bindings) {
-      render_view_host_->AllowBindings(new_bindings);
+    if ((GetEnabledBindings() & new_bindings) != new_bindings) {
+      AllowBindings(new_bindings);
     }
   } else if (render_view_host_->is_active()) {
     // If the ongoing navigation is not to a WebUI or the RenderView is in a
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 86c7d56..5f003ad 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -177,6 +177,8 @@
       const TextSurroundingSelectionCallback& callback,
       int max_length) override;
   void RequestFocusedFormFieldData(FormFieldDataCallback& callback) override;
+  void AllowBindings(int binding_flags) override;
+  int GetEnabledBindings() const override;
 
   // mojom::FrameHost
   void GetInterfaceProvider(
@@ -328,11 +330,6 @@
     render_frame_proxy_host_ = proxy;
   }
 
-  // Returns a bitwise OR of bindings types that have been enabled for this
-  // RenderFrameHostImpl's RenderView. See BindingsPolicy for details.
-  // TODO(creis): Make bindings frame-specific, to support cases like <webview>.
-  int GetEnabledBindings();
-
   // The unique ID of the latest NavigationEntry that this RenderFrameHost is
   // showing. This may change even when this frame hasn't committed a page,
   // such as for a new subframe navigation in a different frame.
@@ -1112,6 +1109,7 @@
 
   mojo::Binding<mojom::FrameHost> frame_host_binding_;
   mojom::FramePtr frame_;
+  mojom::FrameBindingsControlAssociatedPtr frame_bindings_control_;
 
   // If this is true then this object was created in response to a renderer
   // initiated request. Init() will be called, and until then navigation
@@ -1139,6 +1137,10 @@
   std::unique_ptr<AssociatedInterfaceProviderImpl>
       remote_associated_interfaces_;
 
+  // A bitwise OR of bindings types that have been enabled for this RenderFrame.
+  // See BindingsPolicy for details.
+  int enabled_bindings_ = 0;
+
   // NOTE: This must be the last member.
   base::WeakPtrFactory<RenderFrameHostImpl> weak_ptr_factory_;
 
diff --git a/content/browser/frame_host/render_frame_host_manager_browsertest.cc b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
index 4036409..943c616 100644
--- a/content/browser/frame_host/render_frame_host_manager_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
@@ -1830,8 +1830,6 @@
   RenderViewHost* initial_rvh = new_web_contents->
       GetRenderManagerForTesting()->GetSwappedOutRenderViewHost(site_instance1);
   ASSERT_TRUE(initial_rvh);
-  // The following condition is what was causing the bug.
-  EXPECT_EQ(0, initial_rvh->GetEnabledBindings());
 
   // Navigate to url1 and check bindings.
   NavigateToURL(new_shell, url1);
@@ -1839,7 +1837,7 @@
   // |initial_rvh| did not have a chance to be used.
   EXPECT_EQ(new_web_contents->GetSiteInstance(), site_instance1);
   EXPECT_EQ(BINDINGS_POLICY_WEB_UI,
-      new_web_contents->GetRenderViewHost()->GetEnabledBindings());
+            new_web_contents->GetMainFrame()->GetEnabledBindings());
 }
 
 // crbug.com/424526
diff --git a/content/browser/frame_host/render_frame_host_manager_unittest.cc b/content/browser/frame_host/render_frame_host_manager_unittest.cc
index 5eab849..9391ad1 100644
--- a/content/browser/frame_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_unittest.cc
@@ -504,7 +504,8 @@
   // Navigate our first tab to the chrome url and then to the destination,
   // ensuring we grant bindings to the chrome URL.
   NavigateActiveAndCommit(kChromeUrl);
-  EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
+  EXPECT_TRUE(main_rfh()->GetEnabledBindings() &
+              BINDINGS_POLICY_WEB_UI);
   NavigateActiveAndCommit(kDestUrl);
 
   EXPECT_FALSE(contents()->GetPendingMainFrame());
@@ -1033,8 +1034,7 @@
 
   // Commit.
   manager->DidNavigateFrame(host, true);
-  EXPECT_TRUE(
-      host->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
+  EXPECT_TRUE(host->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
 }
 
 // Tests that we can open a WebUI link in a new tab from a WebUI page and still
@@ -1068,15 +1068,13 @@
   // It should already have bindings.
   EXPECT_EQ(host1, GetPendingFrameHost(manager1));
   EXPECT_NE(host1, manager1->current_frame_host());
-  EXPECT_TRUE(
-      host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
+  EXPECT_TRUE(host1->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
 
   // Commit and ensure we still have bindings.
   manager1->DidNavigateFrame(host1, true);
   SiteInstance* webui_instance = host1->GetSiteInstance();
   EXPECT_EQ(host1, manager1->current_frame_host());
-  EXPECT_TRUE(
-      host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
+  EXPECT_TRUE(host1->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
 
   // Now simulate clicking a link that opens in a new tab.
   std::unique_ptr<TestWebContents> web_contents2(
@@ -1101,8 +1099,7 @@
   EXPECT_EQ(host2, manager2->current_frame_host());
   EXPECT_TRUE(manager2->GetNavigatingWebUI());
   EXPECT_FALSE(host2->web_ui());
-  EXPECT_TRUE(
-      host2->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
+  EXPECT_TRUE(host2->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
 
   manager2->DidNavigateFrame(host2, true);
 }
@@ -1557,7 +1554,8 @@
 
   // Ensure the RVH has WebUI bindings.
   TestRenderViewHost* rvh1 = test_rvh();
-  EXPECT_TRUE(rvh1->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
+  EXPECT_TRUE(rvh1->GetMainFrame()->GetEnabledBindings() &
+              BINDINGS_POLICY_WEB_UI);
 
   // Create a new tab and simulate it being the opener for the main
   // tab.  It should be in the same SiteInstance.
@@ -1583,7 +1581,8 @@
   EXPECT_FALSE(opener1_rvh->is_active());
 
   // Ensure the new RVH has WebUI bindings.
-  EXPECT_TRUE(rvh2->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
+  EXPECT_TRUE(rvh2->GetMainFrame()->GetEnabledBindings() &
+              BINDINGS_POLICY_WEB_UI);
 }
 
 // Test that we reuse the same guest SiteInstance if we navigate across sites.
@@ -2143,8 +2142,7 @@
   RenderFrameHostManager* main_rfhm = contents()->GetRenderManagerForTesting();
   RenderFrameHostImpl* webui_rfh = NavigateToEntry(main_rfhm, webui_entry);
   EXPECT_EQ(webui_rfh, GetPendingFrameHost(main_rfhm));
-  EXPECT_TRUE(webui_rfh->render_view_host()->GetEnabledBindings() &
-              BINDINGS_POLICY_WEB_UI);
+  EXPECT_TRUE(webui_rfh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
 
   // Before it commits, do a cross-process navigation in a subframe.  This
   // should not grant WebUI bindings to the subframe's RVH.
@@ -2154,8 +2152,7 @@
       base::string16() /* title */, ui::PAGE_TRANSITION_LINK,
       false /* is_renderer_init */);
   RenderFrameHostImpl* bar_rfh = NavigateToEntry(subframe_rfhm, subframe_entry);
-  EXPECT_FALSE(bar_rfh->render_view_host()->GetEnabledBindings() &
-               BINDINGS_POLICY_WEB_UI);
+  EXPECT_FALSE(bar_rfh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
 }
 
 // Test that opener proxies are created properly with a cycle on the opener
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index 0ada79d..c637c02 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -200,7 +200,6 @@
       frames_ref_count_(0),
       delegate_(delegate),
       instance_(static_cast<SiteInstanceImpl*>(instance)),
-      enabled_bindings_(0),
       is_active_(!swapped_out),
       is_swapped_out_(swapped_out),
       main_frame_routing_id_(main_frame_routing_id),
@@ -318,11 +317,6 @@
 
   GetProcess()->GetRendererInterface()->CreateView(std::move(params));
 
-  // If it's enabled, tell the renderer to set up the Javascript bindings for
-  // sending messages back to the browser.
-  if (GetProcess()->IsForGuestsOnly())
-    DCHECK_EQ(0, enabled_bindings_);
-  Send(new ViewMsg_AllowBindings(GetRoutingID(), enabled_bindings_));
   // Let our delegate know that we created a RenderView.
   delegate_->RenderViewCreated(this);
 
@@ -646,49 +640,13 @@
   return RenderFrameHost::FromID(GetProcess()->GetID(), main_frame_routing_id_);
 }
 
-void RenderViewHostImpl::AllowBindings(int bindings_flags) {
-  // Never grant any bindings to browser plugin guests.
-  if (GetProcess()->IsForGuestsOnly()) {
-    NOTREACHED() << "Never grant bindings to a guest process.";
-    return;
-  }
-
-  // Ensure we aren't granting WebUI bindings to a process that has already
-  // been used for non-privileged views.
-  if (bindings_flags & BINDINGS_POLICY_WEB_UI &&
-      GetProcess()->HasConnection() &&
-      !ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
-          GetProcess()->GetID())) {
-    // This process has no bindings yet. Make sure it does not have more
-    // than this single active view.
-    // --single-process only has one renderer.
-    if (GetProcess()->GetActiveViewCount() > 1 &&
-        !base::CommandLine::ForCurrentProcess()->HasSwitch(
-            switches::kSingleProcess))
-      return;
-  }
-
-  if (bindings_flags & BINDINGS_POLICY_WEB_UI) {
-    ChildProcessSecurityPolicyImpl::GetInstance()->GrantWebUIBindings(
-        GetProcess()->GetID());
-  }
-
-  enabled_bindings_ |= bindings_flags;
-  if (GetWidget()->renderer_initialized())
-    Send(new ViewMsg_AllowBindings(GetRoutingID(), enabled_bindings_));
-}
-
-int RenderViewHostImpl::GetEnabledBindings() const {
-  return enabled_bindings_;
-}
-
 void RenderViewHostImpl::SetWebUIProperty(const std::string& name,
                                           const std::string& value) {
   // This is a sanity check before telling the renderer to enable the property.
   // It could lie and send the corresponding IPC messages anyway, but we will
   // not act on them if enabled_bindings_ doesn't agree. If we get here without
   // WebUI bindings, kill the renderer process.
-  if (enabled_bindings_ & BINDINGS_POLICY_WEB_UI) {
+  if (GetMainFrame()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI) {
     Send(new ViewMsg_SetWebUIProperty(GetRoutingID(), name, value));
   } else {
     RecordAction(
diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h
index cb30e86..9a167fc 100644
--- a/content/browser/renderer_host/render_view_host_impl.h
+++ b/content/browser/renderer_host/render_view_host_impl.h
@@ -85,7 +85,6 @@
   RenderProcessHost* GetProcess() const override;
   int GetRoutingID() const override;
   RenderFrameHost* GetMainFrame() override;
-  void AllowBindings(int binding_flags) override;
   void DirectoryEnumerationFinished(
       int request_id,
       const std::vector<base::FilePath>& files) override;
@@ -101,7 +100,6 @@
       const gfx::Point& location,
       const blink::WebPluginAction& action) override;
   RenderViewHostDelegate* GetDelegate() const override;
-  int GetEnabledBindings() const override;
   SiteInstanceImpl* GetSiteInstance() const override;
   bool IsRenderViewLive() const override;
   void NotifyMoveOrResizeStarted() override;
@@ -284,10 +282,6 @@
   // over time.
   scoped_refptr<SiteInstanceImpl> instance_;
 
-  // A bitwise OR of bindings types that have been enabled for this RenderView.
-  // See BindingsPolicy for details.
-  int enabled_bindings_;
-
   // Tracks whether this RenderViewHost is in an active state.  False if the
   // main frame is pending swap out, pending deletion, or swapped out, because
   // it is not visible to the user in any of these cases.
diff --git a/content/browser/renderer_host/render_view_host_unittest.cc b/content/browser/renderer_host/render_view_host_unittest.cc
index 57418fc..99b5a65 100644
--- a/content/browser/renderer_host/render_view_host_unittest.cc
+++ b/content/browser/renderer_host/render_view_host_unittest.cc
@@ -88,8 +88,8 @@
   std::unique_ptr<TestWebContents> new_web_contents(
       TestWebContents::Create(browser_context(), rvh()->GetSiteInstance()));
 
-  rvh()->AllowBindings(BINDINGS_POLICY_WEB_UI);
-  EXPECT_FALSE(rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
+  main_rfh()->AllowBindings(BINDINGS_POLICY_WEB_UI);
+  EXPECT_FALSE(main_rfh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
 }
 
 class MockDraggingRenderViewHostDelegateView
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index b1da402..3d61efb 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -243,8 +243,7 @@
 
   NavigateToURL(shell(), foo);
   EXPECT_EQ(base::ASCIIToUTF16("OK"), shell()->web_contents()->GetTitle());
-  EXPECT_EQ(0,
-      shell()->web_contents()->GetRenderViewHost()->GetEnabledBindings());
+  EXPECT_EQ(0, shell()->web_contents()->GetMainFrame()->GetEnabledBindings());
 
   RenderProcessHostWatcher terminated(
       shell()->web_contents(),
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index ebe84c7..0ad160d6 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -1150,8 +1150,7 @@
   EXPECT_EQ(url1, entry1->GetURL());
   EXPECT_EQ(instance1,
             NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance());
-  EXPECT_TRUE(webui_rfh->GetRenderViewHost()->GetEnabledBindings() &
-              BINDINGS_POLICY_WEB_UI);
+  EXPECT_TRUE(webui_rfh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
 
   // Navigate to new site.
   const GURL url2("http://www.google.com");
@@ -1178,8 +1177,7 @@
   EXPECT_EQ(url2, entry2->GetURL());
   EXPECT_EQ(instance2,
             NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance());
-  EXPECT_FALSE(google_rfh->GetRenderViewHost()->GetEnabledBindings() &
-               BINDINGS_POLICY_WEB_UI);
+  EXPECT_FALSE(google_rfh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
 
   // Navigate to third page on same site.
   const GURL url3("http://news.google.com");
@@ -1261,8 +1259,7 @@
   EXPECT_EQ(url1, entry1->GetURL());
   EXPECT_EQ(instance1,
             NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance());
-  EXPECT_TRUE(webui_rfh->GetRenderViewHost()->GetEnabledBindings() &
-              BINDINGS_POLICY_WEB_UI);
+  EXPECT_TRUE(webui_rfh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
 
   // Navigate to new site.
   const GURL url2("http://www.google.com");
@@ -1289,8 +1286,7 @@
   EXPECT_EQ(url2, entry2->GetURL());
   EXPECT_EQ(instance2,
             NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance());
-  EXPECT_FALSE(google_rfh->GetRenderViewHost()->GetEnabledBindings() &
-               BINDINGS_POLICY_WEB_UI);
+  EXPECT_FALSE(google_rfh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
 
   // Navigate to third page on same site.
   const GURL url3("http://news.google.com");
diff --git a/content/common/frame.mojom b/content/common/frame.mojom
index f751a31a..affcff1 100644
--- a/content/common/frame.mojom
+++ b/content/common/frame.mojom
@@ -15,6 +15,16 @@
   GetInterfaceProvider(service_manager.mojom.InterfaceProvider& interfaces);
 };
 
+// Implemented by the frame (e.g. renderer processes).
+// Instances of this interface must be associated with (i.e., FIFO with) the
+// legacy IPC channel.
+interface FrameBindingsControl {
+  // Used to tell a render frame whether it should expose various bindings
+  // that allow JS content extended privileges. See BindingsPolicy for valid
+  // flag values.
+  AllowBindings(int32 enabled_bindings_flags);
+};
+
 // Implemented by the frame server (i.e. the browser process).
 interface FrameHost {
   GetInterfaceProvider(service_manager.mojom.InterfaceProvider& interfaces);
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index 9c568d0..d78ba06 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -222,6 +222,14 @@
   // Retrieves the text input info associated with the current form field.
   virtual void RequestFocusedFormFieldData(FormFieldDataCallback& callback) = 0;
 
+  // Tell the render frame to enable a set of javascript bindings. The argument
+  // should be a combination of values from BindingsPolicy.
+  virtual void AllowBindings(int binding_flags) = 0;
+
+  // Returns a bitwise OR of bindings types that have been enabled for this
+  // RenderFrame. See BindingsPolicy for details.
+  virtual int GetEnabledBindings() const = 0;
+
  private:
   // This interface should only be implemented inside content.
   friend class RenderFrameHostImpl;
diff --git a/content/public/browser/render_view_host.h b/content/public/browser/render_view_host.h
index 04841375..96012bd 100644
--- a/content/public/browser/render_view_host.h
+++ b/content/public/browser/render_view_host.h
@@ -86,10 +86,6 @@
   // Returns the main frame for this render view.
   virtual RenderFrameHost* GetMainFrame() = 0;
 
-  // Tell the render view to enable a set of javascript bindings. The argument
-  // should be a combination of values from BindingsPolicy.
-  virtual void AllowBindings(int binding_flags) = 0;
-
   // Notifies the listener that a directory enumeration is complete.
   virtual void DirectoryEnumerationFinished(
       int request_id,
@@ -123,10 +119,6 @@
 
   virtual RenderViewHostDelegate* GetDelegate() const = 0;
 
-  // Returns a bitwise OR of bindings types that have been enabled for this
-  // RenderView. See BindingsPolicy for details.
-  virtual int GetEnabledBindings() const = 0;
-
   virtual SiteInstance* GetSiteInstance() const = 0;
 
   // Returns true if the RenderView is active and has not crashed.
diff --git a/content/public/renderer/render_frame.h b/content/public/renderer/render_frame.h
index bccf9041..a6abdae8 100644
--- a/content/public/renderer/render_frame.h
+++ b/content/public/renderer/render_frame.h
@@ -254,6 +254,10 @@
   virtual base::SingleThreadTaskRunner* GetLoadingTaskRunner() = 0;
   virtual base::SingleThreadTaskRunner* GetUnthrottledTaskRunner() = 0;
 
+  // Bitwise-ORed set of extra bindings that have been enabled.  See
+  // BindingsPolicy for details.
+  virtual int GetEnabledBindings() const = 0;
+
  protected:
   ~RenderFrame() override {}
 
diff --git a/content/public/renderer/render_view.h b/content/public/renderer/render_view.h
index 789b349..b828c99 100644
--- a/content/public/renderer/render_view.h
+++ b/content/public/renderer/render_view.h
@@ -87,10 +87,6 @@
   // false if the scrollbars should be hidden.
   virtual bool ShouldDisplayScrollbars(int width, int height) const = 0;
 
-  // Bitwise-ORed set of extra bindings that have been enabled.  See
-  // BindingsPolicy for details.
-  virtual int GetEnabledBindings() const = 0;
-
   // Whether content state (such as form state, scroll position and page
   // contents) should be sent to the browser immediately. This is normally
   // false, but set to true by some tests.
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 71213fc..3457a0f 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -140,6 +140,7 @@
 #include "content/renderer/stats_collection_controller.h"
 #include "content/renderer/web_frame_utils.h"
 #include "content/renderer/web_ui_extension.h"
+#include "content/renderer/web_ui_extension_data.h"
 #include "crypto/sha2.h"
 #include "gin/modules/console.h"
 #include "gin/modules/module_registry.h"
@@ -1129,6 +1130,7 @@
       engagement_binding_(this),
       frame_binding_(this),
       host_zoom_binding_(this),
+      frame_bindings_control_binding_(this),
       has_accessed_initial_document_(false),
       weak_factory_(this) {
   // We don't have a service_manager::Connection at this point, so use empty
@@ -1249,8 +1251,6 @@
                  "parent", parent_id);
   }
 
-  MaybeEnableMojoBindings();
-
 #if BUILDFLAG(ENABLE_PLUGINS)
   new PepperBrowserConnection(this);
 #endif
@@ -1282,6 +1282,13 @@
     DCHECK(render_view_->HasAddedInputHandler());
     input_handler_manager->RegisterRoutingID(GetRoutingID());
   }
+
+  const base::CommandLine& command_line =
+      *base::CommandLine::ForCurrentProcess();
+  if (command_line.HasSwitch(switches::kDomAutomationController))
+    enabled_bindings_ |= BINDINGS_POLICY_DOM_AUTOMATION;
+  if (command_line.HasSwitch(switches::kStatsCollectionController))
+    enabled_bindings_ |= BINDINGS_POLICY_STATS_COLLECTION;
 }
 
 void RenderFrameImpl::InitializeBlameContext(RenderFrameImpl* parent_frame) {
@@ -1649,6 +1656,11 @@
       std::move(pending_remote_interface_provider_request_));
 }
 
+void RenderFrameImpl::BindFrameBindingsControl(
+    mojom::FrameBindingsControlAssociatedRequest request) {
+  frame_bindings_control_binding_.Bind(std::move(request));
+}
+
 ManifestManager* RenderFrameImpl::manifest_manager() {
   return manifest_manager_;
 }
@@ -2707,6 +2719,22 @@
                             browser_info.identity, browser_spec);
 }
 
+void RenderFrameImpl::AllowBindings(int32_t enabled_bindings_flags) {
+  if (IsMainFrame() && (enabled_bindings_flags & BINDINGS_POLICY_WEB_UI) &&
+      !(enabled_bindings_ & BINDINGS_POLICY_WEB_UI)) {
+    // TODO(sammc): Move WebUIExtensionData to be a RenderFrameObserver.
+    // WebUIExtensionData deletes itself when |render_view_| is destroyed.
+    new WebUIExtensionData(render_view_);
+  }
+
+  enabled_bindings_ |= enabled_bindings_flags;
+
+  // Keep track of the total bindings accumulated in this process.
+  RenderProcess::current()->AddBindings(enabled_bindings_flags);
+
+  MaybeEnableMojoBindings();
+}
+
 // mojom::HostZoom implementation ----------------------------------------------
 
 void RenderFrameImpl::SetHostZoomLevel(const GURL& url, double zoom_level) {
@@ -3691,15 +3719,13 @@
 void RenderFrameImpl::didClearWindowObject(blink::WebLocalFrame* frame) {
   DCHECK_EQ(frame_, frame);
 
-  int enabled_bindings = render_view_->GetEnabledBindings();
-
-  if (enabled_bindings & BINDINGS_POLICY_WEB_UI)
+  if (enabled_bindings_ & BINDINGS_POLICY_WEB_UI)
     WebUIExtension::Install(frame);
 
-  if (enabled_bindings & BINDINGS_POLICY_DOM_AUTOMATION)
+  if (enabled_bindings_ & BINDINGS_POLICY_DOM_AUTOMATION)
     DomAutomationController::Install(this, frame);
 
-  if (enabled_bindings & BINDINGS_POLICY_STATS_COLLECTION)
+  if (enabled_bindings_ & BINDINGS_POLICY_STATS_COLLECTION)
     StatsCollectionController::Install(frame);
 
   const base::CommandLine& command_line =
@@ -6293,7 +6319,6 @@
 }
 
 void RenderFrameImpl::MaybeEnableMojoBindings() {
-  int enabled_bindings = RenderProcess::current()->GetEnabledBindings();
   // BINDINGS_POLICY_WEB_UI, BINDINGS_POLICY_MOJO and BINDINGS_POLICY_HEADLESS
   // are mutually exclusive. They provide access to Mojo bindings, but do so in
   // incompatible ways.
@@ -6303,22 +6328,23 @@
   // Make sure that at most one of BINDINGS_POLICY_WEB_UI, BINDINGS_POLICY_MOJO
   // and BINDINGS_POLICY_HEADLESS have been set.
   // NOTE x & (x - 1) == 0 is true iff x is zero or a power of two.
-  DCHECK_EQ((enabled_bindings & kAllBindingsTypes) &
-                ((enabled_bindings & kAllBindingsTypes) - 1),
+  DCHECK_EQ((enabled_bindings_ & kAllBindingsTypes) &
+                ((enabled_bindings_ & kAllBindingsTypes) - 1),
             0);
 
+  DCHECK_EQ(RenderProcess::current()->GetEnabledBindings(), enabled_bindings_);
+
   // If an MojoBindingsController already exists for this RenderFrameImpl, avoid
   // creating another one. It is not kept as a member, as it deletes itself when
   // the frame is destroyed.
   if (RenderFrameObserverTracker<MojoBindingsController>::Get(this))
     return;
 
-  if (IsMainFrame() &&
-      enabled_bindings & BINDINGS_POLICY_WEB_UI) {
+  if (IsMainFrame() && enabled_bindings_ & BINDINGS_POLICY_WEB_UI) {
     new MojoBindingsController(this, MojoBindingsType::FOR_WEB_UI);
-  } else if (enabled_bindings & BINDINGS_POLICY_MOJO) {
+  } else if (enabled_bindings_ & BINDINGS_POLICY_MOJO) {
     new MojoBindingsController(this, MojoBindingsType::FOR_LAYOUT_TESTS);
-  } else if (enabled_bindings & BINDINGS_POLICY_HEADLESS) {
+  } else if (enabled_bindings_ & BINDINGS_POLICY_HEADLESS) {
     new MojoBindingsController(this, MojoBindingsType::FOR_HEADLESS);
   }
 }
@@ -6593,6 +6619,9 @@
   GetAssociatedInterfaceRegistry()->AddInterface(
       base::Bind(&RenderFrameImpl::BindEngagement, weak_factory_.GetWeakPtr()));
 
+  GetAssociatedInterfaceRegistry()->AddInterface(base::Bind(
+      &RenderFrameImpl::BindFrameBindingsControl, weak_factory_.GetWeakPtr()));
+
   if (!frame_->parent()) {
     // Only main frame have ImageDownloader service.
     GetInterfaceRegistry()->AddInterface(base::Bind(
@@ -6666,6 +6695,10 @@
   return GetWebFrame()->unthrottledTaskRunner();
 }
 
+int RenderFrameImpl::GetEnabledBindings() const {
+  return enabled_bindings_;
+}
+
 blink::WebPlugin* RenderFrameImpl::GetWebPluginForFind() {
   if (frame_->document().isPluginDocument())
     return frame_->document().to<WebPluginDocument>().plugin();
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index eb9372d..de9e73a 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -177,6 +177,7 @@
       NON_EXPORTED_BASE(blink::mojom::EngagementClient),
       NON_EXPORTED_BASE(mojom::Frame),
       NON_EXPORTED_BASE(mojom::HostZoom),
+      NON_EXPORTED_BASE(mojom::FrameBindingsControl),
       NON_EXPORTED_BASE(public blink::WebFrameClient),
       NON_EXPORTED_BASE(public blink::WebFrameSerializerClient) {
  public:
@@ -460,6 +461,7 @@
   base::SingleThreadTaskRunner* GetTimerTaskRunner() override;
   base::SingleThreadTaskRunner* GetLoadingTaskRunner() override;
   base::SingleThreadTaskRunner* GetUnthrottledTaskRunner() override;
+  int GetEnabledBindings() const override;
 
   // blink::mojom::EngagementClient implementation:
   void SetEngagementLevel(const url::Origin& origin,
@@ -469,6 +471,9 @@
   void GetInterfaceProvider(
       service_manager::mojom::InterfaceProviderRequest request) override;
 
+  // mojom::FrameBindingsControl implementation:
+  void AllowBindings(int32_t enabled_bindings_flags) override;
+
   // mojom::HostZoom implementation:
   void SetHostZoomLevel(const GURL& url, double zoom_level) override;
 
@@ -676,6 +681,9 @@
   // Binds to the FrameHost in the browser.
   void BindFrame(mojom::FrameRequest request, mojom::FrameHostPtr frame_host);
 
+  void BindFrameBindingsControl(
+      mojom::FrameBindingsControlAssociatedRequest request);
+
   ManifestManager* manifest_manager();
 
   // TODO(creis): Remove when the only caller, the HistoryController, is no
@@ -1361,6 +1369,8 @@
   mojo::AssociatedBinding<blink::mojom::EngagementClient> engagement_binding_;
   mojo::Binding<mojom::Frame> frame_binding_;
   mojo::AssociatedBinding<mojom::HostZoom> host_zoom_binding_;
+  mojo::AssociatedBinding<mojom::FrameBindingsControl>
+      frame_bindings_control_binding_;
   mojom::FrameHostPtr frame_host_;
 
   // Indicates whether |didAccessInitialDocument| was called.
@@ -1376,6 +1386,10 @@
 
   bool browser_side_navigation_pending_ = false;
 
+  // A bitwise OR of bindings types that have been enabled for this RenderFrame.
+  // See BindingsPolicy for details.
+  int enabled_bindings_ = 0;
+
   base::WeakPtrFactory<RenderFrameImpl> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(RenderFrameImpl);
diff --git a/content/renderer/render_process_impl.cc b/content/renderer/render_process_impl.cc
index fb207b5..db042b9 100644
--- a/content/renderer/render_process_impl.cc
+++ b/content/renderer/render_process_impl.cc
@@ -29,6 +29,7 @@
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
 #include "content/child/site_isolation_stats_gatherer.h"
+#include "content/public/common/bindings_policy.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
@@ -193,6 +194,11 @@
 
   SiteIsolationStatsGatherer::SetEnabled(
       GetContentClient()->renderer()->ShouldGatherSiteIsolationStats());
+
+  if (command_line.HasSwitch(switches::kDomAutomationController))
+    enabled_bindings_ |= BINDINGS_POLICY_DOM_AUTOMATION;
+  if (command_line.HasSwitch(switches::kStatsCollectionController))
+    enabled_bindings_ |= BINDINGS_POLICY_STATS_COLLECTION;
 }
 
 RenderProcessImpl::~RenderProcessImpl() {
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index 13f946f..5b8ed78 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -749,7 +749,7 @@
 
 TEST_F(RenderViewImplTest, DecideNavigationPolicyForWebUI) {
   // Enable bindings to simulate a WebUI view.
-  view()->OnAllowBindings(BINDINGS_POLICY_WEB_UI);
+  view()->GetMainRenderFrame()->AllowBindings(BINDINGS_POLICY_WEB_UI);
 
   DocumentState state;
   state.set_navigation_state(NavigationStateImpl::CreateContentInitiated());
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index c3f96d8..cbce5ea 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -713,11 +713,6 @@
 
   new IdleUserDetector(this);
 
-  if (command_line.HasSwitch(switches::kDomAutomationController))
-    enabled_bindings_ |= BINDINGS_POLICY_DOM_AUTOMATION;
-  if (command_line.HasSwitch(switches::kStatsCollectionController))
-    enabled_bindings_ |= BINDINGS_POLICY_STATS_COLLECTION;
-
   GetContentClient()->renderer()->RenderViewCreated(this);
 
   // Ensure that sandbox flags are inherited from an opener in a different
@@ -1206,7 +1201,6 @@
     IPC_MESSAGE_HANDLER(InputMsg_ScrollFocusedEditableNodeIntoRect,
                         OnScrollFocusedEditableNodeIntoRect)
     IPC_MESSAGE_HANDLER(ViewMsg_SetPageScale, OnSetPageScale)
-    IPC_MESSAGE_HANDLER(ViewMsg_AllowBindings, OnAllowBindings)
     IPC_MESSAGE_HANDLER(ViewMsg_SetInitialFocus, OnSetInitialFocus)
     IPC_MESSAGE_HANDLER(ViewMsg_UpdateTargetURL_ACK, OnUpdateTargetURLAck)
     IPC_MESSAGE_HANDLER(ViewMsg_UpdateWebPreferences, OnUpdateWebPreferences)
@@ -2008,10 +2002,6 @@
            disable_scrollbars_size_limit_.height() <= height));
 }
 
-int RenderViewImpl::GetEnabledBindings() const {
-  return enabled_bindings_;
-}
-
 bool RenderViewImpl::GetContentStateImmediately() const {
   return send_content_state_immediately_;
 }
@@ -2044,22 +2034,6 @@
   SetZoomLevel(zoom_level);
 }
 
-void RenderViewImpl::OnAllowBindings(int enabled_bindings_flags) {
-  if ((enabled_bindings_flags & BINDINGS_POLICY_WEB_UI) &&
-      !(enabled_bindings_ & BINDINGS_POLICY_WEB_UI)) {
-    // WebUIExtensionData deletes itself when we're destroyed.
-    new WebUIExtensionData(this);
-  }
-
-  enabled_bindings_ |= enabled_bindings_flags;
-
-  // Keep track of the total bindings accumulated in this process.
-  RenderProcess::current()->AddBindings(enabled_bindings_flags);
-
-  if (main_render_frame_)
-    main_render_frame_->MaybeEnableMojoBindings();
-}
-
 void RenderViewImpl::OnUpdateWebPreferences(const WebPreferences& prefs) {
   webkit_preferences_ = prefs;
   ApplyWebPreferencesInternal(webkit_preferences_, webview(), compositor_deps_);
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index 18e359d..4eb69544 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -369,7 +369,6 @@
   blink::WebView* GetWebView() override;
   blink::WebFrameWidget* GetWebFrameWidget() override;
   bool ShouldDisplayScrollbars(int width, int height) const override;
-  int GetEnabledBindings() const override;
   bool GetContentStateImmediately() const override;
   void Repaint(const gfx::Size& size) override;
   void SetEditCommandForNextKeyEvent(const std::string& name,
@@ -524,7 +523,6 @@
   void OnExecuteEditCommand(const std::string& name, const std::string& value);
   void OnMoveCaret(const gfx::Point& point);
   void OnScrollFocusedEditableNodeIntoRect(const gfx::Rect& rect);
-  void OnAllowBindings(int enabled_bindings_flags);
   void OnAllowScriptToClose(bool script_can_close);
   void OnCancelDownload(int32_t download_id);
   void OnClosePage();
diff --git a/content/renderer/web_ui_extension.cc b/content/renderer/web_ui_extension.cc
index cb42f60..1b3b6e7 100644
--- a/content/renderer/web_ui_extension.cc
+++ b/content/renderer/web_ui_extension.cc
@@ -14,6 +14,7 @@
 #include "content/public/common/bindings_policy.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/renderer/chrome_object_extensions_utils.h"
+#include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_thread.h"
 #include "content/public/renderer/render_view.h"
 #include "content/renderer/web_ui_extension_data.h"
@@ -44,8 +45,12 @@
 
   GURL frame_url = frame->document().url();
 
+  RenderFrame* render_frame = RenderFrame::FromWebFrame(frame);
+  if (!render_frame)
+    return false;
+
   bool webui_enabled =
-      (render_view->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI) &&
+      (render_frame->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI) &&
       (frame_url.SchemeIs(kChromeUIScheme) ||
        frame_url.SchemeIs(url::kDataScheme));
 
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index 6749acd..bd3da17 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -164,8 +164,6 @@
     "common/shell_origin_trial_policy.h",
     "common/shell_switches.cc",
     "common/shell_switches.h",
-    "common/shell_test_configuration.cc",
-    "common/shell_test_configuration.h",
     "renderer/layout_test/blink_test_helpers.cc",
     "renderer/layout_test/blink_test_helpers.h",
     "renderer/layout_test/blink_test_runner.cc",
@@ -760,6 +758,12 @@
 
 mojom("mojo_bindings") {
   sources = [
+    "common/layout_test.mojom",
     "common/layout_test/layout_test_bluetooth_fake_adapter_setter.mojom",
   ]
+  public_deps = [
+    "//mojo/common:common_custom_types",
+    "//ui/gfx/geometry/mojo",
+    "//url/mojo:url_mojom_gurl",
+  ]
 }
diff --git a/content/shell/browser/content_shell_renderer_manifest_overlay.json b/content/shell/browser/content_shell_renderer_manifest_overlay.json
index ec29032..2d373b4 100644
--- a/content/shell/browser/content_shell_renderer_manifest_overlay.json
+++ b/content/shell/browser/content_shell_renderer_manifest_overlay.json
@@ -7,6 +7,13 @@
           "content::mojom::TestService"
         ]
       }
+    },
+    "navigation:frame": {
+      "provides": {
+        "browser": [
+          "content::mojom::LayoutTestControl"
+        ]
+      }
     }
   }
 }
diff --git a/content/shell/browser/layout_test/blink_test_controller.cc b/content/shell/browser/layout_test/blink_test_controller.cc
index e318bf9..46b677d 100644
--- a/content/shell/browser/layout_test/blink_test_controller.cc
+++ b/content/shell/browser/layout_test/blink_test_controller.cc
@@ -38,6 +38,7 @@
 #include "content/public/browser/service_worker_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/associated_interface_provider.h"
 #include "content/public/common/bindings_policy.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/url_constants.h"
@@ -280,6 +281,7 @@
   all_observed_render_process_hosts_.clear();
   main_window_render_process_hosts_.clear();
   accumulated_layout_test_runtime_flags_changes_.Clear();
+  layout_test_control_map_.clear();
   ShellBrowserContext* browser_context =
       ShellContentBrowserClient::Get()->browser_context();
   is_compositing_test_ =
@@ -583,10 +585,9 @@
 }
 
 void BlinkTestController::HandleNewRenderFrameHost(RenderFrameHost* frame) {
-  // All RenderViewHosts in layout tests should get Mojo bindings.
-  RenderViewHost* rvh = frame->GetRenderViewHost();
-  if (!(rvh->GetEnabledBindings() & BINDINGS_POLICY_MOJO))
-    rvh->AllowBindings(BINDINGS_POLICY_MOJO);
+  // All RenderFrameHosts in layout tests should get Mojo bindings.
+  if (!(frame->GetEnabledBindings() & BINDINGS_POLICY_MOJO))
+    frame->AllowBindings(BINDINGS_POLICY_MOJO);
 
   RenderProcessHost* process = frame->GetProcess();
   bool main_window =
@@ -606,24 +607,26 @@
 
     // Make sure the new renderer process has a test configuration shared with
     // other renderers.
-    ShellTestConfiguration params;
-    params.current_working_directory = current_working_directory_;
-    params.temp_path = temp_path_;
-    params.test_url = test_url_;
-    params.enable_pixel_dumping = enable_pixel_dumping_;
-    params.allow_external_pages =
+    mojom::ShellTestConfigurationPtr params =
+        mojom::ShellTestConfiguration::New();
+    params->enable_pixel_dumping = true;
+    params->allow_external_pages = false;
+    params->current_working_directory = current_working_directory_;
+    params->temp_path = temp_path_;
+    params->test_url = test_url_;
+    params->enable_pixel_dumping = enable_pixel_dumping_;
+    params->allow_external_pages =
         base::CommandLine::ForCurrentProcess()->HasSwitch(
             switches::kAllowExternalPages);
-    params.expected_pixel_hash = expected_pixel_hash_;
-    params.initial_size = initial_size_;
+    params->expected_pixel_hash = expected_pixel_hash_;
+    params->initial_size = initial_size_;
 
     if (did_send_initial_test_configuration_) {
-      frame->Send(new ShellViewMsg_ReplicateTestConfiguration(
-          frame->GetRoutingID(), params));
+      GetLayoutTestControlPtr(frame)->ReplicateTestConfiguration(
+          std::move(params));
     } else {
       did_send_initial_test_configuration_ = true;
-      frame->Send(
-          new ShellViewMsg_SetTestConfiguration(frame->GetRoutingID(), params));
+      GetLayoutTestControlPtr(frame)->SetTestConfiguration(std::move(params));
     }
   }
 
@@ -633,8 +636,7 @@
     all_observed_render_process_hosts_.insert(process);
 
     if (!main_window) {
-      frame->Send(
-          new ShellViewMsg_SetupSecondaryRenderer(frame->GetRoutingID()));
+      GetLayoutTestControlPtr(frame)->SetupSecondaryRenderer();
     }
 
     process->Send(new LayoutTestMsg_ReplicateLayoutTestRuntimeFlagsChanges(
@@ -705,8 +707,16 @@
 }
 
 void BlinkTestController::OnInitiateLayoutDump() {
-  pending_layout_dumps_ = main_window_->web_contents()->SendToAllFrames(
-      new ShellViewMsg_LayoutDumpRequest(MSG_ROUTING_NONE));
+  int number_of_messages = 0;
+  for (RenderFrameHost* rfh : main_window_->web_contents()->GetAllFrames()) {
+    if (!rfh->IsRenderFrameLive())
+      continue;
+
+    ++number_of_messages;
+    GetLayoutTestControlPtr(rfh)->LayoutDumpRequest();
+  }
+
+  pending_layout_dumps_ = number_of_messages;
 }
 
 void BlinkTestController::OnLayoutTestRuntimeFlagsChanged(
@@ -954,4 +964,14 @@
   bluetooth_chooser_factory_->SendEvent(event, argument);
 }
 
+mojom::LayoutTestControl* BlinkTestController::GetLayoutTestControlPtr(
+    RenderFrameHost* frame) {
+  if (layout_test_control_map_.find(frame) == layout_test_control_map_.end()) {
+    frame->GetRemoteAssociatedInterfaces()->GetInterface(
+        &layout_test_control_map_[frame]);
+  }
+  DCHECK(layout_test_control_map_[frame].get());
+  return layout_test_control_map_[frame].get();
+}
+
 }  // namespace content
diff --git a/content/shell/browser/layout_test/blink_test_controller.h b/content/shell/browser/layout_test/blink_test_controller.h
index 2d7f699..388ac5f 100644
--- a/content/shell/browser/layout_test/blink_test_controller.h
+++ b/content/shell/browser/layout_test/blink_test_controller.h
@@ -26,6 +26,7 @@
 #include "content/public/browser/render_process_host_observer.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/web_preferences.h"
+#include "content/shell/common/layout_test.mojom.h"
 #include "content/shell/common/leak_detection_result.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -215,6 +216,7 @@
   void OnGetBluetoothManualChooserEvents();
   void OnSendBluetoothManualChooserEvent(const std::string& event,
                                          const std::string& argument);
+  mojom::LayoutTestControl* GetLayoutTestControlPtr(RenderFrameHost* frame);
 
   std::unique_ptr<BlinkTestResultPrinter> printer_;
 
@@ -278,6 +280,9 @@
   // renderer created while test is in progress).
   base::DictionaryValue accumulated_layout_test_runtime_flags_changes_;
 
+  // Map from one frame to one mojo pipe.
+  std::map<RenderFrameHost*, mojom::LayoutTestControlAssociatedPtr>
+      layout_test_control_map_;
 #if defined(OS_ANDROID)
   // Because of the nested message pump implementation, Android needs to allow
   // waiting on the UI thread while layout tests are being ran.
diff --git a/content/shell/common/OWNERS b/content/shell/common/OWNERS
index 42444bc..b6ba3c9 100644
--- a/content/shell/common/OWNERS
+++ b/content/shell/common/OWNERS
@@ -1,2 +1,8 @@
 per-file *_messages*.h=set noparent
 per-file *_messages*.h=file://ipc/SECURITY_OWNERS
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
+per-file *_messages.cc=set noparent
+per-file *_messages.cc=file://ipc/SECURITY_OWNERS
+per-file *_param_traits*.*=set noparent
+per-file *_param_traits*.*=file://ipc/SECURITY_OWNERS
\ No newline at end of file
diff --git a/content/shell/common/layout_test.mojom b/content/shell/common/layout_test.mojom
new file mode 100644
index 0000000..c0de4b5
--- /dev/null
+++ b/content/shell/common/layout_test.mojom
@@ -0,0 +1,50 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module content.mojom;
+
+import "mojo/common/file_path.mojom";
+import "ui/gfx/geometry/mojo/geometry.mojom";
+import "url/mojo/url.mojom";
+
+struct ShellTestConfiguration {
+  // The current working directory.
+  mojo.common.mojom.FilePath current_working_directory;
+
+  // The temporary directory of the system.
+  mojo.common.mojom.FilePath temp_path;
+
+  // The URL of the current layout test.
+  url.mojom.Url test_url;
+
+  // True if pixel tests are enabled.
+  bool enable_pixel_dumping;
+
+  // True if tests can open external URLs
+  bool allow_external_pages;
+
+  // The expected MD5 hash of the pixel results.
+  string expected_pixel_hash;
+
+  // The initial size of the test window.
+  gfx.mojom.Size initial_size;
+};
+
+interface LayoutTestControl {
+  // Asks a frame to dump its contents into a string and send them back over
+  // IPC.
+  LayoutDumpRequest();
+
+  // Replicates test config (for an already started test) to a new renderer
+  // that hosts parts of the main test window.
+  ReplicateTestConfiguration(ShellTestConfiguration config);
+
+  // Sets the test config for a layout test that is being started.  This message
+  // is sent only to a renderer that hosts parts of the main test window.
+  SetTestConfiguration(ShellTestConfiguration config);
+
+  // Sets up a secondary renderer (renderer that doesn't [yet] host parts of the
+  // main test window) for a layout test.
+  SetupSecondaryRenderer();
+};
diff --git a/content/shell/common/shell_messages.h b/content/shell/common/shell_messages.h
index ef45330..087def0f8 100644
--- a/content/shell/common/shell_messages.h
+++ b/content/shell/common/shell_messages.h
@@ -10,7 +10,6 @@
 #include "content/public/common/common_param_traits.h"
 #include "content/public/common/page_state.h"
 #include "content/shell/common/leak_detection_result.h"
-#include "content/shell/common/shell_test_configuration.h"
 #include "ipc/ipc_message_macros.h"
 #include "ipc/ipc_platform_file.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -19,16 +18,6 @@
 
 #define IPC_MESSAGE_START ShellMsgStart
 
-IPC_STRUCT_TRAITS_BEGIN(content::ShellTestConfiguration)
-IPC_STRUCT_TRAITS_MEMBER(current_working_directory)
-IPC_STRUCT_TRAITS_MEMBER(temp_path)
-IPC_STRUCT_TRAITS_MEMBER(test_url)
-IPC_STRUCT_TRAITS_MEMBER(enable_pixel_dumping)
-IPC_STRUCT_TRAITS_MEMBER(allow_external_pages)
-IPC_STRUCT_TRAITS_MEMBER(expected_pixel_hash)
-IPC_STRUCT_TRAITS_MEMBER(initial_size)
-IPC_STRUCT_TRAITS_END()
-
 // Tells the renderer to reset all test runners.
 IPC_MESSAGE_ROUTED0(ShellViewMsg_Reset)
 
@@ -36,20 +25,6 @@
 IPC_MESSAGE_CONTROL1(ShellViewMsg_SetWebKitSourceDir,
                      base::FilePath /* webkit source dir */)
 
-// Sets the test config for a layout test that is being started.  This message
-// is sent only to a renderer that hosts parts of the main test window.
-IPC_MESSAGE_ROUTED1(ShellViewMsg_SetTestConfiguration,
-                    content::ShellTestConfiguration)
-
-// Replicates test config (for an already started test) to a new renderer
-// that hosts parts of the main test window.
-IPC_MESSAGE_ROUTED1(ShellViewMsg_ReplicateTestConfiguration,
-                    content::ShellTestConfiguration)
-
-// Sets up a secondary renderer (renderer that doesn't [yet] host parts of the
-// main test window) for a layout test.
-IPC_MESSAGE_ROUTED0(ShellViewMsg_SetupSecondaryRenderer)
-
 // Tells the main window that a secondary renderer in a different process asked
 // to finish the test.
 IPC_MESSAGE_ROUTED0(ShellViewMsg_TestFinishedInSecondaryRenderer)
@@ -66,9 +41,6 @@
 
 IPC_MESSAGE_ROUTED0(ShellViewMsg_TryLeakDetection)
 
-// Asks a frame to dump its contents into a string and send them back over IPC.
-IPC_MESSAGE_ROUTED0(ShellViewMsg_LayoutDumpRequest)
-
 // Notifies BlinkTestRunner that the layout dump has completed
 // (and that it can proceed with finishing up the test).
 IPC_MESSAGE_ROUTED1(ShellViewMsg_LayoutDumpCompleted,
diff --git a/content/shell/common/shell_test_configuration.cc b/content/shell/common/shell_test_configuration.cc
deleted file mode 100644
index 979adb6..0000000
--- a/content/shell/common/shell_test_configuration.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/shell/common/shell_test_configuration.h"
-
-namespace content {
-
-ShellTestConfiguration::ShellTestConfiguration()
-    : enable_pixel_dumping(true),
-      allow_external_pages(false) {}
-
-ShellTestConfiguration::~ShellTestConfiguration() {}
-
-}  // namespace content
diff --git a/content/shell/common/shell_test_configuration.h b/content/shell/common/shell_test_configuration.h
deleted file mode 100644
index 74b5d0b2..0000000
--- a/content/shell/common/shell_test_configuration.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_SHELL_COMMON_SHELL_TEST_CONFIGURATION_H_
-#define CONTENT_SHELL_COMMON_SHELL_TEST_CONFIGURATION_H_
-
-#include <string>
-
-#include "base/files/file_path.h"
-#include "ui/gfx/geometry/size.h"
-#include "url/gurl.h"
-
-namespace content {
-
-struct ShellTestConfiguration {
-  ShellTestConfiguration();
-  ~ShellTestConfiguration();
-
-  // The current working directory.
-  base::FilePath current_working_directory;
-
-  // The temporary directory of the system.
-  base::FilePath temp_path;
-
-  // The URL of the current layout test.
-  GURL test_url;
-
-  // True if pixel tests are enabled.
-  bool enable_pixel_dumping;
-
-  // True if tests can open external URLs
-  bool allow_external_pages;
-
-  // The expected MD5 hash of the pixel results.
-  std::string expected_pixel_hash;
-
-  // The initial size of the test window.
-  gfx::Size initial_size;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_SHELL_COMMON_SHELL_TEST_CONFIGURATION_H_
diff --git a/content/shell/renderer/layout_test/blink_test_runner.cc b/content/shell/renderer/layout_test/blink_test_runner.cc
index aeccd01d..ec2b3185 100644
--- a/content/shell/renderer/layout_test/blink_test_runner.cc
+++ b/content/shell/renderer/layout_test/blink_test_runner.cc
@@ -332,7 +332,7 @@
   base::FilePath path = base::FilePath::FromUTF8Unsafe(utf8_path);
   if (!path.IsAbsolute()) {
     GURL base_url =
-        net::FilePathToFileURL(test_config_.current_working_directory.Append(
+        net::FilePathToFileURL(test_config_->current_working_directory.Append(
             FILE_PATH_LITERAL("foo")));
     net::FileURLToFilePath(base_url.Resolve(utf8_path), &path);
   }
@@ -563,7 +563,7 @@
 #if defined(OS_WIN)
   if (base::StartsWith(resource, "/tmp/", base::CompareCase::SENSITIVE)) {
     // We want a temp file.
-    GURL base_url = net::FilePathToFileURL(test_config_.temp_path);
+    GURL base_url = net::FilePathToFileURL(test_config_->temp_path);
     return base_url.Resolve(resource.substr(sizeof("/tmp/") - 1)).spec();
   }
 #endif
@@ -643,7 +643,7 @@
 }
 
 bool BlinkTestRunner::AllowExternalPages() {
-  return test_config_.allow_external_pages;
+  return test_config_->allow_external_pages;
 }
 
 std::string BlinkTestRunner::DumpHistoryForWindow(blink::WebView* web_view) {
@@ -896,7 +896,7 @@
 void BlinkTestRunner::CaptureDumpContinued() {
   test_runner::WebTestInterfaces* interfaces =
       LayoutTestRenderThreadObserver::GetInstance()->test_interfaces();
-  if (test_config_.enable_pixel_dumping &&
+  if (test_config_->enable_pixel_dumping &&
       interfaces->TestRunner()->ShouldGeneratePixelResults() &&
       !interfaces->TestRunner()->ShouldDumpAsAudio()) {
     CHECK(render_view()->GetWebView()->isAcceleratedCompositingActive());
@@ -923,7 +923,7 @@
   base::MD5Sum(snapshot.getPixels(), snapshot.getSize(), &digest);
   std::string actual_pixel_hash = base::MD5DigestToBase16(digest);
 
-  if (actual_pixel_hash == test_config_.expected_pixel_hash) {
+  if (actual_pixel_hash == test_config_->expected_pixel_hash) {
     SkBitmap empty_image;
     Send(new ShellViewHostMsg_ImageDump(
         routing_id(), actual_pixel_hash, empty_image));
@@ -961,27 +961,28 @@
 }
 
 void BlinkTestRunner::OnReplicateTestConfiguration(
-    const ShellTestConfiguration& params) {
+    mojom::ShellTestConfigurationPtr params) {
   test_runner::WebTestInterfaces* interfaces =
       LayoutTestRenderThreadObserver::GetInstance()->test_interfaces();
 
-  test_config_ = params;
+  test_config_ = params.Clone();
 
   is_main_window_ = true;
   interfaces->SetMainView(render_view()->GetWebView());
 
   interfaces->SetTestIsRunning(true);
-  interfaces->ConfigureForTestWithURL(params.test_url,
-                                      params.enable_pixel_dumping);
+  interfaces->ConfigureForTestWithURL(params->test_url,
+                                      params->enable_pixel_dumping);
 }
 
 void BlinkTestRunner::OnSetTestConfiguration(
-    const ShellTestConfiguration& params) {
-  OnReplicateTestConfiguration(params);
+    mojom::ShellTestConfigurationPtr params) {
+  mojom::ShellTestConfigurationPtr local_params = params.Clone();
+  OnReplicateTestConfiguration(std::move(params));
 
-  ForceResizeRenderView(
-      render_view(),
-      WebSize(params.initial_size.width(), params.initial_size.height()));
+  ForceResizeRenderView(render_view(),
+                        WebSize(local_params->initial_size.width(),
+                                local_params->initial_size.height()));
   LayoutTestRenderThreadObserver::GetInstance()
       ->test_interfaces()
       ->TestRunner()
diff --git a/content/shell/renderer/layout_test/blink_test_runner.h b/content/shell/renderer/layout_test/blink_test_runner.h
index 62d7422..21e617e 100644
--- a/content/shell/renderer/layout_test/blink_test_runner.h
+++ b/content/shell/renderer/layout_test/blink_test_runner.h
@@ -17,8 +17,8 @@
 #include "content/public/common/page_state.h"
 #include "content/public/renderer/render_view_observer.h"
 #include "content/public/renderer/render_view_observer_tracker.h"
+#include "content/shell/common/layout_test.mojom.h"
 #include "content/shell/common/layout_test/layout_test_bluetooth_fake_adapter_setter.mojom.h"
-#include "content/shell/common/shell_test_configuration.h"
 #include "v8/include/v8.h"
 
 class SkBitmap;
@@ -176,8 +176,8 @@
   void ReportLeakDetectionResult(const LeakDetectionResult& result);
 
   // Message handlers forwarded by LayoutTestRenderFrameObserver.
-  void OnSetTestConfiguration(const ShellTestConfiguration& params);
-  void OnReplicateTestConfiguration(const ShellTestConfiguration& params);
+  void OnSetTestConfiguration(mojom::ShellTestConfigurationPtr params);
+  void OnReplicateTestConfiguration(mojom::ShellTestConfigurationPtr params);
   void OnSetupSecondaryRenderer();
 
  private:
@@ -210,7 +210,7 @@
 
   test_runner::TestPreferences prefs_;
 
-  ShellTestConfiguration test_config_;
+  mojom::ShellTestConfigurationPtr test_config_;
 
   std::vector<int> routing_ids_;
   std::vector<std::vector<PageState> > session_histories_;
diff --git a/content/shell/renderer/layout_test/layout_test_render_frame_observer.cc b/content/shell/renderer/layout_test/layout_test_render_frame_observer.cc
index 4417b57f..0f8aaa0 100644
--- a/content/shell/renderer/layout_test/layout_test_render_frame_observer.cc
+++ b/content/shell/renderer/layout_test/layout_test_render_frame_observer.cc
@@ -8,6 +8,7 @@
 
 #include "components/test_runner/web_test_interfaces.h"
 #include "components/test_runner/web_test_runner.h"
+#include "content/public/common/associated_interface_registry.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/shell/common/shell_messages.h"
 #include "content/shell/renderer/layout_test/blink_test_runner.h"
@@ -19,36 +20,28 @@
 
 LayoutTestRenderFrameObserver::LayoutTestRenderFrameObserver(
     RenderFrame* render_frame)
-    : RenderFrameObserver(render_frame) {
+    : RenderFrameObserver(render_frame), binding_(this) {
   render_frame->GetWebFrame()->setContentSettingsClient(
       LayoutTestRenderThreadObserver::GetInstance()
           ->test_interfaces()
           ->TestRunner()
           ->GetWebContentSettings());
+  render_frame->GetAssociatedInterfaceRegistry()->AddInterface(base::Bind(
+      &LayoutTestRenderFrameObserver::BindRequest, base::Unretained(this)));
 }
 
-bool LayoutTestRenderFrameObserver::OnMessageReceived(
-    const IPC::Message& message) {
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(LayoutTestRenderFrameObserver, message)
-    IPC_MESSAGE_HANDLER(ShellViewMsg_LayoutDumpRequest, OnLayoutDumpRequest)
-    IPC_MESSAGE_HANDLER(ShellViewMsg_ReplicateTestConfiguration,
-                        OnReplicateTestConfiguration)
-    IPC_MESSAGE_HANDLER(ShellViewMsg_SetTestConfiguration,
-                        OnSetTestConfiguration)
-    IPC_MESSAGE_HANDLER(ShellViewMsg_SetupSecondaryRenderer,
-                        OnSetupSecondaryRenderer)
-    IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
+LayoutTestRenderFrameObserver::~LayoutTestRenderFrameObserver() = default;
 
-  return handled;
+void LayoutTestRenderFrameObserver::BindRequest(
+    mojom::LayoutTestControlAssociatedRequest request) {
+  binding_.Bind(std::move(request));
 }
 
 void LayoutTestRenderFrameObserver::OnDestruct() {
   delete this;
 }
 
-void LayoutTestRenderFrameObserver::OnLayoutDumpRequest() {
+void LayoutTestRenderFrameObserver::LayoutDumpRequest() {
   std::string dump =
       LayoutTestRenderThreadObserver::GetInstance()
           ->test_interfaces()
@@ -57,19 +50,19 @@
   Send(new ShellViewHostMsg_LayoutDumpResponse(routing_id(), dump));
 }
 
-void LayoutTestRenderFrameObserver::OnReplicateTestConfiguration(
-    const ShellTestConfiguration& test_config) {
+void LayoutTestRenderFrameObserver::ReplicateTestConfiguration(
+    mojom::ShellTestConfigurationPtr config) {
   BlinkTestRunner::Get(render_frame()->GetRenderView())
-      ->OnReplicateTestConfiguration(test_config);
+      ->OnReplicateTestConfiguration(std::move(config));
 }
 
-void LayoutTestRenderFrameObserver::OnSetTestConfiguration(
-    const ShellTestConfiguration& test_config) {
+void LayoutTestRenderFrameObserver::SetTestConfiguration(
+    mojom::ShellTestConfigurationPtr config) {
   BlinkTestRunner::Get(render_frame()->GetRenderView())
-      ->OnSetTestConfiguration(test_config);
+      ->OnSetTestConfiguration(std::move(config));
 }
 
-void LayoutTestRenderFrameObserver::OnSetupSecondaryRenderer() {
+void LayoutTestRenderFrameObserver::SetupSecondaryRenderer() {
   BlinkTestRunner::Get(render_frame()->GetRenderView())
       ->OnSetupSecondaryRenderer();
 }
diff --git a/content/shell/renderer/layout_test/layout_test_render_frame_observer.h b/content/shell/renderer/layout_test/layout_test_render_frame_observer.h
index 230ec1ce..bd620c08 100644
--- a/content/shell/renderer/layout_test/layout_test_render_frame_observer.h
+++ b/content/shell/renderer/layout_test/layout_test_render_frame_observer.h
@@ -7,33 +7,29 @@
 
 #include "base/macros.h"
 #include "content/public/renderer/render_frame_observer.h"
-
-namespace IPC {
-class Message;
-}  // namespace IPC
+#include "content/shell/common/layout_test.mojom.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
 
 namespace content {
-struct ShellTestConfiguration;
-class RenderFrame;
-struct ShellTestConfiguration;
 
-class LayoutTestRenderFrameObserver : public RenderFrameObserver {
+class LayoutTestRenderFrameObserver : public RenderFrameObserver,
+                                      public mojom::LayoutTestControl {
  public:
   explicit LayoutTestRenderFrameObserver(RenderFrame* render_frame);
-  ~LayoutTestRenderFrameObserver() override {}
-
-  // RenderFrameObserver implementation.
-  bool OnMessageReceived(const IPC::Message& message) override;
+  ~LayoutTestRenderFrameObserver() override;
 
  private:
   // RenderFrameObserver implementation.
   void OnDestruct() override;
 
-  void OnLayoutDumpRequest();
-  void OnSetTestConfiguration(const ShellTestConfiguration& test_config);
-  void OnReplicateTestConfiguration(const ShellTestConfiguration& test_config);
-  void OnSetupSecondaryRenderer();
+  void LayoutDumpRequest() override;
+  void SetTestConfiguration(mojom::ShellTestConfigurationPtr config) override;
+  void ReplicateTestConfiguration(
+      mojom::ShellTestConfigurationPtr config) override;
+  void SetupSecondaryRenderer() override;
+  void BindRequest(mojom::LayoutTestControlAssociatedRequest request);
 
+  mojo::AssociatedBinding<mojom::LayoutTestControl> binding_;
   DISALLOW_COPY_AND_ASSIGN(LayoutTestRenderFrameObserver);
 };
 
diff --git a/headless/lib/browser/headless_web_contents_impl.cc b/headless/lib/browser/headless_web_contents_impl.cc
index 1516d96..094979f 100644
--- a/headless/lib/browser/headless_web_contents_impl.cc
+++ b/headless/lib/browser/headless_web_contents_impl.cc
@@ -173,8 +173,7 @@
 void HeadlessWebContentsImpl::RenderFrameCreated(
     content::RenderFrameHost* render_frame_host) {
   if (!mojo_services_.empty()) {
-    render_frame_host->GetRenderViewHost()->AllowBindings(
-        content::BINDINGS_POLICY_HEADLESS);
+    render_frame_host->AllowBindings(content::BINDINGS_POLICY_HEADLESS);
   }
 
   service_manager::InterfaceRegistry* interface_registry =
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
index 1f67eda1..63483c4 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
@@ -13,10 +13,6 @@
 crbug.com/661725 http/tests/security/frameNavigation/xss-ALLOWED-top-navigation-after-postMessage.html [ Failure Timeout ]
 crbug.com/661725 virtual/mojo-loading/http/tests/security/frameNavigation/xss-ALLOWED-top-navigation-after-postMessage.html [ Failure Timeout ]
 
-# https://crbug.com/611838 - regression when geolocation tests use JS mock.
-crbug.com/611838 http/tests/security/powerfulFeatureRestrictions/geolocation-on-secure-origin-in-insecure-origin.html [ Timeout ]
-crbug.com/611838 virtual/mojo-loading/http/tests/security/powerfulFeatureRestrictions/geolocation-on-secure-origin-in-insecure-origin.html [ Timeout ]
-
 # https://crbug.com/582245 - no exception, b/c BindingSecurity::shouldAllowAccessTo exits early when |!target|.
 crbug.com/582245 http/tests/security/xss-DENIED-getSVGDocument-iframe.html [ Failure ]
 crbug.com/582245 http/tests/security/xss-DENIED-getSVGDocument-object.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-execution-contexts-events.html b/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-execution-contexts-events.html
index 5d14ffd..f79329b8 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-execution-contexts-events.html
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-execution-contexts-events.html
@@ -19,6 +19,7 @@
     frame.id = "crafted-iframe";
     document.body.appendChild(frame);
     frame.contentDocument.write("<div>crafted</div>");
+    frame.contentDocument.close();
 }
 
 function test()
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_copier.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_copier.py
index 61d064148..f5d25a40 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_copier.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_copier.py
@@ -79,6 +79,7 @@
                 self.filesystem.basename(self.source_repo_path)))
         self.import_in_place = (self.source_repo_path == self.destination_directory)
         self.dir_above_repo = self.filesystem.dirname(self.source_repo_path)
+        self.is_wpt = self.filesystem.basename(self.source_repo_path) == 'wpt'
 
         self.import_list = []
 
@@ -176,33 +177,46 @@
                     continue
 
                 if 'reference' in test_info.keys():
-                    test_basename = self.filesystem.basename(test_info['test'])
-                    # Add the ref file, following WebKit style.
-                    # FIXME: Ideally we'd support reading the metadata
-                    # directly rather than relying on a naming convention.
-                    # Using a naming convention creates duplicate copies of the
-                    # reference files (http://crrev.com/268729).
-                    ref_file = self.filesystem.splitext(test_basename)[0] + '-expected'
-                    # Make sure to use the extension from the *reference*, not
-                    # from the test, because at least flexbox tests use XHTML
-                    # references but HTML tests.
-                    ref_file += self.filesystem.splitext(test_info['reference'])[1]
-
-                    if not self.filesystem.exists(test_info['reference']):
+                    ref_path_full = test_info['reference']
+                    if not self.filesystem.exists(ref_path_full):
                         _log.warning('Skipping: %s', path_full)
-                        _log.warning('  Reason: Ref file "%s" was not found.', ref_file)
+                        _log.warning('  Reason: Ref file "%s" was not found.', ref_path_full)
                         continue
 
-                    if self.path_too_long(path_full.replace(filename, ref_file)):
-                        _log.warning('Skipping: %s', path_full)
-                        _log.warning('  Reason: Ref file path length would be too long: %s.', ref_file)
-                        _log.warning('  Max length %d; see http://crbug.com/609871.', MAX_PATH_LENGTH)
-                        continue
+                    if self.is_wpt:
+                        if self.path_too_long(ref_path_full):
+                            _log.warning('Skipping: %s', path_full)
+                            _log.warning('  Reason: Ref file path length would be too long: %s.', ref_path_full)
+                            _log.warning('  Max length %d; see http://crbug.com/609871.', MAX_PATH_LENGTH)
+                            continue
+                        # For WPT, we don't need to rename the reference file to
+                        # WebKit style name.
+                        # We don't ask to copy ref_path_full here because this
+                        # filesystem walk will find the reference file, and copy
+                        # it as a non-test file.
+                    else:
+                        test_basename = self.filesystem.basename(test_info['test'])
+                        # Add the ref file, following WebKit style.
+                        # FIXME: Ideally we'd support reading the metadata
+                        # directly rather than relying on a naming convention.
+                        # Using a naming convention creates duplicate copies of the
+                        # reference files (http://crrev.com/268729).
+                        ref_file = self.filesystem.splitext(test_basename)[0] + '-expected'
+                        # Make sure to use the extension from the *reference*, not
+                        # from the test, because at least flexbox tests use XHTML
+                        # references but HTML tests.
+                        ref_file += self.filesystem.splitext(ref_path_full)[1]
+
+                        if self.path_too_long(path_full.replace(filename, ref_file)):
+                            _log.warning('Skipping: %s', path_full)
+                            _log.warning('  Reason: Ref file path length would be too long: %s.', ref_file)
+                            _log.warning('  Max length %d; see http://crbug.com/609871.', MAX_PATH_LENGTH)
+                            continue
+                        copy_list.append({'src': test_info['reference'], 'dest': ref_file,
+                                          'reference_support_info': test_info['reference_support_info']})
 
                     reftests += 1
                     total_tests += 1
-                    copy_list.append({'src': test_info['reference'], 'dest': ref_file,
-                                      'reference_support_info': test_info['reference_support_info']})
                     copy_list.append({'src': test_info['test'], 'dest': filename})
 
                 elif 'jstest' in test_info.keys():
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_copier_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_copier_unittest.py
index 8b47d958..a0048c49 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_copier_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_copier_unittest.py
@@ -139,7 +139,7 @@
         self.assertEqual(copier.import_list, [])
         self.assertLog([
             'WARNING: Skipping: /blink/w3c/dir1/my-ref-test.html\n',
-            'WARNING:   Reason: Ref file "my-ref-test-expected.html" was not found.\n'
+            'WARNING:   Reason: Ref file "/blink/w3c/dir1/not-here.html" was not found.\n'
         ])
 
     def test_files_with_long_path_are_skipped(self):
diff --git a/third_party/closure_compiler/externs/file_manager_private.js b/third_party/closure_compiler/externs/file_manager_private.js
index 5615d14..938d251d 100644
--- a/third_party/closure_compiler/externs/file_manager_private.js
+++ b/third_party/closure_compiler/externs/file_manager_private.js
@@ -510,7 +510,7 @@
     parentEntry, name, callback) {};
 
 /**
- * Changes the zoom factor of the Files.app. |operation| Zooming mode.
+ * Changes the zoom factor of the Files app. |operation| Zooming mode.
  * @param {string} operation
  */
 chrome.fileManagerPrivate.zoom = function(operation) {};
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 32d7f49e..fb7390a 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -19028,7 +19028,7 @@
   <owner>joshwoodward@google.com</owner>
   <summary>
     Chrome OS File Browser: number of saved folder shorcuts. This is recorded
-    when Files.app is launched.
+    when the Files app is launched.
   </summary>
 </histogram>
 
@@ -19254,7 +19254,7 @@
   <owner>joshwoodward@google.com</owner>
   <summary>
     File types that were tried to be viewed through browser. This is recorded
-    when the user tries to view a file from Files.app.
+    when the user tries to view a file from the Files app.
   </summary>
 </histogram>
 
@@ -63265,6 +63265,17 @@
   </summary>
 </histogram>
 
+<histogram name="SiteEngagementService.EngagementScore.IsZero" enum="Boolean">
+  <owner>charleszhao@chromium.org</owner>
+  <owner>dominickn@chromium.org</owner>
+  <owner>kcarattini@chromium.org</owner>
+  <summary>
+    The distribution of zero versus non-zero engagement scores accumulated by
+    the user, recorded at the same time as
+    SiteEngagementService.EngagementScore.
+  </summary>
+</histogram>
+
 <histogram name="SiteEngagementService.EngagementScoreBucket" units="%">
   <owner>calamity@chromium.org</owner>
   <summary>
diff --git a/ui/file_manager/audio_player/js/metadata_worker.js b/ui/file_manager/audio_player/js/metadata_worker.js
index 26af2a1..dec52ec 100644
--- a/ui/file_manager/audio_player/js/metadata_worker.js
+++ b/ui/file_manager/audio_player/js/metadata_worker.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Load the worker script of Files.app.
+// Load the worker script of the Files app.
 importScripts(
     'chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/' +
     'foreground/js/metadata/metadata_dispatcher.js');
diff --git a/ui/file_manager/externs/es6_workaround.js b/ui/file_manager/externs/es6_workaround.js
index 53f5ec8..bc88a1c 100644
--- a/ui/file_manager/externs/es6_workaround.js
+++ b/ui/file_manager/externs/es6_workaround.js
@@ -3,9 +3,9 @@
 // found in the LICENSE file.
 
 /**
-* @fileoverview Partial definitions for ECMAScript 6. To compile Files.app, some
-*     definitions are defined in this file. They should be removed once they are
-*     ready in closure compiler by default.
+* @fileoverview Partial definitions for ECMAScript 6. To compile the Files app,
+*     some definitions are defined in this file. They should be removed once
+*     they are ready in closure compiler by default.
 * @externs
 */
 
diff --git a/ui/file_manager/file_manager/background/js/background.js b/ui/file_manager/file_manager/background/js/background.js
index fcdafc05..06a77b4 100644
--- a/ui/file_manager/file_manager/background/js/background.js
+++ b/ui/file_manager/file_manager/background/js/background.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 /**
- * Type of a Files.app's instance launch.
+ * Type of a Files app's instance launch.
  * @enum {number}
  */
 var LaunchType = {
@@ -364,7 +364,7 @@
           continue;
 
         // The isFocused() method should always be available, but in case
-        // Files.app's failed on some error, wrap it with try catch.
+        // the Files app has failed on some error, wrap it with try catch.
         try {
           if (window.background.appWindows[key].contentWindow.isFocused()) {
             if (opt_callback)
@@ -453,7 +453,7 @@
   };
 
   // Every other action opens a Files app window.
-  // For mounted devices just focus any Files.app window. The mounted
+  // For mounted devices just focus any Files app window. The mounted
   // volume will appear on the navigation list.
   launchFileManager(
       appState,
@@ -581,7 +581,7 @@
 };
 
 /**
- * Handles mounted FSP volumes and fires Files app. This is a quick fix for
+ * Handles mounted FSP volumes and fires the Files app. This is a quick fix for
  * crbug.com/456648.
  * @param {!Object} event Event details.
  * @private
diff --git a/ui/file_manager/file_manager/common/js/util.js b/ui/file_manager/file_manager/common/js/util.js
index d7b0b83..f3e698d6 100644
--- a/ui/file_manager/file_manager/common/js/util.js
+++ b/ui/file_manager/file_manager/common/js/util.js
@@ -361,8 +361,8 @@
 }
 
 /**
- * @return {boolean} True if Files.app is running as an open files or a select
- *     folder dialog. False otherwise.
+ * @return {boolean} True if the Files app is running as an open files or a
+ *     select folder dialog. False otherwise.
  */
 util.runningInBrowser = function() {
   return !window.appID;
@@ -554,7 +554,7 @@
 };
 
 /**
- * Checks, if the Files.app's window is in a full screen mode.
+ * Checks, if the Files app's window is in a full screen mode.
  *
  * @param {chrome.app.window.AppWindow} appWindow App window to be maximized.
  * @return {boolean} True if the full screen mode is enabled.
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js
index f779b039..5a1395f4c 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -793,9 +793,9 @@
    */
   FileManager.prototype.initVolumeManager_ = function() {
     var allowedPaths = this.launchParams_.allowedPaths;
-    // Files.app native implementation create snapshot files for non-native
-    // files. But it does not work for folders (e.g., dialog for loading
-    // unpacked extensions).
+    // The native implementation of the Files app creates snapshot files for
+    // non-native files. But it does not work for folders (e.g., dialog for
+    // loading unpacked extensions).
     if (allowedPaths === AllowedPaths.NATIVE_PATH &&
         !DialogType.isFolderDialog(this.launchParams_.type)) {
       if (this.launchParams_.type == DialogType.SELECT_SAVEAS_FILE) {
@@ -810,7 +810,7 @@
     // even depends on the value of |supportVirtualPath|. If it is
     // VirtualPathSupport.NO_VIRTUAL_PATH, it hides Drive even if Drive is
     // enabled on preference.
-    // In other words, even if Drive is disabled on preference but Files.app
+    // In other words, even if Drive is disabled on preference but the Files app
     // should show Drive when it is re-enabled, then the value should be set to
     // true.
     // Note that the Drive enabling preference change is listened by
@@ -820,9 +820,9 @@
   };
 
   /**
-   * One time initialization of the Files.app's essential UI elements. These
-   * elements will be shown to the user. Only visible elements should be
-   * initialized here. Any heavy operation should be avoided. Files.app's
+   * One time initialization of the essential UI elements in the Files app.
+   * These elements will be shown to the user. Only visible elements should be
+   * initialized here. Any heavy operation should be avoided. The Files app's
    * window is shown at the end of this routine.
    * @private
    */
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
index 4a75d367..cb724ff3 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -968,8 +968,9 @@
   canExecute: function(event, fileManager) {
     // Hides the help menu in modal dialog mode. It does not make much sense
     // because after all, users cannot view the help without closing, and
-    // besides that the help page is about Files.app as an app, not about the
-    // dialog mode itself. It can also lead to hard-to-fix bug crbug.com/339089.
+    // besides that the help page is about the Files app as an app, not about
+    // the dialog mode itself. It can also lead to hard-to-fix bug
+    // crbug.com/339089.
     var hideHelp = DialogType.isModal(fileManager.dialogType);
     event.canExecute = !hideHelp;
     event.command.setHidden(hideHelp);
@@ -1317,7 +1318,7 @@
 });
 
 /**
- * Zoom in to the Files.app.
+ * Zoom in to the Files app.
  * @type {Command}
  */
 CommandHandler.COMMANDS_['zoom-in'] = /** @type {Command} */ ({
@@ -1332,7 +1333,7 @@
 });
 
 /**
- * Zoom out from the Files.app.
+ * Zoom out from the Files app.
  * @type {Command}
  */
 CommandHandler.COMMANDS_['zoom-out'] = /** @type {Command} */ ({
diff --git a/ui/file_manager/file_manager/foreground/js/import_controller.js b/ui/file_manager/file_manager/foreground/js/import_controller.js
index 0653754..6cecbcd 100644
--- a/ui/file_manager/file_manager/foreground/js/import_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/import_controller.js
@@ -1094,7 +1094,7 @@
 
 /**
  * Reveals the import root directory (the parent of all import destinations)
- * in a new Files.app window.
+ * in a new Files app window.
  * E.g. "Chrome OS Cloud backup". Creates it if it doesn't exist.
  *
  * @return {!Promise} Resolves when the folder has been shown.
@@ -1103,7 +1103,7 @@
 
 /**
  * Returns the date-stamped import destination directory in a new
- * Files.app window. E.g. "2015-12-04".
+ * Files app window. E.g. "2015-12-04".
  * Creates it if it doesn't exist.
  *
  * @param {!Date} date The import date
@@ -1114,7 +1114,7 @@
 
 /**
  * Reveals the date-stamped import destination directory in a new
- * Files.app window. E.g. "2015-12-04".
+ * Files app window. E.g. "2015-12-04".
  * Creates it if it doesn't exist.
  *
  * @param {!Date} date The import date
diff --git a/ui/file_manager/file_manager/foreground/js/main_scripts.js b/ui/file_manager/file_manager/foreground/js/main_scripts.js
index 4aac7b0..ee9aea0 100644
--- a/ui/file_manager/file_manager/foreground/js/main_scripts.js
+++ b/ui/file_manager/file_manager/foreground/js/main_scripts.js
@@ -13,7 +13,7 @@
 //
 // 1) You add a new dependency to "whatever.js"
 // 2) You make changes in "whatever.js"
-// 3) Rebuild "resources.pak" and open Files.app
+// 3) Rebuild "resources.pak" and open the Files app
 // 4) You don't see the changes in "whatever.js". Why is that?
 //
 // Because the dependencies are computed at gyp time, the existing build
@@ -75,7 +75,7 @@
 // 'strict mode' is invoked for this scope.
 'use strict';
 
-// error_util.js must be loaded before all other Files.app's scripts.
+// error_util.js must be loaded before all other scripts of the Files app.
 // <include src="../../common/js/error_util.js">
 //
 // <include src="../../common/js/async_util.js">
diff --git a/ui/file_manager/file_manager/manifest.json b/ui/file_manager/file_manager/manifest.json
index 78e3ced..9e3655d 100644
--- a/ui/file_manager/file_manager/manifest.json
+++ b/ui/file_manager/file_manager/manifest.json
@@ -139,14 +139,14 @@
     },
     // The following handlers are used only internally, therefore they do not
     // have any file filter.
-    // Selects the passed file after launching Files.app.
+    // Selects the passed file after launching the Files app.
     {
       "id": "select",
       "default_title": "__MSG_OPEN_ACTION__",
       "default_icon": "common/images/file_types/200/generic.png",
       "file_filters": []
     },
-    // Opens the passed directory after launching Files.app.
+    // Opens the passed directory after launching the Files app.
     {
       "id": "open",
       "default_title": "__MSG_OPEN_ACTION__",
diff --git a/ui/file_manager/file_manager_resources.grd b/ui/file_manager/file_manager_resources.grd
index 6678891..15a4b070 100644
--- a/ui/file_manager/file_manager_resources.grd
+++ b/ui/file_manager/file_manager_resources.grd
@@ -12,7 +12,7 @@
   <release seq="1">
     <includes>
       <include name="IDR_FILEMANAGER_MANIFEST" file="file_manager/manifest.json" type="BINDATA" />
-      <!-- Files.app pages and scripts. -->
+      <!-- The Files app pages and scripts. -->
       <include name="IDR_FILE_MANAGER_MAIN" file="file_manager/main.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
       <include name="IDR_FILE_MANAGER_MAIN_JS" file="file_manager/foreground/js/main_scripts.js" flattenhtml="true" type="BINDATA" />
       <include name="IDR_FILE_MANAGER_ELEMENTS_IMPORTER_JS" file="file_manager/foreground/js/elements_importer.js" type="BINDATA" />