diff --git a/WATCHLISTS b/WATCHLISTS
index 0886534..d5a9169a 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -280,15 +280,6 @@
                   '|third_party/WebKit/LayoutTests/platform/.*/fast/css-grid-layout/' \
                   '|third_party/WebKit/LayoutTests/platform/.*/ietestcenter/css3/grid/'
     },
-    'blink_css_regions': {
-      'filepath': 'third_party/WebKit/Source/core/rendering/.*(Region|FlowThread)' \
-                  '|third_party/WebKit/Source/core/dom/NodeRendering' \
-                  '|third_party/WebKit/Source/core/dom/.*NamedFlow' \
-                  '|third_party/WebKit/Source/core/css/.*Region' \
-                  '|third_party/WebKit/LayoutTests/compositing/regions/' \
-                  '|third_party/WebKit/LayoutTests/fast/regions/' \
-                  '|third_party/WebKit/LayoutTests/platform/chromium.*/fast/regions/'
-    },
     'blink_custom_elements': {
       'filepath': 'third_party/WebKit/Source/core/html/custom/' \
                   '|third_party/WebKit/Source/bindings/core/v8/.*CustomElement',
@@ -306,14 +297,7 @@
       'filepath': 'third_party/WebKit/Source/core/dom/'
     },
     'blink_dom_events': {
-      'filepath': 'third_party/WebKit/Source/core/events/Event\.' \
-                  '|third_party/WebKit/Source/core/events/EventInit' \
-                  '|third_party/WebKit/Source/core/events/EventPath' \
-                  '|third_party/WebKit/Source/core/events/EventListener' \
-                  '|third_party/WebKit/Source/core/events/EventDispatcher' \
-                  '|third_party/WebKit/Source/core/events/EventDispatchMediator' \
-                  '|third_party/WebKit/Source/core/events/EventTarget' \
-                  '|third_party/WebKit/Source/core/events/.*EventContext'
+      'filepath': 'third_party/WebKit/Source/core/dom/events/'
     },
     'blink_events': {
       'filepath': 'third_party/WebKit/Source/core/page/.*Event' \
@@ -536,9 +520,6 @@
       'filepath': 'third_party/WebKit/LayoutTests/external/' \
                   '|third_party/WebKit/Tools/Scripts/webkitpy/w3c/'
     },
-    'blink_web': {
-      'filepath': 'third_party/WebKit/Source/web'
-    },
     'blink_webcomponents': {
       'filepath': 'third_party/WebKit/Source/core/dom/.*Shadow' \
                   '|third_party/WebKit/Source/core/dom/.*Slot' \
@@ -563,7 +544,7 @@
                   '|third_party/WebKit/LayoutTests/external/wpt/workers'
     },
     'blink_wtf': {
-      'filepath': 'third_party/WebKit/Source/wtf',
+      'filepath': 'third_party/WebKit/Source/platform/wtf',
     },
     'blink_xml': {
       'filepath': 'third_party/WebKit/Source/core/xml/'
@@ -1734,7 +1715,6 @@
     'blink_css_grid_layout': ['jfernandez@igalia.com',
                               'rego@igalia.com',
                               'svillar@igalia.com'],
-    'blink_css_regions': ['ChromiumBugTracker@adobe.com'],
     'blink_custom_elements': ['dglazkov+blink@chromium.org',
                               'dominicc+watchlist@chromium.org'],
     'blink_device_orientation': ['mlamouri+watch-blink@chromium.org',
@@ -1866,8 +1846,6 @@
     'blink_vibration': ['mlamouri+watch-blink@chromium.org'],
     'blink_viewport_interaction': ['kenneth.christiansen@gmail.com'],
     'blink_w3ctests': ['blink-reviews-w3ctests@chromium.org'],
-    'blink_web': ['kinuko+watch@chromium.org',
-                  'platform-architecture-syd+reviews-web@chromium.org'],
     'blink_webcomponents': ['dglazkov+blink@chromium.org'],
     'blink_webp': ['jzern@chromium.org',
                    'skal@google.com',
diff --git a/chrome/VERSION b/chrome/VERSION
index 39fb857b..acc2ecd 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=63
 MINOR=0
-BUILD=3223
+BUILD=3224
 PATCH=0
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index e49c01c..7c9941b 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -2436,6 +2436,9 @@
   <message name="IDS_SETTINGS_SITE_SETTINGS_PERMISSIONS" desc="The Permissions header, used to group together permissions, such as Geolocation, on the Site Details page.">
     Permissions
   </message>
+  <message name="IDS_SETTINGS_SITE_SETTINGS_SOURCE_DRM_DISABLED" desc="A label shown when the protected content / protected media identifier permission on the Site Details page is disabled because the user has turned off using unique identifiers to access protected content.">
+    To change this setting, first <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>turn on device identifiers<ph name="END_LINK">&lt;/a&gt;</ph>
+  </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS_SOURCE_KILL_SWITCH" desc="A label shown when a permission on the Site Details page is temporarily blocked for the user's safety.">
     Temporarily blocked to protect your security
   </message>
diff --git a/chrome/browser/installable/installable_manager.cc b/chrome/browser/installable/installable_manager.cc
index 05b3111..03e2f943 100644
--- a/chrome/browser/installable/installable_manager.cc
+++ b/chrome/browser/installable/installable_manager.cc
@@ -284,9 +284,11 @@
 void InstallableManager::Reset() {
   // Prevent any outstanding callbacks to or from this object from being called.
   weak_factory_.InvalidateWeakPtrs();
-  task_queue_.Reset();
   icons_.clear();
-  metrics_->Flush();
+
+  // If we have paused tasks, we are waiting for a service worker.
+  metrics_->Flush(task_queue_.HasPaused());
+  task_queue_.Reset();
 
   metrics_ = base::MakeUnique<InstallableMetrics>();
   manifest_ = base::MakeUnique<ManifestProperty>();
diff --git a/chrome/browser/installable/installable_manager_browsertest.cc b/chrome/browser/installable/installable_manager_browsertest.cc
index dbce2ba..410d53bc 100644
--- a/chrome/browser/installable/installable_manager_browsertest.cc
+++ b/chrome/browser/installable/installable_manager_browsertest.cc
@@ -892,6 +892,64 @@
 }
 
 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
+                       WaitingForServiceWorkerRecordsNonPwa) {
+  base::RunLoop tester_run_loop, sw_run_loop;
+  base::HistogramTester histograms;
+  std::unique_ptr<CallbackTester> tester(
+      new CallbackTester(tester_run_loop.QuitClosure()));
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  auto manager = base::MakeUnique<LazyWorkerInstallableManager>(
+      web_contents, sw_run_loop.QuitClosure());
+
+  manager->RecordMenuOpenHistogram();
+  manager->RecordMenuItemAddToHomescreenHistogram();
+
+  {
+    // Load a URL with no service worker.
+    GURL test_url = embedded_test_server()->GetURL(
+        "/banners/manifest_no_service_worker.html");
+    ui_test_utils::NavigateToURL(browser(), test_url);
+
+    // Kick off fetching the data. This should block on waiting for a worker.
+    manager->GetData(GetWebAppParams(),
+                     base::Bind(&CallbackTester::OnDidFinishInstallableCheck,
+                                base::Unretained(tester.get())));
+    sw_run_loop.Run();
+  }
+
+  manager->RecordMenuOpenHistogram();
+  manager->RecordMenuOpenHistogram();
+  manager->RecordMenuItemAddToHomescreenHistogram();
+
+  // Navigate to force metrics recording.
+  ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
+
+  // Expect to record that we completed the check and found a non-PWA since we
+  // waited until navigation and didn't get a service worker.
+  histograms.ExpectBucketCount(
+      "Webapp.InstallabilityCheckStatus.MenuOpen",
+      static_cast<int>(InstallabilityCheckStatus::NOT_STARTED), 1);
+  histograms.ExpectBucketCount(
+      "Webapp.InstallabilityCheckStatus.MenuOpen",
+      static_cast<int>(
+          InstallabilityCheckStatus::COMPLETE_NON_PROGRESSIVE_WEB_APP),
+      2);
+  histograms.ExpectTotalCount("Webapp.InstallabilityCheckStatus.MenuOpen", 3);
+  histograms.ExpectBucketCount(
+      "Webapp.InstallabilityCheckStatus.MenuItemAddToHomescreen",
+      static_cast<int>(InstallabilityCheckStatus::NOT_STARTED), 1);
+  histograms.ExpectBucketCount(
+      "Webapp.InstallabilityCheckStatus.MenuItemAddToHomescreen",
+      static_cast<int>(
+          InstallabilityCheckStatus::COMPLETE_NON_PROGRESSIVE_WEB_APP),
+      1);
+  histograms.ExpectTotalCount(
+      "Webapp.InstallabilityCheckStatus.MenuItemAddToHomescreen", 2);
+}
+
+IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
                        CheckServiceWorkerErrorIsNotCached) {
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
@@ -970,60 +1028,6 @@
   EXPECT_EQ(NOT_OFFLINE_CAPABLE, tester->error_code());
 }
 
-IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
-                       WaitingForServiceWorkerRecordsNonPwa) {
-  base::RunLoop tester_run_loop, sw_run_loop;
-  base::HistogramTester histograms;
-  std::unique_ptr<CallbackTester> tester(
-      new CallbackTester(tester_run_loop.QuitClosure()));
-
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  auto manager = base::MakeUnique<LazyWorkerInstallableManager>(
-      web_contents, sw_run_loop.QuitClosure());
-
-  manager->RecordMenuOpenHistogram();
-  manager->RecordMenuItemAddToHomescreenHistogram();
-
-  {
-    // Load a URL with no service worker.
-    GURL test_url = embedded_test_server()->GetURL(
-        "/banners/manifest_no_service_worker.html");
-    ui_test_utils::NavigateToURL(browser(), test_url);
-
-    // Kick off fetching the data. This should block on waiting for a worker.
-    manager->GetData(GetWebAppParams(),
-                     base::Bind(&CallbackTester::OnDidFinishInstallableCheck,
-                                base::Unretained(tester.get())));
-    sw_run_loop.Run();
-  }
-
-  manager->RecordMenuOpenHistogram();
-  manager->RecordMenuItemAddToHomescreenHistogram();
-  manager->RecordMenuItemAddToHomescreenHistogram();
-
-  // Navigate to force metrics recording.
-  ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
-
-  // Expect to record that we didn't finish the check since we waited until
-  // navigation and didn't get a service worker.
-  histograms.ExpectBucketCount(
-      "Webapp.InstallabilityCheckStatus.MenuOpen",
-      static_cast<int>(InstallabilityCheckStatus::NOT_STARTED), 1);
-  histograms.ExpectBucketCount(
-      "Webapp.InstallabilityCheckStatus.MenuOpen",
-      static_cast<int>(InstallabilityCheckStatus::IN_PROGRESS_UNKNOWN), 1);
-  histograms.ExpectTotalCount("Webapp.InstallabilityCheckStatus.MenuOpen", 2);
-  histograms.ExpectBucketCount(
-      "Webapp.InstallabilityCheckStatus.MenuItemAddToHomescreen",
-      static_cast<int>(InstallabilityCheckStatus::NOT_STARTED), 1);
-  histograms.ExpectBucketCount(
-      "Webapp.InstallabilityCheckStatus.MenuItemAddToHomescreen",
-      static_cast<int>(InstallabilityCheckStatus::IN_PROGRESS_UNKNOWN), 2);
-  histograms.ExpectTotalCount(
-      "Webapp.InstallabilityCheckStatus.MenuItemAddToHomescreen", 3);
-}
-
 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckDataUrlIcon) {
   // Verify that InstallableManager can handle data URL icons.
   base::RunLoop run_loop;
diff --git a/chrome/browser/installable/installable_metrics.cc b/chrome/browser/installable/installable_metrics.cc
index 51dcfd3..a6afb87 100644
--- a/chrome/browser/installable/installable_metrics.cc
+++ b/chrome/browser/installable/installable_metrics.cc
@@ -72,11 +72,16 @@
     }
   }
 
-  void Flush() override {
-    WriteMetricsAndResetCounts(
+  void Flush(bool waiting_for_service_worker) override {
+    InstallabilityCheckStatus status =
         started_ ? InstallabilityCheckStatus::IN_PROGRESS_UNKNOWN
-                 : InstallabilityCheckStatus::NOT_STARTED,
-        AddToHomescreenTimeoutStatus::TIMEOUT_MANIFEST_FETCH_UNKNOWN,
+                 : InstallabilityCheckStatus::NOT_STARTED;
+
+    if (waiting_for_service_worker)
+      status = InstallabilityCheckStatus::COMPLETE_NON_PROGRESSIVE_WEB_APP;
+
+    WriteMetricsAndResetCounts(
+        status, AddToHomescreenTimeoutStatus::TIMEOUT_MANIFEST_FETCH_UNKNOWN,
         AddToHomescreenTimeoutStatus::TIMEOUT_INSTALLABILITY_CHECK_UNKNOWN);
   }
 
@@ -175,7 +180,7 @@
   ~DirectRecorder() override {}
 
   void Resolve(bool check_passed) override {}
-  void Flush() override {}
+  void Flush(bool waiting_for_service_worker) override {}
   void Start() override {}
   void RecordMenuOpen() override {
     WriteMenuOpenHistogram(installability_check_status_, 1);
@@ -239,6 +244,7 @@
   recorder_->Start();
 }
 
-void InstallableMetrics::Flush() {
-  recorder_->Flush();
+void InstallableMetrics::Flush(bool waiting_for_service_worker) {
+  recorder_->Flush(waiting_for_service_worker);
+  recorder_ = base::MakeUnique<AccumulatingRecorder>();
 }
diff --git a/chrome/browser/installable/installable_metrics.h b/chrome/browser/installable/installable_metrics.h
index fe20094..5f8c02b 100644
--- a/chrome/browser/installable/installable_metrics.h
+++ b/chrome/browser/installable/installable_metrics.h
@@ -37,7 +37,7 @@
    public:
     virtual ~Recorder() {}
     virtual void Resolve(bool check_passed) {}
-    virtual void Flush() {}
+    virtual void Flush(bool has_paused) {}
 
     virtual void RecordMenuOpen() = 0;
     virtual void RecordMenuItemAddToHomescreen() = 0;
@@ -74,7 +74,7 @@
   void Resolve(bool check_passed);
 
   // Called to save any queued metrics.
-  void Flush();
+  void Flush(bool has_paused);
 
   // Called to indicate that the InstallableManager has started working on the
   // current page.
diff --git a/chrome/browser/installable/installable_task_queue.cc b/chrome/browser/installable/installable_task_queue.cc
index 3e6c229c..ddbdbcb3 100644
--- a/chrome/browser/installable/installable_task_queue.cc
+++ b/chrome/browser/installable/installable_task_queue.cc
@@ -11,9 +11,9 @@
   tasks_.push_back(task);
 }
 
-void InstallableTaskQueue::Reset() {
-  tasks_.clear();
-  paused_tasks_.clear();
+void InstallableTaskQueue::PauseCurrent() {
+  paused_tasks_.push_back(Current());
+  Next();
 }
 
 void InstallableTaskQueue::UnpauseAll() {
@@ -23,21 +23,25 @@
   paused_tasks_.clear();
 }
 
+bool InstallableTaskQueue::HasCurrent() const {
+  return !tasks_.empty();
+}
+
+bool InstallableTaskQueue::HasPaused() const {
+  return !paused_tasks_.empty();
+}
+
 InstallableTask& InstallableTaskQueue::Current() {
   DCHECK(!tasks_.empty());
   return tasks_[0];
 }
 
-void InstallableTaskQueue::PauseCurrent() {
-  paused_tasks_.push_back(Current());
-  Next();
-}
-
 void InstallableTaskQueue::Next() {
   DCHECK(!tasks_.empty());
   tasks_.erase(tasks_.begin());
 }
 
-bool InstallableTaskQueue::HasCurrent() const {
-  return !tasks_.empty();
+void InstallableTaskQueue::Reset() {
+  tasks_.clear();
+  paused_tasks_.clear();
 }
diff --git a/chrome/browser/installable/installable_task_queue.h b/chrome/browser/installable/installable_task_queue.h
index f0737d8..39ea7b0 100644
--- a/chrome/browser/installable/installable_task_queue.h
+++ b/chrome/browser/installable/installable_task_queue.h
@@ -31,6 +31,9 @@
   // Reports whether there are any tasks in the main list.
   bool HasCurrent() const;
 
+  // Reports whether there are any tasks in the paused list.
+  bool HasPaused() const;
+
   // Returns the currently active task.
   InstallableTask& Current();
 
diff --git a/chrome/browser/resources/settings/route.js b/chrome/browser/resources/settings/route.js
index d4dee7d..20b8d4b 100644
--- a/chrome/browser/resources/settings/route.js
+++ b/chrome/browser/resources/settings/route.js
@@ -160,6 +160,15 @@
     }
 
     /**
+     * Returns the absolute path string for this Route, assuming this function
+     * has been called from within chrome://settings.
+     * @return {string}
+     */
+    getAbsolutePath() {
+      return window.location.origin + this.path;
+    }
+
+    /**
      * Returns true if this route matches or is an ancestor of the parameter.
      * @param {!settings.Route} route
      * @return {boolean}
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index 6e5c02a..96213672 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -900,8 +900,7 @@
                  type="chrome_html" />
       <structure name="IDR_SETTINGS_CONSTANTS_JS"
                  file="site_settings/constants.js"
-                 type="chrome_html"
-                 preprocess="true" />
+                 type="chrome_html" />
       <structure name="IDR_SETTINGS_CONSTANTS_HTML"
                  file="site_settings/constants.html"
                  type="chrome_html" />
@@ -910,7 +909,8 @@
                  type="chrome_html" />
       <structure name="IDR_SETTINGS_SITE_SETTINGS_PAGE_JS"
                  file="site_settings_page/site_settings_page.js"
-                 type="chrome_html" />
+                 type="chrome_html"
+                 preprocess="true" />
       <structure name="IDR_SETTINGS_SITE_SETTINGS_PREFS_BROWSER_PROXY_HTML"
                  file="site_settings/site_settings_prefs_browser_proxy.html"
                  type="chrome_html" />
@@ -919,16 +919,19 @@
                  type="chrome_html" />
       <structure name="IDR_SETTINGS_SITE_DETAILS_HTML"
                  file="site_settings/site_details.html"
-                 type="chrome_html" />
+                 type="chrome_html"
+                 preprocess="true" />
       <structure name="IDR_SETTINGS_SITE_DETAILS_JS"
                  file="site_settings/site_details.js"
-                 type="chrome_html" />
+                 type="chrome_html"
+                 preprocess="true" />
       <structure name="IDR_SETTINGS_SITE_DETAILS_PERMISSION_HTML"
                  file="site_settings/site_details_permission.html"
                  type="chrome_html" />
       <structure name="IDR_SETTINGS_SITE_DETAILS_PERMISSION_JS"
                  file="site_settings/site_details_permission.js"
-                 type="chrome_html" />
+                 type="chrome_html"
+                 preprocess="true" />
       <structure name="IDR_SETTINGS_SEARCH_ENGINES_PAGE_SEARCH_ENGINE_DIALOG_JS"
                  file="search_engines_page/search_engine_dialog.js"
                  type="chrome_html" />
diff --git a/chrome/browser/resources/settings/site_settings/compiled_resources2.gyp b/chrome/browser/resources/settings/site_settings/compiled_resources2.gyp
index 59d25c5..40d66f5a 100644
--- a/chrome/browser/resources/settings/site_settings/compiled_resources2.gyp
+++ b/chrome/browser/resources/settings/site_settings/compiled_resources2.gyp
@@ -160,6 +160,7 @@
       'dependencies': [
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:web_ui_listener_behavior',
         'constants',
diff --git a/chrome/browser/resources/settings/site_settings/constants.js b/chrome/browser/resources/settings/site_settings/constants.js
index 163fc33..d08a61b1 100644
--- a/chrome/browser/resources/settings/site_settings/constants.js
+++ b/chrome/browser/resources/settings/site_settings/constants.js
@@ -30,9 +30,7 @@
   MIDI_DEVICES: 'midi-sysex',
   USB_DEVICES: 'usb-chooser-data',
   ZOOM_LEVELS: 'zoom-levels',
-  // <if expr="chromeos">
   PROTECTED_CONTENT: 'protectedContent',
-  // </if>
   ADS: 'ads',
 };
 
@@ -58,13 +56,16 @@
  * @enum {string}
  */
 settings.SiteSettingSource = {
+  DEFAULT: 'default',
+  // This source is for the Protected Media Identifier / Protected Content
+  // content setting only, which is only available on ChromeOS.
+  DRM_DISABLED: 'drm-disabled',
   EMBARGO: 'embargo',
   EXTENSION: 'extension',
   INSECURE_ORIGIN: 'insecure-origin',
   KILL_SWITCH: 'kill-switch',
   POLICY: 'policy',
   PREFERENCE: 'preference',
-  DEFAULT: 'default',
 };
 
 /**
diff --git a/chrome/browser/resources/settings/site_settings/site_details.html b/chrome/browser/resources/settings/site_settings/site_details.html
index 204d19ea..9f69e21 100644
--- a/chrome/browser/resources/settings/site_settings/site_details.html
+++ b/chrome/browser/resources/settings/site_settings/site_details.html
@@ -131,6 +131,13 @@
           icon="cr:extension" id="unsandboxedPlugins"
           label="$i18n{siteSettingsUnsandboxedPlugins}">
       </site-details-permission>
+<if expr="chromeos">
+      <site-details-permission
+          category="{{ContentSettingsTypes.PROTECTED_CONTENT}}"
+          icon="settings:security" id="protectedContent"
+          label="$i18n{siteSettingsProtectedContent}">
+      </site-details-permission>
+</if>
     </div>
 
     <div id="clearAndReset" class="settings-box"
diff --git a/chrome/browser/resources/settings/site_settings/site_details.js b/chrome/browser/resources/settings/site_settings/site_details.js
index b45b86d5..62d59c4 100644
--- a/chrome/browser/resources/settings/site_settings/site_details.js
+++ b/chrome/browser/resources/settings/site_settings/site_details.js
@@ -74,6 +74,11 @@
     this.addWebUIListener(
         'contentSettingSitePermissionChanged',
         this.onPermissionChanged_.bind(this));
+
+    // <if expr="chromeos">
+    this.addWebUIListener(
+        'prefEnableDrmChanged', this.prefEnableDrmChanged_.bind(this));
+    // </if>
   },
 
   /** @override */
@@ -108,7 +113,6 @@
         this.updatePermissions_(this.getCategoryList_());
       }
     });
-
   },
 
   /**
@@ -132,6 +136,12 @@
       this.updatePermissions_([category]);
   },
 
+  // <if expr="chromeos">
+  prefEnableDrmChanged_: function() {
+    this.updatePermissions_([settings.ContentSettingsTypes.PROTECTED_CONTENT]);
+  },
+  // </if>
+
   /**
    * Retrieves the permissions listed in |categoryList| from the backend for
    * |this.origin|.
@@ -222,10 +232,11 @@
    * @private
    */
   getCategoryList_: function() {
-    return Array.prototype.map.call(
-        this.root.querySelectorAll('site-details-permission'), (element) => {
-          return element.category;
-        });
+    var categoryList = [];
+    this.root.querySelectorAll('site-details-permission').forEach((element) => {
+      categoryList.push(element.category);
+    });
+    return categoryList;
   },
 
   /**
diff --git a/chrome/browser/resources/settings/site_settings/site_details_permission.html b/chrome/browser/resources/settings/site_settings/site_details_permission.html
index b356f18..c55f571 100644
--- a/chrome/browser/resources/settings/site_settings/site_details_permission.html
+++ b/chrome/browser/resources/settings/site_settings/site_details_permission.html
@@ -1,6 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="../settings_shared_css.html">
@@ -13,7 +14,8 @@
   <template>
     <style include="settings-shared md-select"></style>
     <div id="details">
-      <div id="permissionItem" class="list-item">
+      <div id="permissionItem"
+          class$="list-item [[permissionSourceStringClass_(site.source)]]">
         <div>
           <iron-icon icon="[[icon]]">
           </iron-icon>
@@ -21,8 +23,8 @@
         <div class="middle" id="permissionHeader">
           [[label]]
           <div class="secondary"
-              hidden$="[[!hasPermissionSourceString_(site.source)]]">
-            [[permissionSourceString_(
+              hidden$="[[!hasPermissionSourceString_(site.source)]]"
+              inner-h-t-m-l="[[permissionSourceString_(
                 site.source,
                 '$i18nPolymer{siteSettingsSourceEmbargo}',
                 '$i18nPolymer{siteSettingsSourceInsecureOrigin}',
@@ -32,13 +34,14 @@
                 '$i18nPolymer{siteSettingsSourceExtensionAsk}',
                 '$i18nPolymer{siteSettingsSourcePolicyAllow}',
                 '$i18nPolymer{siteSettingsSourcePolicyBlock}',
-                '$i18nPolymer{siteSettingsSourcePolicyAsk}')]]
+                '$i18nPolymer{siteSettingsSourcePolicyAsk}')]]">
           </div>
         </div>
         <div class="md-select-wrapper">
           <select id="permission" class="md-select"
               aria-labelledby="permissionHeader"
-              on-change="onPermissionSelectionChange_">
+              on-change="onPermissionSelectionChange_"
+              disabled$="[[!isPermissionUserControlled_(site.source)]]">
             <option id="default" value$="[[ContentSetting.DEFAULT]]">
               [[defaultSettingString_(
                   defaultSetting_,
diff --git a/chrome/browser/resources/settings/site_settings/site_details_permission.js b/chrome/browser/resources/settings/site_settings/site_details_permission.js
index 26d8fd7..27c34c2 100644
--- a/chrome/browser/resources/settings/site_settings/site_details_permission.js
+++ b/chrome/browser/resources/settings/site_settings/site_details_permission.js
@@ -10,7 +10,7 @@
 Polymer({
   is: 'site-details-permission',
 
-  behaviors: [SiteSettingsBehavior, WebUIListenerBehavior],
+  behaviors: [SiteSettingsBehavior, WebUIListenerBehavior, I18nBehavior],
 
   properties: {
     /**
@@ -51,18 +51,6 @@
       this.$.permission.value = site.setting;
     }
 
-    // Handle non-default sources.
-    if (site.source == settings.SiteSettingSource.DEFAULT ||
-        site.source == settings.SiteSettingSource.PREFERENCE) {
-      this.$.permissionItem.classList.remove('two-line');
-      this.$.permission.disabled = false;
-    } else {
-      this.$.permissionItem.classList.add('two-line');
-      // Users are able to override embargo, so leave enabled in that case.
-      this.$.permission.disabled =
-          site.source != settings.SiteSettingSource.EMBARGO;
-    }
-
     if (this.isNonDefaultAsk_(site.setting, site.source)) {
       assert(
           this.$.permission.disabled,
@@ -143,6 +131,28 @@
   },
 
   /**
+   * Checks if there's a permission source string to display, and returns the
+   * class name to apply to permissions if so.
+   * @return {string} CSS class applied when there is an additional description
+   *     string.
+   * @private
+   */
+  permissionSourceStringClass_: function(source) {
+    return this.hasPermissionSourceString_(source) ? 'two-line' : '';
+  },
+
+  /**
+   * Returns true if this permission's source is controlled by the user.
+   * @return {boolean}
+   * @private
+   */
+  isPermissionUserControlled_: function(source) {
+    // Users are able override embargo.
+    return !this.hasPermissionSourceString_(source) ||
+        source == settings.SiteSettingSource.EMBARGO;
+  },
+
+  /**
    * Returns true if the permission is set to a non-default 'ask'. Currently,
    * this only gets called when |this.site| is updated.
    * @param {!settings.ContentSetting} setting The setting of the permission.
@@ -194,7 +204,18 @@
     policyStrings[settings.ContentSetting.BLOCK] = policyBlockString;
     policyStrings[settings.ContentSetting.ASK] = policyAskString;
 
-    if (source == settings.SiteSettingSource.EMBARGO) {
+    if (source == settings.SiteSettingSource.DRM_DISABLED) {
+      assert(
+          settings.ContentSetting.BLOCK == this.site.setting,
+          'If DRM is disabled, Protected Content must be blocked.');
+      assert(
+          settings.ContentSettingsTypes.PROTECTED_CONTENT == this.category,
+          'The DRM disabled source only applies to Protected Content.');
+      return this.i18nAdvanced('siteSettingsSourceDrmDisabled', {
+        substitutions:
+            [settings.routes.SITE_SETTINGS_PROTECTED_CONTENT.getAbsolutePath()]
+      });
+    } else if (source == settings.SiteSettingSource.EMBARGO) {
       assert(
           settings.ContentSetting.BLOCK == this.site.setting,
           'Embargo is only used to block permissions.');
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.js b/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
index e41cefd..e3354d0 100644
--- a/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
+++ b/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
@@ -115,11 +115,16 @@
       if (key == settings.ContentSettingsTypes.USB_DEVICES ||
           key == settings.ContentSettingsTypes.ZOOM_LEVELS)
         continue;
-      // Some values are not available (and will DCHECK) in guest mode.
+      // Protocol handlers are not available (and will DCHECK) in guest mode.
       if (this.isGuest_ &&
           key == settings.ContentSettingsTypes.PROTOCOL_HANDLERS) {
         continue;
       }
+      // Similarly, protected content is only available in CrOS.
+      // <if expr="not chromeos">
+      if (key == settings.ContentSettingsTypes.PROTECTED_CONTENT)
+        continue;
+      // </if>
       this.updateDefaultValueLabel_(key);
     }
 
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 83a6618..72d654b 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -1875,6 +1875,8 @@
     {"siteSettingsSourcePolicyBlock",
      IDS_PAGE_INFO_PERMISSION_BLOCKED_BY_POLICY},
     {"siteSettingsSourcePolicyAsk", IDS_PAGE_INFO_PERMISSION_ASK_BY_POLICY},
+    {"siteSettingsSourceDrmDisabled",
+     IDS_SETTINGS_SITE_SETTINGS_SOURCE_DRM_DISABLED},
     {"siteSettingsSourceEmbargo",
      IDS_PAGE_INFO_PERMISSION_AUTOMATICALLY_BLOCKED},
     {"siteSettingsSourceInsecureOrigin",
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chrome/browser/ui/webui/settings/site_settings_handler.cc
index ac50ebe..553f2d5 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -49,6 +49,9 @@
 #include "ui/base/text/bytes_formatting.h"
 
 #if defined(OS_CHROMEOS)
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
 #endif
 
@@ -208,12 +211,24 @@
           ->AddZoomLevelChangedCallback(
               base::Bind(&SiteSettingsHandler::OnZoomLevelChanged,
                          base::Unretained(this)));
+
+#if defined(OS_CHROMEOS)
+  pref_change_registrar_ = base::MakeUnique<PrefChangeRegistrar>();
+  pref_change_registrar_->Init(profile_->GetPrefs());
+  pref_change_registrar_->Add(
+      prefs::kEnableDRM,
+      base::Bind(&SiteSettingsHandler::OnPrefEnableDrmChanged,
+                 base::Unretained(this)));
+#endif
 }
 
 void SiteSettingsHandler::OnJavascriptDisallowed() {
   observer_.RemoveAll();
   notification_registrar_.RemoveAll();
   host_zoom_map_subscription_.reset();
+#if defined(OS_CHROMEOS)
+  pref_change_registrar_->Remove(prefs::kEnableDRM);
+#endif
 }
 
 void SiteSettingsHandler::OnGetUsageInfo(
@@ -239,6 +254,13 @@
   }
 }
 
+#if defined(OS_CHROMEOS)
+void SiteSettingsHandler::OnPrefEnableDrmChanged() {
+  CallJavascriptFunction("cr.webUIListenerCallback",
+                         base::Value("prefEnableDrmChanged"));
+}
+#endif
+
 void SiteSettingsHandler::OnContentSettingChanged(
     const ContentSettingsPattern& primary_pattern,
     const ContentSettingsPattern& secondary_pattern,
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.h b/chrome/browser/ui/webui/settings/site_settings_handler.h
index 17e71446..460e51d 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.h
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.h
@@ -20,6 +20,10 @@
 class HostContentSettingsMap;
 class Profile;
 
+#if defined(OS_CHROMEOS)
+class PrefChangeRegistrar;
+#endif
+
 namespace base {
 class ListValue;
 }
@@ -43,6 +47,11 @@
   void OnGetUsageInfo(const storage::UsageInfoEntries& entries);
   void OnUsageInfoCleared(storage::QuotaStatusCode code);
 
+#if defined(OS_CHROMEOS)
+  // Alert the Javascript that the |kEnableDRM| pref has changed.
+  void OnPrefEnableDrmChanged();
+#endif
+
   // content_settings::Observer:
   void OnContentSettingChanged(const ContentSettingsPattern& primary_pattern,
                                const ContentSettingsPattern& secondary_pattern,
@@ -142,6 +151,11 @@
   // Change observer for content settings.
   ScopedObserver<HostContentSettingsMap, content_settings::Observer> observer_;
 
+#if defined(OS_CHROMEOS)
+  // Change observer for prefs.
+  std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
+#endif
+
   DISALLOW_COPY_AND_ASSIGN(SiteSettingsHandler);
 };
 
diff --git a/chrome/browser/ui/webui/site_settings_helper.cc b/chrome/browser/ui/webui/site_settings_helper.cc
index 01dc403..b776b0b 100644
--- a/chrome/browser/ui/webui/site_settings_helper.cc
+++ b/chrome/browser/ui/webui/site_settings_helper.cc
@@ -80,6 +80,7 @@
 
 const SiteSettingSourceStringMapping kSiteSettingSourceStringMapping[] = {
     {SiteSettingSource::kDefault, "default"},
+    {SiteSettingSource::kDrmDisabled, "drm-disabled"},
     {SiteSettingSource::kEmbargo, "embargo"},
     {SiteSettingSource::kExtension, "extension"},
     {SiteSettingSource::kInsecureOrigin, "insecure-origin"},
@@ -98,14 +99,17 @@
 //    2. Insecure origins (some permissions are denied to insecure origins).
 //    3. Enterprise policy.
 //    4. Extensions.
-//    5. User-set per-origin setting.
-//    6. Embargo.
-//    7. User-set patterns.
-//    8. User-set global default for a ContentSettingsType.
-//    9. Chrome's built-in default.
+//    5. DRM disabled (for CrOS's Protected Content ContentSettingsType only).
+//    6. User-set per-origin setting.
+//    7. Embargo.
+//    8. User-set patterns.
+//    9. User-set global default for a ContentSettingsType.
+//   10. Chrome's built-in default.
 SiteSettingSource CalculateSiteSettingSource(
+    Profile* profile,
+    const ContentSettingsType content_type,
     const content_settings::SettingInfo& info,
-    PermissionStatusSource permission_status_source) {
+    const PermissionStatusSource permission_status_source) {
   if (permission_status_source == PermissionStatusSource::KILL_SWITCH)
     return SiteSettingSource::kKillSwitch;  // Source #1.
 
@@ -120,6 +124,12 @@
   if (info.source == content_settings::SETTING_SOURCE_EXTENSION)
     return SiteSettingSource::kExtension;  // Source #4.
 
+  // Protected Content will be blocked if the |kEnableDRM| pref is off.
+  if (content_type == CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER &&
+      !profile->GetPrefs()->GetBoolean(prefs::kEnableDRM)) {
+    return SiteSettingSource::kDrmDisabled;  // Source #5.
+  }
+
   DCHECK_NE(content_settings::SETTING_SOURCE_NONE, info.source);
   if (info.source == content_settings::SETTING_SOURCE_USER) {
     if (permission_status_source ==
@@ -127,13 +137,13 @@
         permission_status_source ==
             PermissionStatusSource::MULTIPLE_DISMISSALS ||
         permission_status_source == PermissionStatusSource::MULTIPLE_IGNORES) {
-      return SiteSettingSource::kEmbargo;  // Source #6.
+      return SiteSettingSource::kEmbargo;  // Source #7.
     }
     if (info.primary_pattern == ContentSettingsPattern::Wildcard() &&
         info.secondary_pattern == ContentSettingsPattern::Wildcard()) {
-      return SiteSettingSource::kDefault;  // Source #8, #9.
+      return SiteSettingSource::kDefault;  // Source #9, #10.
     }
-    // Source #5, #7. When #5 is the source, |permission_status_source| won't
+    // Source #6, #8. When #6 is the source, |permission_status_source| won't
     // be set to any of the source #6 enum values, as PermissionManager is
     // aware of the difference between these two sources internally. The
     // subtlety here should go away when PermissionManager can handle all
@@ -423,8 +433,9 @@
 
   // Retrieve the source of the content setting.
   *source_string = SiteSettingSourceToString(
-      CalculateSiteSettingSource(info, result.source));
+      CalculateSiteSettingSource(profile, content_type, info, result.source));
   *display_name = GetDisplayNameForGURL(origin, extension_registry);
+
   return result.content_setting;
 }
 
diff --git a/chrome/browser/ui/webui/site_settings_helper.h b/chrome/browser/ui/webui/site_settings_helper.h
index f5ef7f8..a7600bf 100644
--- a/chrome/browser/ui/webui/site_settings_helper.h
+++ b/chrome/browser/ui/webui/site_settings_helper.h
@@ -54,6 +54,7 @@
 
 enum class SiteSettingSource {
   kDefault,
+  kDrmDisabled,
   kEmbargo,
   kExtension,
   kInsecureOrigin,
diff --git a/chrome/browser/ui/webui/site_settings_helper_unittest.cc b/chrome/browser/ui/webui/site_settings_helper_unittest.cc
index cb9a40f..b62eb619 100644
--- a/chrome/browser/ui/webui/site_settings_helper_unittest.cc
+++ b/chrome/browser/ui/webui/site_settings_helper_unittest.cc
@@ -6,11 +6,13 @@
 
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings_utils.h"
 #include "components/content_settings/core/test/content_settings_mock_provider.h"
 #include "components/content_settings/core/test/content_settings_test_utils.h"
+#include "components/prefs/pref_service.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "extensions/browser/extension_registry.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -170,6 +172,18 @@
   EXPECT_EQ(SiteSettingSourceToString(SiteSettingSource::kPreference), source);
   EXPECT_EQ(CONTENT_SETTING_ALLOW, content_setting);
 
+// ChromeOS - DRM disabled.
+#if defined(OS_CHROMEOS)
+  profile.GetPrefs()->SetBoolean(prefs::kEnableDRM, false);
+  // Note this is not testing |kContentType|, because this setting is only valid
+  // for protected content.
+  content_setting = GetContentSettingForOrigin(
+      &profile, map, origin, CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER,
+      &source, extension_registry, &display_name);
+  EXPECT_EQ(SiteSettingSourceToString(SiteSettingSource::kDrmDisabled), source);
+  EXPECT_EQ(CONTENT_SETTING_BLOCK, content_setting);
+#endif
+
   // Extension.
   auto extension_provider = base::MakeUnique<content_settings::MockProvider>();
   extension_provider->SetWebsiteSetting(ContentSettingsPattern::FromURL(origin),
diff --git a/chrome/test/data/webui/settings/route_tests.js b/chrome/test/data/webui/settings/route_tests.js
index a7041aa..10509ad 100644
--- a/chrome/test/data/webui/settings/route_tests.js
+++ b/chrome/test/data/webui/settings/route_tests.js
@@ -228,4 +228,26 @@
     assertFalse(hasRoute('PEOPLE'));
     assertFalse(hasRoute('RESET'));
   });
+
+  test(
+      'getAbsolutePath works in direct and within-settings navigation',
+      function() {
+        settings.resetRouteForTesting();
+        // Check getting the absolute path while not inside settings returns the
+        // correct path.
+        window.location.href = "https://example.com/path/to/page.html";
+        assertEquals(
+            'chrome://settings/cloudPrinters',
+            settings.routes.CLOUD_PRINTERS.getAbsolutePath());
+
+        // Check getting the absolute path while inside settings returns the
+        // correct path for the current route and a different route.
+        settings.navigateTo(settings.routes.DOWNLOADS);
+        assertEquals(
+            'chrome://settings/downloads',
+            settings.getCurrentRoute().getAbsolutePath());
+        assertEquals(
+            'chrome://settings/languages',
+            settings.routes.LANGUAGES.getAbsolutePath());
+      });
 });
diff --git a/chrome/test/data/webui/settings/site_details_permission_tests.js b/chrome/test/data/webui/settings/site_details_permission_tests.js
index ce10dd2..0d8e2dd0 100644
--- a/chrome/test/data/webui/settings/site_details_permission_tests.js
+++ b/chrome/test/data/webui/settings/site_details_permission_tests.js
@@ -247,4 +247,20 @@
     assertFalse(testElement.$.permissionItem.classList.contains('two-line'));
     assertFalse(testElement.$.permission.disabled);
   });
+
+  test('source string correct for drm disabled source', function() {
+    var origin = 'https://www.example.com';
+    testElement.category = settings.ContentSettingsTypes.PROTECTED_CONTENT;
+    testElement.site = {
+      origin: origin,
+      embeddingOrigin: origin,
+      setting: settings.ContentSetting.BLOCK,
+      source: settings.SiteSettingSource.DRM_DISABLED,
+    };
+    assertEquals(
+        'To change this setting, first turn on device identifiers',
+        testElement.$.permissionItem.innerText.trim());
+    assertTrue(testElement.$.permissionItem.classList.contains('two-line'));
+    assertTrue(testElement.$.permission.disabled);
+  });
 });
diff --git a/chrome/test/data/webui/settings/site_details_tests.js b/chrome/test/data/webui/settings/site_details_tests.js
index 11b4fe5..b6b5e9ad 100644
--- a/chrome/test/data/webui/settings/site_details_tests.js
+++ b/chrome/test/data/webui/settings/site_details_tests.js
@@ -16,6 +16,18 @@
    */
   var prefs;
 
+  // Helper to create a mock permission preference.
+  function createExceptionForTest(override) {
+    return Object.assign(
+        {
+          embeddingOrigin: 'https://foo.com:443',
+          origin: 'https://foo.com:443',
+          setting: settings.ContentSetting.ALLOW,
+          source: settings.SiteSettingSource.PREFERENCE,
+        },
+        override);
+  }
+
   // Initialize a site-details before each test.
   setup(function() {
     prefs = {
@@ -59,112 +71,35 @@
         unsandboxed_plugins: {
           setting: settings.ContentSetting.ASK,
         },
+        protectedContent: {
+          setting: settings.ContentSetting.ALLOW,
+        },
       },
       exceptions: {
-        auto_downloads: [
-          {
-            embeddingOrigin: 'https://foo.com:443',
-            origin: 'https://foo.com:443',
-            setting: settings.ContentSetting.ALLOW,
-            source: settings.SiteSettingSource.PREFERENCE,
-          },
-        ],
-        background_sync: [
-          {
-            embeddingOrigin: 'https://foo.com:443',
-            origin: 'https://foo.com:443',
-            setting: settings.ContentSetting.ALLOW,
-            source: settings.SiteSettingSource.PREFERENCE,
-          },
-        ],
-        camera: [
-          {
-            embeddingOrigin: 'https://foo.com:443',
-            origin: 'https://foo.com:443',
-            setting: settings.ContentSetting.ALLOW,
-            source: settings.SiteSettingSource.PREFERENCE,
-          },
-        ],
-        geolocation: [
-          {
-            embeddingOrigin: 'https://foo.com:443',
-            origin: 'https://foo.com:443',
-            setting: settings.ContentSetting.ALLOW,
-            source: settings.SiteSettingSource.PREFERENCE,
-          },
-        ],
-        images: [
-          {
-            embeddingOrigin: 'https://foo.com:443',
-            origin: 'https://foo.com:443',
-            setting: settings.ContentSetting.ALLOW,
-            source: settings.SiteSettingSource.DEFAULT,
-          },
-        ],
-        javascript: [
-          {
-            embeddingOrigin: 'https://foo.com:443',
-            origin: 'https://foo.com:443',
-            setting: settings.ContentSetting.ALLOW,
-            source: settings.SiteSettingSource.PREFERENCE,
-          },
-        ],
-        mic: [
-          {
-            embeddingOrigin: 'https://foo.com:443',
-            origin: 'https://foo.com:443',
-            setting: settings.ContentSetting.ALLOW,
-            source: settings.SiteSettingSource.PREFERENCE,
-          },
-        ],
-        midi_devices: [
-          {
-            embeddingOrigin: 'https://foo.com:443',
-            origin: 'https://foo.com:443',
-            setting: settings.ContentSetting.ALLOW,
-            source: settings.SiteSettingSource.PREFERENCE,
-          },
-        ],
-        notifications: [
-          {
-            embeddingOrigin: 'https://foo.com:443',
-            origin: 'https://foo.com:443',
-            setting: settings.ContentSetting.ASK,
-            source: settings.SiteSettingSource.POLICY,
-          },
-        ],
-        plugins: [
-          {
-            embeddingOrigin: 'https://foo.com:443',
-            origin: 'https://foo.com:443',
-            setting: settings.ContentSetting.ALLOW,
-            source: settings.SiteSettingSource.EXTENSION,
-          },
-        ],
-        popups: [
-          {
-            embeddingOrigin: 'https://foo.com:443',
-            origin: 'https://foo.com:443',
-            setting: settings.ContentSetting.BLOCK,
-            source: settings.SiteSettingSource.DEFAULT,
-          },
-        ],
-        sound: [
-          {
-            embeddingOrigin: 'https://foo.com:443',
-            origin: 'https://foo.com:443',
-            setting: settings.ContentSetting.ALLOW,
-            source: 'default',
-          },
-        ],
-        unsandboxed_plugins: [
-          {
-            embeddingOrigin: 'https://foo.com:443',
-            origin: 'https://foo.com:443',
-            setting: settings.ContentSetting.ALLOW,
-            source: settings.SiteSettingSource.PREFERENCE,
-          },
-        ],
+        auto_downloads: [createExceptionForTest()],
+        background_sync: [createExceptionForTest()],
+        camera: [createExceptionForTest()],
+        geolocation: [createExceptionForTest()],
+        images: [createExceptionForTest({
+          source: settings.SiteSettingSource.DEFAULT,
+        })],
+        javascript: [createExceptionForTest()],
+        mic: [createExceptionForTest()],
+        midi_devices: [createExceptionForTest()],
+        notifications: [createExceptionForTest({
+          setting: settings.ContentSetting.ASK,
+          source: settings.SiteSettingSource.POLICY,
+        })],
+        plugins: [createExceptionForTest({
+          source: settings.SiteSettingSource.EXTENSION,
+        })],
+        popups: [createExceptionForTest({
+          setting: settings.ContentSetting.BLOCK,
+          source: settings.SiteSettingSource.DEFAULT,
+        })],
+        sound: [createExceptionForTest()],
+        unsandboxed_plugins: [createExceptionForTest()],
+        protectedContent: [createExceptionForTest()],
       }
     };
 
@@ -218,6 +153,11 @@
         .then(() => {
           testElement.root.querySelectorAll('site-details-permission')
               .forEach((siteDetailsPermission) => {
+                if (!cr.isChromeOS &&
+                    siteDetailsPermission.category ==
+                        settings.ContentSettingsTypes.PROTECTED_CONTENT)
+                  return;
+
                 // Verify settings match the values specified in |prefs|.
                 var expectedSetting = settings.ContentSetting.ALLOW;
                 var expectedSource = settings.SiteSettingSource.PREFERENCE;
diff --git a/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.js b/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.js
index 2324b6e..885c8b3 100644
--- a/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.js
+++ b/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.js
@@ -12,8 +12,9 @@
 
 /**
  * An example empty pref.
- * TODO(patricialor): Use the values from settings.ContentSettingsTypes (see
- * site_settings/constants.js) as the keys for these instead.
+ * TODO(https://crbug.com/742706): Use the values from
+ * settings.ContentSettingsTypes (see site_settings/constants.js) as the keys
+ * for these instead.
  * @type {SiteSettingsPref}
  */
 var prefsEmpty = {
@@ -31,6 +32,7 @@
     plugins: {},
     images: {},
     popups: {},
+    protectedContent: {},
     sound: {},
     unsandboxed_plugins: {},
   },
@@ -48,6 +50,7 @@
     plugins: [],
     images: [],
     popups: [],
+    protectedContent: [],
     sound: [],
     unsandboxed_plugins: [],
   },
@@ -247,6 +250,8 @@
     } else if (
         contentType == settings.ContentSettingsTypes.UNSANDBOXED_PLUGINS) {
       pref = this.prefs_.defaults.unsandboxed_plugins;
+    } else if (contentType == settings.ContentSettingsTypes.PROTECTED_CONTENT) {
+      pref = this.prefs_.defaults.protectedContent;
     } else {
       console.log('getDefault received unknown category: ' + contentType);
     }
diff --git a/content/browser/tracing/background_tracing_manager_impl.cc b/content/browser/tracing/background_tracing_manager_impl.cc
index a7305d71..9004fb2 100644
--- a/content/browser/tracing/background_tracing_manager_impl.cc
+++ b/content/browser/tracing/background_tracing_manager_impl.cc
@@ -560,7 +560,8 @@
     trace_data_sink = TracingControllerImpl::CreateCompressedStringSink(
         TracingControllerImpl::CreateCallbackEndpoint(
             base::Bind(&BackgroundTracingManagerImpl::OnFinalizeStarted,
-                       base::Unretained(this))));
+                       base::Unretained(this))),
+        true /* compress_with_background_priority */);
     RecordBackgroundTracingMetric(FINALIZATION_ALLOWED);
     AddCustomMetadata();
   } else {
diff --git a/content/browser/tracing/tracing_controller_browsertest.cc b/content/browser/tracing/tracing_controller_browsertest.cc
index 7087fef..e551a51a 100644
--- a/content/browser/tracing/tracing_controller_browsertest.cc
+++ b/content/browser/tracing/tracing_controller_browsertest.cc
@@ -295,7 +295,8 @@
               base::Unretained(this), run_loop.QuitClosure());
       bool result = controller->StopTracing(
           TracingControllerImpl::CreateCompressedStringSink(
-              new TracingControllerTestEndpoint(callback)));
+              new TracingControllerTestEndpoint(callback),
+              true /* compress_with_background_priority */));
       ASSERT_TRUE(result);
       run_loop.Run();
       EXPECT_EQ(disable_recording_done_callback_count(), 1);
diff --git a/content/browser/tracing/tracing_controller_impl.h b/content/browser/tracing/tracing_controller_impl.h
index 0f4e0cb1..53c1ec7 100644
--- a/content/browser/tracing/tracing_controller_impl.h
+++ b/content/browser/tracing/tracing_controller_impl.h
@@ -56,7 +56,8 @@
                                 base::RefCountedString*)>& callback);
 
   CONTENT_EXPORT static scoped_refptr<TraceDataSink> CreateCompressedStringSink(
-      scoped_refptr<TraceDataEndpoint> endpoint);
+      scoped_refptr<TraceDataEndpoint> endpoint,
+      bool compress_with_background_priority);
   static scoped_refptr<TraceDataSink> CreateJSONSink(
       scoped_refptr<TraceDataEndpoint> endpoint);
 
diff --git a/content/browser/tracing/tracing_controller_impl_data_sinks.cc b/content/browser/tracing/tracing_controller_impl_data_sinks.cc
index 013da86..9eb0c1c 100644
--- a/content/browser/tracing/tracing_controller_impl_data_sinks.cc
+++ b/content/browser/tracing/tracing_controller_impl_data_sinks.cc
@@ -193,30 +193,34 @@
 
 class CompressedTraceDataEndpoint : public TraceDataEndpoint {
  public:
-  explicit CompressedTraceDataEndpoint(
-      scoped_refptr<TraceDataEndpoint> endpoint)
-      : endpoint_(endpoint), already_tried_open_(false) {}
+  CompressedTraceDataEndpoint(scoped_refptr<TraceDataEndpoint> endpoint,
+                              bool compress_with_background_priority)
+      : endpoint_(endpoint),
+        already_tried_open_(false),
+        background_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+            {compress_with_background_priority
+                 ? base::TaskPriority::BACKGROUND
+                 : base::TaskPriority::USER_VISIBLE})) {}
 
   void ReceiveTraceChunk(std::unique_ptr<std::string> chunk) override {
-    BrowserThread::PostTask(
-        BrowserThread::FILE, FROM_HERE,
-        base::BindOnce(&CompressedTraceDataEndpoint::CompressOnFileThread, this,
-                       base::Passed(std::move(chunk))));
+    background_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&CompressedTraceDataEndpoint::CompressOnBackgroundThread,
+                       this, base::Passed(std::move(chunk))));
   }
 
   void ReceiveTraceFinalContents(
       std::unique_ptr<const base::DictionaryValue> metadata) override {
-    BrowserThread::PostTask(
-        BrowserThread::FILE, FROM_HERE,
-        base::BindOnce(&CompressedTraceDataEndpoint::CloseOnFileThread, this,
-                       base::Passed(std::move(metadata))));
+    background_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&CompressedTraceDataEndpoint::CloseOnBackgroundThread,
+                       this, base::Passed(std::move(metadata))));
   }
 
  private:
   ~CompressedTraceDataEndpoint() override {}
 
-  bool OpenZStreamOnFileThread() {
-    DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+  bool OpenZStreamOnBackgroundThread() {
     if (stream_)
       return true;
 
@@ -238,19 +242,16 @@
     return result == 0;
   }
 
-  void CompressOnFileThread(std::unique_ptr<std::string> chunk) {
-    DCHECK_CURRENTLY_ON(BrowserThread::FILE);
-    if (!OpenZStreamOnFileThread())
+  void CompressOnBackgroundThread(std::unique_ptr<std::string> chunk) {
+    if (!OpenZStreamOnBackgroundThread())
       return;
 
     stream_->avail_in = chunk->size();
     stream_->next_in = reinterpret_cast<unsigned char*>(&*chunk->begin());
-    DrainStreamOnFileThread(false);
+    DrainStreamOnBackgroundThread(false);
   }
 
-  void DrainStreamOnFileThread(bool finished) {
-    DCHECK_CURRENTLY_ON(BrowserThread::FILE);
-
+  void DrainStreamOnBackgroundThread(bool finished) {
     int err;
     const int kChunkSize = 0x4000;
     char buffer[kChunkSize];
@@ -272,13 +273,12 @@
     } while (stream_->avail_out == 0);
   }
 
-  void CloseOnFileThread(
+  void CloseOnBackgroundThread(
       std::unique_ptr<const base::DictionaryValue> metadata) {
-    DCHECK_CURRENTLY_ON(BrowserThread::FILE);
-    if (!OpenZStreamOnFileThread())
+    if (!OpenZStreamOnBackgroundThread())
       return;
 
-    DrainStreamOnFileThread(true);
+    DrainStreamOnBackgroundThread(true);
     deflateEnd(stream_.get());
     stream_.reset();
 
@@ -288,6 +288,7 @@
   scoped_refptr<TraceDataEndpoint> endpoint_;
   std::unique_ptr<z_stream> stream_;
   bool already_tried_open_;
+  const scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(CompressedTraceDataEndpoint);
 };
@@ -333,8 +334,10 @@
 
 scoped_refptr<TracingController::TraceDataSink>
 TracingControllerImpl::CreateCompressedStringSink(
-    scoped_refptr<TraceDataEndpoint> endpoint) {
-  return new JSONTraceDataSink(new CompressedTraceDataEndpoint(endpoint));
+    scoped_refptr<TraceDataEndpoint> endpoint,
+    bool compress_with_background_priority) {
+  return new JSONTraceDataSink(new CompressedTraceDataEndpoint(
+      endpoint, compress_with_background_priority));
 }
 
 scoped_refptr<TraceDataEndpoint> TracingControllerImpl::CreateCallbackEndpoint(
diff --git a/content/browser/tracing/tracing_ui.cc b/content/browser/tracing/tracing_ui.cc
index a59ee05..03b1eb0 100644
--- a/content/browser/tracing/tracing_ui.cc
+++ b/content/browser/tracing/tracing_ui.cc
@@ -131,7 +131,8 @@
     scoped_refptr<TracingControllerImpl::TraceDataSink> data_sink =
         TracingControllerImpl::CreateCompressedStringSink(
             TracingControllerImpl::CreateCallbackEndpoint(
-                base::Bind(TracingCallbackWrapperBase64, callback)));
+                base::Bind(TracingCallbackWrapperBase64, callback)),
+            false /* compress_with_background_priority */);
     return TracingController::GetInstance()->StopTracing(data_sink);
   }
 
diff --git a/third_party/WebKit/Source/bindings/scripts/utilities.py b/third_party/WebKit/Source/bindings/scripts/utilities.py
index 9b54872a..b641b42 100644
--- a/third_party/WebKit/Source/bindings/scripts/utilities.py
+++ b/third_party/WebKit/Source/bindings/scripts/utilities.py
@@ -375,7 +375,7 @@
 
     match = re.search(
         r'(?:\[([^[]*)\]\s*)?'
-        r'(interface|callback\s+interface|partial\s+interface)\s+'
+        r'(interface|callback\s+interface|partial\s+interface|dictionary)\s+'
         r'(\w+)\s*'
         r'(:\s*\w+\s*)?'
         r'{',
diff --git a/third_party/WebKit/Source/bindings/templates/external_reference_table.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/external_reference_table.cpp.tmpl
index 01a7b56..95c31af1 100644
--- a/third_party/WebKit/Source/bindings/templates/external_reference_table.cpp.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/external_reference_table.cpp.tmpl
@@ -14,7 +14,7 @@
 
 const intptr_t* {{class}}::GetTable() {
   static const intptr_t reference_table[] = {
-#if !defined(OS_ANDROID)
+#if defined(USE_V8_CONTEXT_SNAPSHOT)
     // Android is sensitive on its APK size, and this table improves it.
     // So we drop the table entries until the V8 context snapshot feature
     // is released on Android.
@@ -44,9 +44,9 @@
                   (not method.overloads.has_partial_overloads or not interface.is_partial)) or
                  (not method.overloads and method.visible) %}
     {# TODO(bashi): Remove this 'if' condition when crbug.com/630986 is fixed. #}
-    {% if not interface.is_callback %}
+    {%          if not interface.is_callback %}
     reinterpret_cast<intptr_t>({{v8_class}}::{{method.name}}MethodCallback{{world_suffix}}),
-    {% endif %}
+    {%          endif %}
     {%        endif %}
     {%      endif %}{# overload(_index) #}
     {%         if method.is_cross_origin and method.visible %}
@@ -109,7 +109,7 @@
     {% endfor %}{# interfaces #}
 
     reinterpret_cast<intptr_t>(V8ObjectConstructor::IsValidConstructorMode),
-#endif  // OS_ANDROID
+#endif  // USE_V8_CONTEXT_SNAPSHOT
     0  // terminate with a null
   };
 
diff --git a/third_party/WebKit/Source/modules/BUILD.gn b/third_party/WebKit/Source/modules/BUILD.gn
index 375b540..6cb84ea 100644
--- a/third_party/WebKit/Source/modules/BUILD.gn
+++ b/third_party/WebKit/Source/modules/BUILD.gn
@@ -80,6 +80,7 @@
     "//third_party/WebKit/Source:non_test_config",
     "//third_party/WebKit/Source:inside_blink",
     "//third_party/WebKit/Source/core:blink_core_pch",
+    "//tools/v8_context_snapshot:use_v8_context_snapshot",
   ]
 
   deps = [
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/bindings/bindings_tests.py b/third_party/WebKit/Tools/Scripts/webkitpy/bindings/bindings_tests.py
index e07f0367..c4da589 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/bindings/bindings_tests.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/bindings/bindings_tests.py
@@ -49,6 +49,9 @@
                           generate_callback_function_impl)
 from utilities import ComponentInfoProviderCore
 from utilities import ComponentInfoProviderModules
+from utilities import get_file_contents
+from utilities import get_first_interface_name_from_idl
+from utilities import to_snake_case
 
 
 PASS_MESSAGE = 'All tests PASS!'
@@ -63,7 +66,20 @@
 TBR=(someone in Source/bindings/OWNERS or WATCHLISTS:bindings)
 """
 
+SOURCE_PATH = path_finder.get_source_dir()
+IS_SNAKE_CASE = path_finder.is_source_in_blink()
 DEPENDENCY_IDL_FILES = frozenset([
+    'test_implements.idl',
+    'test_implements_2.idl',
+    'test_implements_3.idl',
+    'test_interface_partial.idl',
+    'test_interface_partial_2.idl',
+    'test_interface_partial_3.idl',
+    'test_interface_partial_4.idl',
+    'test_interface_partial_secure_context.idl',
+    'test_interface_2_partial.idl',
+    'test_interface_2_partial_2.idl',
+]) if IS_SNAKE_CASE else frozenset([
     'TestImplements.idl',
     'TestImplements2.idl',
     'TestImplements3.idl',
@@ -77,8 +93,6 @@
 ])
 
 COMPONENT_DIRECTORY = frozenset(['core', 'modules'])
-
-SOURCE_PATH = path_finder.get_source_dir()
 TEST_INPUT_DIRECTORY = os.path.join(SOURCE_PATH, 'bindings', 'tests', 'idls')
 REFERENCE_DIRECTORY = os.path.join(SOURCE_PATH, 'bindings', 'tests', 'results')
 
@@ -184,11 +198,11 @@
 
 class IdlCompilerOptions(object):
     def __init__(self, output_directory, cache_directory, impl_output_directory,
-                 target_component, snake_case_generated_files):
+                 target_component):
         self.output_directory = output_directory
         self.cache_directory = cache_directory
         self.impl_output_directory = impl_output_directory
-        self.snake_case_generated_files = snake_case_generated_files
+        self.snake_case_generated_files = IS_SNAKE_CASE
         self.target_component = target_component
 
 
@@ -282,8 +296,7 @@
                 output_directory=output_dir,
                 impl_output_directory=output_dir,
                 cache_directory=None,
-                target_component=component,
-                snake_case_generated_files=False)
+                target_component=component)
 
             if component == 'core':
                 partial_interface_output_dir = os.path.join(output_directory,
@@ -294,8 +307,7 @@
                     output_directory=partial_interface_output_dir,
                     impl_output_directory=None,
                     cache_directory=None,
-                    target_component='modules',
-                    snake_case_generated_files=False)
+                    target_component='modules')
 
             idl_filenames = []
             dictionary_impl_filenames = []
@@ -310,8 +322,14 @@
                         os.path.join(input_directory, filename))
                     idl_filenames.append(idl_path)
                     idl_basename = os.path.basename(idl_path)
-                    definition_name, _ = os.path.splitext(idl_basename)
-                    if definition_name in interfaces_info:
+                    name_from_basename, _ = os.path.splitext(idl_basename)
+                    definition_name = get_first_interface_name_from_idl(get_file_contents(idl_path))
+                    is_partial_interface_idl = False
+                    if IS_SNAKE_CASE:
+                        is_partial_interface_idl = to_snake_case(definition_name) != name_from_basename
+                    else:
+                        is_partial_interface_idl = definition_name != name_from_basename
+                    if not is_partial_interface_idl:
                         interface_info = interfaces_info[definition_name]
                         if interface_info['is_dictionary']:
                             dictionary_impl_filenames.append(idl_path)
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/bindings/collect_idls_into_json.py b/third_party/WebKit/Tools/Scripts/webkitpy/bindings/collect_idls_into_json.py
index 1a10718..ad37f91c 100755
--- a/third_party/WebKit/Tools/Scripts/webkitpy/bindings/collect_idls_into_json.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/bindings/collect_idls_into_json.py
@@ -11,13 +11,8 @@
 import os
 import sys
 
-_bindings_path = os.path.normpath(
-    os.path.join(os.path.dirname(__file__),
-                 os.pardir, os.pardir, os.pardir, os.pardir,
-                 'Source', 'bindings', 'scripts'))
-
-if _bindings_path not in sys.path:
-    sys.path.append(_bindings_path)
+from webkitpy.common import path_finder
+path_finder.add_bindings_scripts_dir_to_sys_path()
 
 import utilities
 from blink_idl_parser import parse_file, BlinkIDLParser
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/path_finder.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/path_finder.py
index 91b4538..8d8f362 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/path_finder.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/path_finder.py
@@ -75,6 +75,11 @@
     return os.path.join(get_blink_dir(), 'Source')
 
 
+# TODO(tkent): Remove this function after the source move. crbug.com/622551
+def is_source_in_blink():
+    return '/blink/renderer' in get_source_dir().replace(os.path.sep, '/')
+
+
 def get_typ_dir():
     return os.path.join(get_chromium_src_dir(), 'third_party', 'typ')
 
diff --git a/tools/v8_context_snapshot/BUILD.gn b/tools/v8_context_snapshot/BUILD.gn
index 3792c69d..9adab19 100644
--- a/tools/v8_context_snapshot/BUILD.gn
+++ b/tools/v8_context_snapshot/BUILD.gn
@@ -7,22 +7,16 @@
 # third_party/WebKit/Source/bindings/core/v8/V8ContextSnapshot.{cpp|h}.
 # to speedup creating a V8 context and setting up around it.
 
+import("//tools/v8_context_snapshot/v8_context_snapshot.gni")
+
 import("//build/config/c++/c++.gni")
-import("//build/config/chromecast_build.gni")
 import("//build/config/compiler/compiler.gni")
-import("//build/config/v8_target_cpu.gni")
 import("//v8/snapshot_toolchain.gni")
 
 if (is_android) {
   import("//build/config/android/rules.gni")
 }
 
-declare_args() {
-  # TODO(crbug.com/764576): Enable the feature on more environments.
-  use_v8_context_snapshot = !is_chromeos && !is_android && !is_chromecast &&
-                            (v8_target_cpu == target_cpu || is_msan)
-}
-
 if (is_android) {
   android_assets("v8_context_snapshot_assets") {
     deps = [
@@ -43,6 +37,12 @@
   }
 }
 
+config("use_v8_context_snapshot") {
+  if (use_v8_context_snapshot) {
+    defines = [ "USE_V8_CONTEXT_SNAPSHOT" ]
+  }
+}
+
 if (use_v8_context_snapshot) {
   action("generate_v8_context_snapshot") {
     script = "run.py"
diff --git a/tools/v8_context_snapshot/v8_context_snapshot.gni b/tools/v8_context_snapshot/v8_context_snapshot.gni
new file mode 100644
index 0000000..e43ee81
--- /dev/null
+++ b/tools/v8_context_snapshot/v8_context_snapshot.gni
@@ -0,0 +1,18 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Targets in ths file are to take a V8 context snapshot on build time.
+# Created V8 context snapshot is used in
+# third_party/WebKit/Source/bindings/core/v8/V8ContextSnapshot.{cpp|h}.
+# to speedup creating a V8 context and setting up around it.
+
+import("//build/config/chromecast_build.gni")
+import("//build/config/v8_target_cpu.gni")
+
+declare_args() {
+  # TODO(crbug.com/764576): Enable the feature on more environments.
+  use_v8_context_snapshot =
+      target_os != "chromeos" && target_os != "android" && !is_chromecast &&
+      (v8_target_cpu == target_cpu || is_msan)
+}
diff --git a/ui/file_manager/file_manager/foreground/elements/files_quick_view.css b/ui/file_manager/file_manager/foreground/elements/files_quick_view.css
index e6fd3d4..781b49c 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_quick_view.css
+++ b/ui/file_manager/file_manager/foreground/elements/files_quick_view.css
@@ -118,6 +118,9 @@
 #toolbar {
   --paper-toolbar-background: rgb(40, 42, 45);
   --paper-toolbar-height: 48px;
+  --paper-toolbar-content: {
+    -webkit-padding-end: 0;
+  };
   color: white;
   font-size: 108%;
   margin: 0;
@@ -138,8 +141,11 @@
 
 #buttons {
   display: flex;
-  position: absolute;
-  right: 0px;
+}
+
+#file-path {
+  overflow: hidden;
+  text-overflow: ellipsis;
 }
 
 :host-context(html[dir='rtl']) #buttons {
diff --git a/ui/file_manager/file_manager/foreground/elements/files_quick_view.html b/ui/file_manager/file_manager/foreground/elements/files_quick_view.html
index 3e15292..40f0a8d 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_quick_view.html
+++ b/ui/file_manager/file_manager/foreground/elements/files_quick_view.html
@@ -18,7 +18,7 @@
   <template>
     <dialog id="dialog">
       <paper-toolbar id="toolbar">
-        <div>[[filePath]]</div>
+        <div id="file-path">[[filePath]]</div>
         <div id="buttons">
            <paper-button id="open-button" on-tap="onOpenInNewButtonTap" hidden$="[[!hasTask]]" i18n-values="aria-label:QUICK_VIEW_OPEN_IN_NEW_BUTTON_LABEL" tabindex="0" has-tooltip>
              <iron-icon icon="files:open-in-new"></iron-icon>
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 581a12d..2c3822c 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
@@ -3,23 +3,6 @@
 // found in the LICENSE file.
 
 /**
- * Sets 'hidden' property of a cr.ui.Command instance and dispatches
- * 'hiddenChange' event manually so that associated cr.ui.MenuItem can handle
- * the event.
- * TODO(fukino): Remove this workaround when crbug.com/481941 is fixed.
- *
- * @param {boolean} value New value of hidden property.
- */
-cr.ui.Command.prototype.setHidden = function(value) {
-  if (value === this.hidden)
-    return;
-
-  var oldValue = this.hidden;
-  this.hidden = value;
-  cr.dispatchPropertyChange(this, 'hidden', value, oldValue);
-};
-
-/**
  * A command.
  * @interface
  */