diff --git a/DEPS b/DEPS
index e7fc4413..c9961df 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '0fa156fcfba3b430801b5448ddfc254732bf7386',
+  'skia_revision': '1119dc366e15ef737d05d3a087410ea40c508101',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'a41f32d2aa7d677a89ba2c2b4a83d3c76895efd7',
+  'v8_revision': '9a91af37de7c0a8b21cd4219abb1bbb9c232918b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -60,7 +60,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': 'f770c9916363b17ba928da7a6d7842dc9e39ae53',
+  'swiftshader_revision': '2ed3149ac7f92d367b9629da29c45ffc0d6055d3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -96,7 +96,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '9a55abab029cb9ae94f5160ded11b09a4638a955',
+  'catapult_revision': '0bd6d0b868da1fbca500f92ead42bc22f6c6b77c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -241,7 +241,7 @@
     Var('chromium_git') + '/external/github.com/open-source-parsers/jsoncpp.git' + '@' + 'f572e8e42e22cfcf5ab0aea26574f408943edfa4', # from svn 248
 
   'src/third_party/libyuv':
-    Var('chromium_git') + '/libyuv/libyuv.git' + '@' + '2adb84e39e360723d19c68f315d99e3e0f88318c',  # from r1650
+    Var('chromium_git') + '/libyuv/libyuv.git' + '@' + '8cab2e31d76246263206318f3568d452e7f3ff3e',  # from r1651
 
   'src/third_party/smhasher/src':
     Var('chromium_git') + '/external/smhasher.git' + '@' + 'e87738e57558e0ec472b2fc3a643b838e5b6e88f',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index c2789a9..7fd1372 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1462,6 +1462,7 @@
     "//ash/strings:ash_test_strings",
     "//ash/resources:ash_test_resources_100_percent",
     "//ash/resources:ash_test_resources_200_percent",
+    "//testing/buildbot/filters:ash_unittests_filters",
   ]
 
   if (!use_x11) {
diff --git a/ash/system/palette/palette_utils.cc b/ash/system/palette/palette_utils.cc
index 9f65d72..7181d6c 100644
--- a/ash/system/palette/palette_utils.cc
+++ b/ash/system/palette/palette_utils.cc
@@ -11,7 +11,6 @@
 #include "ash/system/status_area_widget.h"
 #include "ash/wm_window.h"
 #include "base/command_line.h"
-#include "base/sys_info.h"
 #include "ui/events/devices/input_device_manager.h"
 #include "ui/events/devices/touchscreen_device.h"
 #include "ui/gfx/geometry/point.h"
@@ -19,18 +18,6 @@
 namespace ash {
 namespace palette_utils {
 
-namespace {
-// Pyro firmware currently reports it has a stylus but it does not.
-// TODO(jdufault): Remove this once firmware is fixed. See b/36367810.
-const char* kBlacklistedDevices[] = {"pyro"};
-
-bool IsBlacklisted(const std::string& name) {
-  return std::find(std::begin(kBlacklistedDevices),
-                   std::end(kBlacklistedDevices),
-                   name) != std::end(kBlacklistedDevices);
-}
-}  // namespace
-
 bool HasStylusInput() {
   // Allow the user to force enable or disable by passing a switch. If both are
   // present, enabling takes precedence over disabling.
@@ -39,10 +26,6 @@
     return true;
   }
 
-  // Disable stylus for any blacklisted devices.
-  if (IsBlacklisted(base::SysInfo::GetLsbReleaseBoard()))
-    return false;
-
   // Check to see if the hardware reports it is stylus capable.
   for (const ui::TouchscreenDevice& device :
        ui::InputDeviceManager::GetInstance()->GetTouchscreenDevices()) {
diff --git a/base/allocator/partition_allocator/address_space_randomization.cc b/base/allocator/partition_allocator/address_space_randomization.cc
index ca9efdd8..d54fb44 100644
--- a/base/allocator/partition_allocator/address_space_randomization.cc
+++ b/base/allocator/partition_allocator/address_space_randomization.cc
@@ -16,6 +16,11 @@
 #include <unistd.h>
 #endif
 
+// VersionHelpers.h must be included after windows.h.
+#if defined(OS_WIN)
+#include <VersionHelpers.h>
+#endif
+
 namespace base {
 
 namespace {
@@ -92,7 +97,13 @@
 #if defined(OS_WIN)
   random &= 0x3ffffffffffUL;
   // Windows >= 8.1 has the full 47 bits. Use them where available.
-  if (base::win::GetVersion() < base::win::Version::VERSION_WIN8_1) {
+  static bool windows_81 = false;
+  static bool windows_81_initialized = false;
+  if (!windows_81_initialized) {
+    windows_81 = IsWindows8Point1OrGreater();
+    windows_81_initialized = true;
+  }
+  if (!windows_81) {
     random += 0x10000000000UL;
   }
 #elif defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
diff --git a/base/task_scheduler/task_tracker_unittest.cc b/base/task_scheduler/task_tracker_unittest.cc
index 1c9b0a8..e1596f7 100644
--- a/base/task_scheduler/task_tracker_unittest.cc
+++ b/base/task_scheduler/task_tracker_unittest.cc
@@ -43,7 +43,7 @@
 
 namespace {
 
-constexpr size_t kLoadTestNumIterations = 100;
+constexpr size_t kLoadTestNumIterations = 75;
 
 // Invokes a closure asynchronously.
 class CallbackThread : public SimpleThread {
diff --git a/build/android/incremental_install/README.md b/build/android/incremental_install/README.md
new file mode 100644
index 0000000..0916e07
--- /dev/null
+++ b/build/android/incremental_install/README.md
@@ -0,0 +1,81 @@
+# Incremental Install
+
+Incremental Install is a way of building & deploying an APK that tries to
+minimize the time it takes to make a change and see that change running on
+device. They work best with `is_component_build=true`, and do *not* require a
+rooted device.
+
+## Building
+
+**Option 1:** Add the gn arg:
+
+    incremental_apk_by_default = true
+
+This causes all apks to be built as incremental (except for blacklisted ones).
+
+**Option 2:** Add `_incremental` to the apk target name. E.g.:
+
+    ninja -C out/Debug chrome_public_apk_incremental
+    ninja -C out/Debug chrome_public_test_apk_incremental
+
+## Running
+
+It is not enough to `adb install` them. You must use a generated wrapper script:
+
+    out/Debug/bin/install_chrome_public_apk_incremental
+    out/Debug/bin/run_chrome_public_test_apk_incremental  # Automatically sets --fast-local-dev
+
+## Caveats
+
+Isolated processes (on L+) are incompatible with incremental install. As a
+work-around, you can disable isolated processes only for incremental apks using
+gn arg:
+
+    disable_incremental_isolated_processes = true
+
+# How it Works
+
+## Overview
+
+The basic idea is to side-load .dex and .so files to `/data/local/tmp` rather
+than bundling them in the .apk. Then, when making a change, only the changed
+.dex / .so needs to be pushed to the device.
+
+Faster Builds:
+
+ * No `final_dex` step (where all .dex files are merged into one)
+ * No need to rebuild .apk for code-only changes (but required for resources)
+ * Apks sign faster because they are smaller.
+
+Faster Installs:
+
+ * The .apk is smaller, and so faster to verify.
+ * No need to run `adb install` for code-only changes.
+ * Only changed .so / .dex files are pushed. MD5s of existing on-device files
+   are cached on host computer.
+
+Slower Initial Runs:
+
+ * The first time you run an incremental .apk, the `DexOpt` needs to run on all
+   .dex files. This step is normally done during `adb install`, but is done on
+   start-up for incremental apks.
+   * DexOpt results are cached, so subsequent runs are much faster
+
+## The Code
+
+All incremental apks have the same classes.dex, which is built from:
+
+    //build/android/incremental_install:bootstrap_java
+
+They also have a transformed `AndroidManifest.xml`, which overrides the the
+main application class and any instrumentation classes so that they instead
+point to `BootstrapApplication`. This is built by:
+
+    //build/android/incremental_install/generate_android_manifest.py
+
+Wrapper scripts and install logic is contained in:
+
+    //build/android/incremental_install/create_install_script.py
+    //build/android/incremental_install/installer.py
+
+Finally, GN logic for incremental apks is sprinkled throughout.
diff --git a/build/android/lint/suppressions.xml b/build/android/lint/suppressions.xml
index 0abc2963..b020fe1 100644
--- a/build/android/lint/suppressions.xml
+++ b/build/android/lint/suppressions.xml
@@ -426,7 +426,7 @@
   </issue>
   <issue id="UselessParent">
     <ignore regexp="android_webview/tools/system_webview_shell/apk/res/layout/activity_webview_browser.xml"/>
-    <ignore regexp="chrome/android/java/res/layout/data_reduction_promo_screen.xml"/>
+    <ignore regexp="chrome/android/java/res/layout/data_usage_breakdown.xml"/>
     <ignore regexp="chromecast/internal"/>
     <ignore regexp="tools/android/kerberos/SpnegoAuthenticator/res/layout/activity_account_authenticator.xml"/>
   </issue>
diff --git a/build/android/pylib/results/presentation/javascript/main_html.js b/build/android/pylib/results/presentation/javascript/main_html.js
index 6c254d5a..ea75a84 100644
--- a/build/android/pylib/results/presentation/javascript/main_html.js
+++ b/build/android/pylib/results/presentation/javascript/main_html.js
@@ -46,12 +46,34 @@
       });
   showTestTable(true);
   showSuiteTable(false);
+  window.scrollTo(0, 0);
+}
+
+function showTestsOfOneSuiteOnlyWithNewState(suite_name) {
+  showTestsOfOneSuiteOnly(suite_name);
+  history.pushState({suite: suite_name}, suite_name, '');
 }
 
 function showSuiteTableOnly() {
   setTitle('Suites Summary')
   showTestTable(false);
   showSuiteTable(true);
+  window.scrollTo(0, 0);
+}
+
+function showSuiteTableOnlyWithReplaceState() {
+  showSuiteTableOnly();
+  history.replaceState({}, 'suite_table', '');
+}
+
+function setBrowserBackButtonLogic() {
+  window.onpopstate = function(event) {
+    if (!event.state || !event.state.suite) {
+      showSuiteTableOnly();
+    } else {
+      showTestsOfOneSuiteOnly(event.state.suite);
+    }
+  };
 }
 
 function setTitle(title) {
@@ -166,17 +188,6 @@
   }
 }
 
-function loadPage() {
-  var args = getArguments();
-  if ('suite' in args) {
-    // The user wants to visit detailed 'subpage' of that suite.
-    showTestsOfOneSuiteOnly(args['suite']);
-  } else {
-    // The user wants to visit the summary of all suites.
-    showSuiteTableOnly();
-  }
-}
-
 function reportIssues() {
   var url = 'https://bugs.chromium.org/p/chromium/issues/entry?' +
             'labels=Pri-2,Type-Bug,Restrict-View-Google&' +
diff --git a/build/android/pylib/results/presentation/template/main.html b/build/android/pylib/results/presentation/template/main.html
index 60e78ad..b21d4c9 100644
--- a/build/android/pylib/results/presentation/template/main.html
+++ b/build/android/pylib/results/presentation/template/main.html
@@ -52,7 +52,7 @@
     <a onclick="reportIssues();"><b>Feedback</b></a>
   </body>
   <script>
-    loadPage();
+    showSuiteTableOnlyWithReplaceState();
     // Enable sorting for each column of tables.
     Array.prototype.slice.call(document.getElementsByTagName('th'))
         .forEach(function(head) {
@@ -61,5 +61,6 @@
                 function() { sortByColumn(head); });
         }
     );
+    setBrowserBackButtonLogic();
   </script>
 </html>
\ No newline at end of file
diff --git a/build/android/pylib/results/presentation/template/table.html b/build/android/pylib/results/presentation/template/table.html
index 3ed498e..5c2a266 100644
--- a/build/android/pylib/results/presentation/template/table.html
+++ b/build/android/pylib/results/presentation/template/table.html
@@ -26,6 +26,8 @@
               {% for link in cell.links %}
                 <a href="{{link.href}}" target="{{link.target}}">{{link.data}}</a>
               {% endfor %}
+            {%- elif cell.cell_type == 'action' -%}
+              <a onclick="{{cell.action}}">{{cell.data}}</a>
             {%- else -%}
               {{cell.data}}
             {%- endif %}
@@ -43,6 +45,8 @@
               {% for link in cell.links %}
                 <a href="{{link.href}}" target="{{link.target}}"><b>{{link.data}}</b></a>
               {% endfor %}
+            {%- elif cell.cell_type == 'action' -%}
+              <a onclick="{{cell.action}}">{{cell.data}}</a>
             {%- else -%}
               <b>{{cell.data}}</b>
             {%- endif %}
diff --git a/build/android/pylib/results/presentation/test_results_presentation.py b/build/android/pylib/results/presentation/test_results_presentation.py
index ea51689f..3324784 100755
--- a/build/android/pylib/results/presentation/test_results_presentation.py
+++ b/build/android/pylib/results/presentation/test_results_presentation.py
@@ -78,6 +78,22 @@
   }
 
 
+def action_cell(action, data, html_class):
+  """Formats table cell with javascript actions.
+
+  Args:
+    action: Javscript action.
+    data: Data in cell.
+    class: Class for table cell.
+  """
+  return {
+    'cell_type': 'action',
+    'action': action,
+    'data': data,
+    'class': html_class,
+  }
+
+
 def logs_cell(result):
   """Formats result logs data for processing in jinja template."""
   link_list = []
@@ -165,13 +181,11 @@
   ]
 
   footer_row = [
-    links_cell(
-        links=[
-            link(href=('?suite=%s' % 'TOTAL'),
-                 target=LinkTarget.CURRENT_TAB,
-                 data='TOTAL')
-        ],
-    ),             # suite_name
+    action_cell(
+          'showTestsOfOneSuiteOnlyWithNewState("TOTAL")',
+          'TOTAL',
+          'center'
+        ),         # TOTAL
     cell(data=0),  # number_success_tests
     cell(data=0),  # number_fail_tests
     cell(data=0),  # all_tests
@@ -191,12 +205,10 @@
       suite_row = suite_row_dict[suite_name]
     else:
       suite_row = [
-        links_cell(
-            links=[
-                link(href=('?suite=%s' % suite_name),
-                     target=LinkTarget.CURRENT_TAB,
-                     data=suite_name)],
-            html_class='left'
+        action_cell(
+          'showTestsOfOneSuiteOnlyWithNewState("%s")' % suite_name,
+          suite_name,
+          'left'
         ),             # suite_name
         cell(data=0),  # number_success_tests
         cell(data=0),  # number_fail_tests
@@ -299,13 +311,13 @@
   parser.add_argument('--json-file', help='Path of json file.', required=True)
   parser.add_argument('--cs-base-url', help='Base url for code search.',
                       default='http://cs.chromium.org')
-  parser.add_argument('--bucket', default='chromium-result-details')
+  parser.add_argument('--bucket', help='Google storage bucket.', required=True)
   parser.add_argument('--builder-name', help='Builder name.', required=True)
   parser.add_argument('--build-number', help='Build number.', required=True)
   parser.add_argument('--test-name', help='The name of the test.',
                       required=True)
   parser.add_argument('--server-url', help='The url of the server.',
-                      default='https://storage.googleapis.com')
+                      default='https://storage.cloud.google.com')
   parser.add_argument(
       '--content-type',
       help=('Content type, which is used to determine '
diff --git a/cc/benchmarks/invalidation_benchmark.cc b/cc/benchmarks/invalidation_benchmark.cc
index 6a61522..c44a157 100644
--- a/cc/benchmarks/invalidation_benchmark.cc
+++ b/cc/benchmarks/invalidation_benchmark.cc
@@ -72,7 +72,7 @@
 void InvalidationBenchmark::RunOnLayer(PictureLayer* layer) {
   gfx::Rect visible_layer_rect = gfx::Rect(layer->bounds());
   gfx::Transform from_screen;
-  bool invertible = layer->screen_space_transform().GetInverse(&from_screen);
+  bool invertible = layer->ScreenSpaceTransform().GetInverse(&from_screen);
   if (!invertible)
     from_screen = gfx::Transform();
   gfx::Rect viewport_rect = MathUtil::ProjectEnclosingClippedRect(
diff --git a/cc/blink/web_layer_impl_fixed_bounds_unittest.cc b/cc/blink/web_layer_impl_fixed_bounds_unittest.cc
index 20993b3..3dd88c1 100644
--- a/cc/blink/web_layer_impl_fixed_bounds_unittest.cc
+++ b/cc/blink/web_layer_impl_fixed_bounds_unittest.cc
@@ -70,10 +70,10 @@
 
 void ExpectEqualLayerRectsInTarget(cc::Layer* layer1, cc::Layer* layer2) {
   gfx::RectF layer1_rect_in_target(gfx::SizeF(layer1->bounds()));
-  layer1->screen_space_transform().TransformRect(&layer1_rect_in_target);
+  layer1->ScreenSpaceTransform().TransformRect(&layer1_rect_in_target);
 
   gfx::RectF layer2_rect_in_target(gfx::SizeF(layer2->bounds()));
-  layer2->screen_space_transform().TransformRect(&layer2_rect_in_target);
+  layer2->ScreenSpaceTransform().TransformRect(&layer2_rect_in_target);
 
   EXPECT_FLOAT_RECT_EQ(layer1_rect_in_target, layer2_rect_in_target);
 }
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 4af2f0a5..b8e2a85 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -1443,7 +1443,7 @@
       ->num_copy_requests_in_subtree;
 }
 
-gfx::Transform Layer::screen_space_transform() const {
+gfx::Transform Layer::ScreenSpaceTransform() const {
   DCHECK_NE(transform_tree_index_, TransformTree::kInvalidNodeId);
   return draw_property_utils::ScreenSpaceTransform(
       this, layer_tree_host_->property_trees()->transform_tree);
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index e95f744..58dde9c 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -202,8 +202,7 @@
     return clip_children_.get();
   }
 
-  // TODO(enne): Fix style here (and everywhere) once LayerImpl does the same.
-  gfx::Transform screen_space_transform() const;
+  gfx::Transform ScreenSpaceTransform() const;
 
   void set_num_unclipped_descendants(size_t descendants) {
     num_unclipped_descendants_ = descendants;
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index f57f8a22..3b849e9 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -1793,11 +1793,6 @@
   surface_layers_.erase(it);
 }
 
-template <typename LayerType>
-static inline bool LayerClipsSubtree(LayerType* layer) {
-  return layer->masks_to_bounds() || layer->mask_layer();
-}
-
 static bool PointHitsRect(
     const gfx::PointF& screen_space_point,
     const gfx::Transform& local_space_to_screen_space_transform,
diff --git a/chrome/android/java/res/color/item_chooser_row_text_color.xml b/chrome/android/java/res/color/item_chooser_row_text_color.xml
index 5e42aab..bb8c05c 100644
--- a/chrome/android/java/res/color/item_chooser_row_text_color.xml
+++ b/chrome/android/java/res/color/item_chooser_row_text_color.xml
@@ -4,7 +4,7 @@
      found in the LICENSE file.
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-  <item android:color="@android:color/white" android:state_selected="true"/>
-  <item android:color="@color/primary_text_disabled_material_light" android:state_enabled="false"/>
-  <item android:color="@color/default_text_color"/>
+    <item android:color="@android:color/white" android:state_selected="true"/>
+    <item android:color="@color/primary_text_disabled_material_light" android:state_enabled="false"/>
+    <item android:color="@color/default_text_color"/>
 </selector>
diff --git a/chrome/android/java/res/layout/data_reduction_old_stats_layout.xml b/chrome/android/java/res/layout/data_reduction_old_stats_layout.xml
new file mode 100644
index 0000000..592602e
--- /dev/null
+++ b/chrome/android/java/res/layout/data_reduction_old_stats_layout.xml
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/data_reduction_stats_container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    android:orientation="vertical" >
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:fadingEdge="horizontal"
+        android:text="@string/data_reduction_stats_title"
+        android:textAppearance="@style/PreferenceCategoryTextStyle" />
+
+    <include layout="@layout/data_usage_chart" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" >
+
+        <TextView
+            android:id="@+id/data_reduction_start_date"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:textAppearance="@style/PreferenceSummary"
+            android:textSize="14sp" />
+
+        <TextView
+            android:id="@+id/data_reduction_end_date"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="@style/PreferenceSummary"
+            android:textSize="14sp" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <TextView
+            android:id="@+id/data_reduction_percent"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_marginTop="0dp"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textSize="50sp" />
+
+        <FrameLayout
+            android:layout_height="wrap_content"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:paddingEnd="6dp"
+            android:paddingStart="20dp" >
+
+            <LinearLayout
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:layout_gravity="end"
+                android:orientation="vertical"
+                tools:ignore="UselessParent" >
+
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="14dp"
+                    android:singleLine="true"
+                    android:text="@string/data_reduction_original_size_label"
+                    android:textColor="?android:attr/textColorPrimary"
+                    android:textSize="14sp" />
+
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="4dp"
+                    android:singleLine="true"
+                    android:text="@string/data_reduction_compressed_size_label"
+                    android:textColor="?android:attr/textColorPrimary"
+                    android:textSize="14sp" />
+            </LinearLayout>
+
+        </FrameLayout>
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:paddingStart="10dp" >
+
+            <TextView
+                android:id="@+id/data_reduction_original_size"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="14dp"
+                android:textColor="?android:attr/textColorPrimary"
+                android:textSize="14sp"
+                android:textStyle="bold" />
+
+            <TextView
+                android:id="@+id/data_reduction_compressed_size"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="4dp"
+                android:textColor="?android:attr/textColorPrimary"
+                android:textSize="14sp"
+                android:textStyle="bold" />
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/data_reduction_proxy_unreachable"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="20dp"
+        android:layout_marginTop="20dp"
+        android:drawableStart="@drawable/exclamation_triangle"
+        android:drawablePadding="6dp"
+        android:text="@string/data_reduction_proxy_unreachable_warn"
+        android:textColor="?android:attr/textColorPrimary"
+        android:textSize="14sp" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/data_reduction_stats_layout.xml b/chrome/android/java/res/layout/data_reduction_stats_layout.xml
index 86092ed..8484734 100644
--- a/chrome/android/java/res/layout/data_reduction_stats_layout.xml
+++ b/chrome/android/java/res/layout/data_reduction_stats_layout.xml
@@ -12,12 +12,75 @@
     android:clipToPadding="false"
     android:orientation="vertical" >
 
-    <TextView
+    <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:fadingEdge="horizontal"
-        android:text="@string/data_reduction_stats_title"
-        android:textAppearance="@style/PreferenceCategoryTextStyle" />
+        android:baselineAligned="false"
+        android:orientation="horizontal" >
+
+        <LinearLayout
+            android:layout_height="wrap_content"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:orientation="vertical" >
+
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"
+                android:orientation="horizontal" >
+
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:singleLine="true"
+                    android:text="@string/data_reduction_savings_label"
+                    android:textAppearance="@style/PreferenceCategoryTextStyle" />
+
+                <TextView
+                    android:id="@+id/data_reduction_savings"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:paddingStart="3dp"
+                    android:textAppearance="@style/PreferenceCategoryTextStyle" />
+
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="4dp"
+                android:orientation="horizontal" >
+
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:singleLine="true"
+                    android:text="@string/data_reduction_usage_label"
+                    android:textColor="?android:attr/textColorSecondary"
+                    android:textSize="14sp" />
+
+                <TextView
+                    android:id="@+id/data_reduction_usage"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:paddingStart="3dp"
+                    android:textColor="?android:attr/textColorSecondary"
+                    android:textSize="14sp" />
+            </LinearLayout>
+
+        </LinearLayout>
+
+        <TextView
+            android:id="@+id/data_reduction_percent"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_marginTop="0dp"
+            android:includeFontPadding="false"
+            android:textColor="@color/light_active_color"
+            android:textSize="50sp" />
+
+    </LinearLayout>
 
     <include layout="@layout/data_usage_chart" />
 
@@ -42,81 +105,6 @@
 
     </LinearLayout>
 
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal" >
-
-        <TextView
-            android:id="@+id/data_reduction_percent"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:layout_marginTop="0dp"
-            android:textColor="?android:attr/textColorPrimary"
-            android:textSize="50sp" />
-
-        <FrameLayout
-            android:layout_height="wrap_content"
-            android:layout_width="0dp"
-            android:layout_weight="1"
-            android:paddingEnd="6dp"
-            android:paddingStart="20dp" >
-
-            <LinearLayout
-                android:layout_height="wrap_content"
-                android:layout_width="wrap_content"
-                android:layout_gravity="end"
-                android:orientation="vertical"
-                tools:ignore="UselessParent" >
-
-                <TextView
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:layout_marginTop="14dp"
-                    android:singleLine="true"
-                    android:text="@string/data_reduction_original_size_label"
-                    android:textColor="?android:attr/textColorPrimary"
-                    android:textSize="14sp" />
-
-                <TextView
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:layout_marginTop="4dp"
-                    android:singleLine="true"
-                    android:text="@string/data_reduction_compressed_size_label"
-                    android:textColor="?android:attr/textColorPrimary"
-                    android:textSize="14sp" />
-            </LinearLayout>
-
-        </FrameLayout>
-
-        <LinearLayout
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:orientation="vertical"
-            android:paddingStart="10dp" >
-
-            <TextView
-                android:id="@+id/data_reduction_original_size"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="14dp"
-                android:textColor="?android:attr/textColorPrimary"
-                android:textSize="14sp"
-                android:textStyle="bold" />
-
-            <TextView
-                android:id="@+id/data_reduction_compressed_size"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="4dp"
-                android:textColor="?android:attr/textColorPrimary"
-                android:textSize="14sp"
-                android:textStyle="bold" />
-        </LinearLayout>
-
-    </LinearLayout>
-
     <TextView
         android:id="@+id/data_reduction_proxy_unreachable"
         android:layout_width="match_parent"
@@ -128,5 +116,7 @@
         android:text="@string/data_reduction_proxy_unreachable_warn"
         android:textColor="?android:attr/textColorPrimary"
         android:textSize="14sp" />
+        
+    <include layout="@layout/data_usage_breakdown" />
 
 </LinearLayout>
diff --git a/chrome/android/java/res/layout/data_usage_breakdown.xml b/chrome/android/java/res/layout/data_usage_breakdown.xml
new file mode 100644
index 0000000..fc49a18
--- /dev/null
+++ b/chrome/android/java/res/layout/data_usage_breakdown.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<org.chromium.chrome.browser.preferences.datareduction.DataReductionSiteBreakdownView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/breakdown"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:orientation="vertical"
+    android:visibility="gone">
+
+    <TextView
+        android:id="@+id/data_reduction_data_usage_breakdown_title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="26dp"
+        android:paddingBottom="10dp"
+        android:singleLine="true"
+        android:text="@string/data_reduction_data_usage_breakdown_title"
+        android:textAppearance="@style/PreferenceCategoryTextStyle" />
+
+    <TableLayout
+        android:id="@+id/data_reduction_proxy_breakdown_table"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:divider="?android:attr/dividerHorizontal"
+        android:showDividers="middle"
+        android:stretchColumns="0">
+
+        <TableRow
+            android:id="@+id/data_reduction_proxy_breakdown_table_labels"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:orientation="horizontal">
+
+            <TextView
+                android:text="@string/data_reduction_breakdown_site_title"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:textColor="?android:attr/textColorSecondary"
+                style="@style/DataUsageBreakdownColumnLabel" />
+
+            <TextView
+                android:text="@string/data_reduction_breakdown_used_title"
+                android:layout_width="wrap_content"
+                android:gravity="end"
+                android:paddingStart="10dp"
+                android:textColor="?android:attr/textColorPrimary"
+                style="@style/DataUsageBreakdownColumnLabel" />
+
+            <TextView
+                android:text="@string/data_reduction_breakdown_saved_title"
+                android:layout_width="wrap_content"
+                android:gravity="end"
+                android:paddingStart="24dp"
+                android:textColor="?android:attr/textColorSecondary"
+                style="@style/DataUsageBreakdownColumnLabel" />
+
+        </TableRow>
+
+    </TableLayout>
+
+    <Button
+        android:id="@+id/data_reduction_reset_statistics"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginTop="24dp"
+        android:text="@string/data_reduction_usage_reset_statistics_button"
+        android:textAllCaps="true"
+        android:textColor="@color/light_active_color"
+        android:textSize="14sp"
+        style="@style/ButtonCompatBorderless" />
+
+</org.chromium.chrome.browser.preferences.datareduction.DataReductionSiteBreakdownView>
diff --git a/chrome/android/java/res/layout/data_usage_breakdown_row.xml b/chrome/android/java/res/layout/data_usage_breakdown_row.xml
new file mode 100644
index 0000000..11b488d5
--- /dev/null
+++ b/chrome/android/java/res/layout/data_usage_breakdown_row.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<TableRow
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/site_row"
+    android:layout_width="match_parent">
+
+    <TextView
+        android:id="@+id/site_hostname"
+        android:layout_width="0dp"
+        android:layout_weight="0"
+        style="@style/DataUsageBreakdownColumnItem" />
+
+    <TextView
+        android:id="@+id/site_data_used"
+        android:layout_width="wrap_content"
+        android:gravity="end"
+        android:paddingStart="10dp"
+        style="@style/DataUsageBreakdownColumnItem" />
+
+    <TextView
+        android:id="@+id/site_data_saved"
+        android:layout_width="wrap_content"
+        android:gravity="end"
+        android:paddingStart="24dp"
+        style="@style/DataUsageBreakdownColumnItem" />
+</TableRow>
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml
index cb67fee..3cfc6e2 100644
--- a/chrome/android/java/res/values-v17/styles.xml
+++ b/chrome/android/java/res/values-v17/styles.xml
@@ -587,6 +587,22 @@
         <item name="android:textSize">14sp</item>
         <item name="android:visibility">gone</item>
     </style>
+    <style name="DataUsageBreakdownColumnLabel">
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:paddingBottom">10dp</item>
+        <item name="android:paddingTop">10dp</item>
+        <item name="android:singleLine">true</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:textAppearance">@style/RobotoMediumStyle</item>
+    </style>
+    <style name="DataUsageBreakdownColumnItem">
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:paddingBottom">10dp</item>
+        <item name="android:paddingTop">10dp</item>
+        <item name="android:singleLine">true</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textSize">14sp</item>
+    </style>
 
     <!-- New tab page RecyclerView overscroll color -->
     <style name="NewTabPageRecyclerView">
diff --git a/chrome/android/java/res/xml/single_website_preferences.xml b/chrome/android/java/res/xml/single_website_preferences.xml
index 854d8d1..0d171442 100644
--- a/chrome/android/java/res/xml/single_website_preferences.xml
+++ b/chrome/android/java/res/xml/single_website_preferences.xml
@@ -46,6 +46,8 @@
     <org.chromium.chrome.browser.preferences.ChromeBaseListPreference
         android:key="popup_permission_list" />
     <org.chromium.chrome.browser.preferences.ChromeBaseListPreference
+        android:key="subresource_filter_permission_list" />
+    <org.chromium.chrome.browser.preferences.ChromeBaseListPreference
         android:key="background_sync_permission_list" />
     <org.chromium.chrome.browser.preferences.ChromeBaseListPreference
         android:key="protected_media_identifier_permission_list" />
@@ -53,8 +55,6 @@
         android:key="autoplay_permission_list" />
     <org.chromium.chrome.browser.preferences.ChromeBaseListPreference
         android:key="midi_sysex_permission_list" />
-    <org.chromium.chrome.browser.preferences.ChromeBaseListPreference
-        android:key="subresource_filter_permission_list" />
 
     <org.chromium.chrome.browser.preferences.ButtonPreference
         android:key="reset_site_button"
diff --git a/chrome/android/java/res/xml/site_settings_preferences.xml b/chrome/android/java/res/xml/site_settings_preferences.xml
index 34e524a..ae118abc 100644
--- a/chrome/android/java/res/xml/site_settings_preferences.xml
+++ b/chrome/android/java/res/xml/site_settings_preferences.xml
@@ -39,6 +39,10 @@
     <org.chromium.chrome.browser.preferences.website.SiteSettingsPreference
         android:fragment="org.chromium.chrome.browser.preferences.website.SingleCategoryPreferences"
         android:key="popups" />
+    <!-- SubresourceFilter -->
+    <org.chromium.chrome.browser.preferences.website.SiteSettingsPreference
+        android:fragment="org.chromium.chrome.browser.preferences.website.SingleCategoryPreferences"
+        android:key="subresource_filter" />
     <!-- Background sync -->
     <org.chromium.chrome.browser.preferences.website.SiteSettingsPreference
         android:fragment="org.chromium.chrome.browser.preferences.website.SingleCategoryPreferences"
@@ -76,8 +80,4 @@
         android:key="usb"
         android:title="@string/website_settings_usb"
         android:icon="@drawable/settings_usb" />
-    <!-- SubresourceFilter -->
-    <org.chromium.chrome.browser.preferences.website.SiteSettingsPreference
-        android:fragment="org.chromium.chrome.browser.preferences.website.SingleCategoryPreferences"
-        android:key="subresource_filter" />
 </PreferenceScreen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index 2b5b237d..a3ac422 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -138,6 +138,7 @@
     // Enables the Data Reduction Proxy menu item in the main menu rather than under Settings on
     // Android.
     public static final String DATA_REDUCTION_MAIN_MENU = "DataReductionProxyMainMenu";
+    public static final String DATA_REDUCTION_SITE_BREAKDOWN = "DataReductionProxySiteBreakdown";
     // When enabled, fullscreen WebContents will be moved to a new Activity. Coming soon...
     public static final String FULLSCREEN_ACTIVITY = "FullscreenActivity";
     // Whether we show an important sites dialog in the "Clear Browsing Data" flow.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index ff9504bc..db9b8ee 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -1860,15 +1860,7 @@
         // If Chrome Home is enabled, the super of this function is not called because it only
         // performs unnecessary transformations on the theme color.
         if (getBottomSheet() != null) {
-            color = ApiCompatibilityUtils.getColor(getResources(), R.color.default_primary_color);
-            getBottomSheet().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
-
-            // Special case the incognito NTP and the tab switcher.
-            if ((tab != null && NewTabPage.isNTPUrl(tab.getUrl()) && tab.isIncognito())
-                    || isInOverviewMode()) {
-                color = Color.BLACK;
-            }
-            ApiCompatibilityUtils.setStatusBarColor(getWindow(), color);
+            getBottomSheet().setStatusBarColor(getWindow());
             return;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java
index 85c9587..2ccd407 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java
@@ -9,15 +9,20 @@
 import android.text.TextUtils;
 import android.webkit.URLUtil;
 
+import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.chrome.browser.preferences.datareduction.DataReductionDataUseItem;
+import org.chromium.chrome.browser.preferences.datareduction.DataReductionPromoUtils;
 
 import java.text.NumberFormat;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
@@ -69,6 +74,8 @@
 
     private static final String WEBLITE_QUERY_PARAM = "lite_url";
 
+    private Callback<List<DataReductionDataUseItem>> mQueryDataUsageCallback;
+
     /**
      * Returns whether the data reduction proxy is enabled.
      *
@@ -196,6 +203,16 @@
     }
 
     /**
+     * Clears all data saving statistics.
+     */
+    public void clearDataSavingStatistics() {
+        // When the data saving statistics are cleared, reset the snackbar promo that tells the user
+        // how much data they have saved using Data Saver so far.
+        DataReductionPromoUtils.saveSnackbarPromoDisplayed(0);
+        nativeClearDataSavingStatistics(mNativeDataReductionProxySettings);
+    }
+
+    /**
      * Returns aggregate original and received content lengths.
      * @return The content lengths.
      */
@@ -304,6 +321,35 @@
         return nativeAreLoFiPreviewsEnabled(mNativeDataReductionProxySettings);
     }
 
+    /**
+     * Queries native Data Reduction Proxy to get data use statistics. On query completion provides
+     * a list of DataReductionDataUseItem to the callback.
+     *
+     * @param numDays Number of days to get stats for.
+     * @param queryDataUsageCallback Callback to give the list of DataReductionDataUseItems on query
+     *            completion.
+     */
+    public void queryDataUsage(
+            int numDays, Callback<List<DataReductionDataUseItem>> queryDataUsageCallback) {
+        mQueryDataUsageCallback = queryDataUsageCallback;
+        nativeQueryDataUsage(mNativeDataReductionProxySettings,
+                new ArrayList<DataReductionDataUseItem>(), numDays);
+    }
+
+    @CalledByNative
+    public static void createDataUseItemAndAddToList(List<DataReductionDataUseItem> items,
+            String hostname, long dataUsed, long originalSize) {
+        items.add(new DataReductionDataUseItem(hostname, dataUsed, originalSize));
+    }
+
+    @CalledByNative
+    public void onQueryDataUsageComplete(List<DataReductionDataUseItem> items) {
+        if (mQueryDataUsageCallback != null) {
+            mQueryDataUsageCallback.onResult(items);
+        }
+        mQueryDataUsageCallback = null;
+    }
+
     private native long nativeInit();
     private native boolean nativeIsDataReductionProxyPromoAllowed(
             long nativeDataReductionProxySettingsAndroid);
@@ -315,6 +361,8 @@
             long nativeDataReductionProxySettingsAndroid, boolean enabled);
     private native long nativeGetDataReductionLastUpdateTime(
             long nativeDataReductionProxySettingsAndroid);
+    private native void nativeClearDataSavingStatistics(
+            long nativeDataReductionProxySettingsAndroid);
     private native ContentLengths nativeGetContentLengths(
             long nativeDataReductionProxySettingsAndroid);
     private native long nativeGetTotalHttpContentLengthSaved(
@@ -329,4 +377,6 @@
             long nativeDataReductionProxySettingsAndroid);
     private native String nativeGetHttpProxyList(long nativeDataReductionProxySettingsAndroid);
     private native String nativeGetLastBypassEvent(long nativeDataReductionProxySettingsAndroid);
+    private native void nativeQueryDataUsage(long nativeDataReductionProxySettingsAndroid,
+            List<DataReductionDataUseItem> items, int numDays);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionDataUseItem.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionDataUseItem.java
new file mode 100644
index 0000000..762689bd
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionDataUseItem.java
@@ -0,0 +1,73 @@
+// 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.
+
+package org.chromium.chrome.browser.preferences.datareduction;
+
+import android.content.Context;
+import android.text.format.Formatter;
+
+/**
+ * Stores the data used and saved by a hostname.
+ */
+public class DataReductionDataUseItem {
+    private String mHostname;
+    private long mDataUsed;
+    private long mOriginalSize;
+
+    /**
+     * Constructor for a DataReductionDataUseItem which associates a hostname with its data usage
+     * and savings.
+     *
+     * @param hostname The hostname associated with this data usage.
+     * @param dataUsed The amount of data used by the host.
+     * @param originalSize The original size of the data.
+     */
+    public DataReductionDataUseItem(String hostname, long dataUsed, long originalSize) {
+        mHostname = hostname;
+        mDataUsed = dataUsed;
+        mOriginalSize = originalSize;
+    }
+
+    /**
+     * Returns the hostname for this data use item.
+     * @return The hostname.
+     */
+    public String getHostname() {
+        return mHostname;
+    }
+
+    /**
+     * Returns the amount of data used by the associated hostname.
+     * @return The data used.
+     */
+    public long getDataUsed() {
+        return mDataUsed;
+    }
+
+    /**
+     * Returns the amount of data saved by the associated hostname.
+     * @return The data saved.
+     */
+    public long getDataSaved() {
+        return mOriginalSize - mDataUsed;
+    }
+
+    /**
+     * Returns a formatted String of the data used by the associated hostname.
+     * @param context An Android context.
+     * @return A formatted string of the data used.
+     */
+    public String getFormattedDataUsed(Context context) {
+        return Formatter.formatFileSize(context, mDataUsed);
+    }
+
+    /**
+     * Returns a formatted String of the data saved by the associated hostname.
+     * @param context An Android context.
+     * @return A formatted string of the data saved.
+     */
+    public String getFormattedDataSaved(Context context) {
+        return Formatter.formatFileSize(context, mOriginalSize - mDataUsed);
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreferences.java
index f39be50..767d0b7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreferences.java
@@ -4,14 +4,11 @@
 
 package org.chromium.chrome.browser.preferences.datareduction;
 
-import static org.chromium.third_party.android.datausagechart.ChartDataUsageView.DAYS_IN_CHART;
-
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.preference.Preference;
 import android.preference.Preference.OnPreferenceChangeListener;
 import android.preference.PreferenceFragment;
-import android.text.format.DateUtils;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
@@ -26,8 +23,6 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.snackbar.DataReductionPromoSnackbarController;
 import org.chromium.chrome.browser.util.IntentUtils;
-import org.chromium.third_party.android.datausagechart.NetworkStats;
-import org.chromium.third_party.android.datausagechart.NetworkStatsHistory;
 
 /**
  * Settings fragment that allows the user to configure Data Saver.
@@ -36,7 +31,6 @@
     public static final String FROM_MAIN_MENU = "FromMainMenu";
 
     public static final String PREF_DATA_REDUCTION_SWITCH = "data_reduction_switch";
-    private static final String PREF_DATA_REDUCTION_STATS = "data_reduction_stats";
 
     // This is the same as Chromium data_reduction_proxy::switches::kEnableDataReductionProxy.
     private static final String ENABLE_DATA_REDUCTION_PROXY = "enable-spdy-proxy-auth";
@@ -132,7 +126,6 @@
         createDataReductionSwitch(isEnabled);
         if (isEnabled) {
             addPreferencesFromResource(R.xml.data_reduction_preferences);
-            updateReductionStatistics();
         } else {
             addPreferencesFromResource(R.xml.data_reduction_preferences_off);
         }
@@ -140,22 +133,6 @@
     }
 
     /**
-     * Updates the preference screen to convey current statistics on data reduction.
-     */
-    public void updateReductionStatistics() {
-        DataReductionProxySettings config = DataReductionProxySettings.getInstance();
-
-        DataReductionStatsPreference statsPref = (DataReductionStatsPreference)
-                getPreferenceScreen().findPreference(PREF_DATA_REDUCTION_STATS);
-        long original[] = config.getOriginalNetworkStatsHistory();
-        long received[] = config.getReceivedNetworkStatsHistory();
-        statsPref.setReductionStats(
-                config.getDataReductionLastUpdateTime(),
-                getNetworkStatsHistory(original, DAYS_IN_CHART),
-                getNetworkStatsHistory(received, DAYS_IN_CHART));
-    }
-
-    /**
      * Returns summary string.
      */
     public static String generateSummary(Resources resources) {
@@ -169,25 +146,6 @@
         }
     }
 
-    private static NetworkStatsHistory getNetworkStatsHistory(long[] history, int days) {
-        if (days > history.length) days = history.length;
-        NetworkStatsHistory networkStatsHistory =
-                new NetworkStatsHistory(
-                        DateUtils.DAY_IN_MILLIS, days, NetworkStatsHistory.FIELD_RX_BYTES);
-
-        DataReductionProxySettings config = DataReductionProxySettings.getInstance();
-        long time = config.getDataReductionLastUpdateTime() - days * DateUtils.DAY_IN_MILLIS;
-        for (int i = history.length - days, bucket = 0; i < history.length; i++, bucket++) {
-            NetworkStats.Entry entry = new NetworkStats.Entry();
-            entry.rxBytes = history[i];
-            long startTime = time + (DateUtils.DAY_IN_MILLIS * bucket);
-            // Spread each day's record over the first hour of the day.
-            networkStatsHistory.recordData(
-                    startTime, startTime + DateUtils.HOUR_IN_MILLIS, entry);
-        }
-        return networkStatsHistory;
-    }
-
     private void createDataReductionSwitch(boolean isEnabled) {
         final ChromeSwitchPreference dataReductionSwitch =
                 new ChromeSwitchPreference(getActivity(), null);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionSiteBreakdownView.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionSiteBreakdownView.java
new file mode 100644
index 0000000..a2a1f97
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionSiteBreakdownView.java
@@ -0,0 +1,134 @@
+// 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.
+
+package org.chromium.chrome.browser.preferences.datareduction;
+
+import android.content.Context;
+import android.text.format.Formatter;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.widget.LinearLayout;
+import android.widget.TableLayout;
+import android.widget.TableRow;
+import android.widget.TextView;
+
+import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.chrome.R;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * A site breakdown view to be used by the Data Saver settings page. It displays the top ten sites
+ * with the most data use or data savings.
+ */
+public class DataReductionSiteBreakdownView extends LinearLayout {
+    private static final int NUM_DATA_USE_ITEMS_TO_DISPLAY = 10;
+
+    private TableLayout mTableLayout;
+
+    public DataReductionSiteBreakdownView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mTableLayout = (TableLayout) findViewById(R.id.data_reduction_proxy_breakdown_table);
+    }
+
+    /**
+     * Display the data use items once they have been fetched from the compression stats.
+     * @param items A list of items split by hostname to show in the breakdown.
+     */
+    public void onQueryDataUsageComplete(List<DataReductionDataUseItem> items) {
+        updateSiteBreakdown(items);
+    }
+
+    /**
+     * Sorts the DataReductionDataUseItems by most to least data used.
+     */
+    private static final class DataUsedComparator
+            implements Comparator<DataReductionDataUseItem>, Serializable {
+        @Override
+        public int compare(DataReductionDataUseItem lhs, DataReductionDataUseItem rhs) {
+            if (lhs.getDataUsed() < rhs.getDataUsed()) {
+                return 1;
+            } else if (lhs.getDataUsed() > rhs.getDataUsed()) {
+                return -1;
+            }
+            return 0;
+        }
+    }
+
+    /**
+     * Update the site breakdown to display the given date use items.
+     * @param items A list of items split by hostname to show in the breakdown.
+     */
+    private void updateSiteBreakdown(List<DataReductionDataUseItem> items) {
+        if (items.size() == 0) {
+            setVisibility(GONE);
+            return;
+        }
+
+        setVisibility(VISIBLE);
+        // Remove all old rows except the header.
+        mTableLayout.removeViews(1, mTableLayout.getChildCount() - 1);
+        final DataUsedComparator comp = new DataUsedComparator();
+        Collections.sort(items, comp);
+
+        int numRemainingSites = 0;
+        int everythingElseDataUsage = 0;
+        int everythingElseDataSavings = 0;
+
+        for (int i = 0; i < items.size(); i++) {
+            if (i < NUM_DATA_USE_ITEMS_TO_DISPLAY) {
+                TableRow row = (TableRow) LayoutInflater.from(getContext())
+                                       .inflate(R.layout.data_usage_breakdown_row, null);
+
+                TextView hostnameView = (TextView) row.findViewById(R.id.site_hostname);
+                TextView dataUsedView = (TextView) row.findViewById(R.id.site_data_used);
+                TextView dataSavedView = (TextView) row.findViewById(R.id.site_data_saved);
+
+                hostnameView.setText(items.get(i).getHostname());
+                dataUsedView.setText(items.get(i).getFormattedDataUsed(getContext()));
+                dataSavedView.setText(items.get(i).getFormattedDataSaved(getContext()));
+
+                mTableLayout.addView(row, i + 1);
+            } else {
+                numRemainingSites++;
+                everythingElseDataUsage += items.get(i).getDataUsed();
+                everythingElseDataSavings += items.get(i).getDataSaved();
+            }
+        }
+
+        if (numRemainingSites > 0) {
+            TableRow row = (TableRow) LayoutInflater.from(getContext())
+                                   .inflate(R.layout.data_usage_breakdown_row, null);
+
+            TextView hostnameView = (TextView) row.findViewById(R.id.site_hostname);
+            TextView dataUsedView = (TextView) row.findViewById(R.id.site_data_used);
+            TextView dataSavedView = (TextView) row.findViewById(R.id.site_data_saved);
+
+            hostnameView.setText(getResources().getString(
+                    R.string.data_reduction_breakdown_remaining_sites_label, numRemainingSites));
+            dataUsedView.setText(Formatter.formatFileSize(getContext(), everythingElseDataUsage));
+            dataSavedView.setText(
+                    Formatter.formatFileSize(getContext(), everythingElseDataSavings));
+
+            int lightActiveColor = ApiCompatibilityUtils.getColor(
+                    getContext().getResources(), R.color.light_active_color);
+
+            hostnameView.setTextColor(lightActiveColor);
+            dataUsedView.setTextColor(lightActiveColor);
+            dataSavedView.setTextColor(lightActiveColor);
+
+            mTableLayout.addView(row, NUM_DATA_USE_ITEMS_TO_DISPLAY + 1);
+        }
+
+        mTableLayout.requestLayout();
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java
index ee08ac48..4c3f177 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java
@@ -16,13 +16,19 @@
 import android.text.format.Formatter;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
 import android.widget.TextView;
 
+import org.chromium.base.Callback;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
 import org.chromium.third_party.android.datausagechart.ChartDataUsageView;
+import org.chromium.third_party.android.datausagechart.NetworkStats;
 import org.chromium.third_party.android.datausagechart.NetworkStatsHistory;
 
+import java.util.List;
 import java.util.TimeZone;
 
 /**
@@ -34,54 +40,32 @@
 
     private TextView mOriginalSizeTextView;
     private TextView mReceivedSizeTextView;
+    private TextView mDataSavingsTextView;
+    private TextView mDataUsageTextView;
     private TextView mPercentReductionTextView;
     private TextView mStartDateTextView;
     private TextView mEndDateTextView;
+    private Button mResetStatisticsButton;
     private ChartDataUsageView mChartDataUsageView;
+    private DataReductionSiteBreakdownView mDataReductionBreakdownView;
     private long mLeftPosition;
     private long mRightPosition;
     private Long mCurrentTime;
     private String mOriginalTotalPhrase;
+    private String mSavingsTotalPhrase;
     private String mReceivedTotalPhrase;
     private String mPercentReductionPhrase;
     private String mStartDatePhrase;
     private String mEndDatePhrase;
 
-    public DataReductionStatsPreference(
-            Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        setWidgetLayoutResource(R.layout.data_reduction_stats_layout);
-    }
-
     public DataReductionStatsPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
-        setWidgetLayoutResource(R.layout.data_reduction_stats_layout);
-    }
 
-    public DataReductionStatsPreference(Context context) {
-        super(context);
-        setWidgetLayoutResource(R.layout.data_reduction_stats_layout);
-    }
-
-    /**
-     * Sets the current statistics for viewing. These include the original total daily size of
-     * received resources before compression, and the actual total daily size of received
-     * resources after compression. The last update time is specified in milliseconds since the
-     * epoch.
-     * @param lastUpdateTimeMillis The last time the statistics were updated.
-     * @param networkStatsHistoryOriginal The history of original content lengths.
-     * @param networkStatsHistoryReceived The history of received content lengths.
-     */
-    public void setReductionStats(
-            long lastUpdateTimeMillis,
-            NetworkStatsHistory networkStatsHistoryOriginal,
-            NetworkStatsHistory networkStatsHistoryReceived) {
-        mCurrentTime = lastUpdateTimeMillis;
-        mRightPosition = mCurrentTime + DateUtils.HOUR_IN_MILLIS
-                - TimeZone.getDefault().getOffset(mCurrentTime);
-        mLeftPosition = lastUpdateTimeMillis - DateUtils.DAY_IN_MILLIS * DAYS_IN_CHART;
-        mOriginalNetworkStatsHistory = networkStatsHistoryOriginal;
-        mReceivedNetworkStatsHistory = networkStatsHistoryReceived;
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.DATA_REDUCTION_SITE_BREAKDOWN)) {
+            setWidgetLayoutResource(R.layout.data_reduction_stats_layout);
+        } else {
+            setWidgetLayoutResource(R.layout.data_reduction_old_stats_layout);
+        }
     }
 
     @Override
@@ -90,23 +74,76 @@
     }
 
     /**
+     * Updates the preference screen to convey current statistics on data reduction.
+     */
+    public void updateReductionStatistics() {
+        long original[] = DataReductionProxySettings.getInstance().getOriginalNetworkStatsHistory();
+        long received[] = DataReductionProxySettings.getInstance().getReceivedNetworkStatsHistory();
+
+        mCurrentTime = DataReductionProxySettings.getInstance().getDataReductionLastUpdateTime();
+        mRightPosition = mCurrentTime + DateUtils.HOUR_IN_MILLIS
+                - TimeZone.getDefault().getOffset(mCurrentTime);
+        mLeftPosition = mCurrentTime - DateUtils.DAY_IN_MILLIS * DAYS_IN_CHART;
+        mOriginalNetworkStatsHistory = getNetworkStatsHistory(original, DAYS_IN_CHART);
+        mReceivedNetworkStatsHistory = getNetworkStatsHistory(received, DAYS_IN_CHART);
+
+        if (mDataReductionBreakdownView != null) {
+            DataReductionProxySettings.getInstance().queryDataUsage(
+                    DAYS_IN_CHART, new Callback<List<DataReductionDataUseItem>>() {
+                        @Override
+                        public void onResult(List<DataReductionDataUseItem> result) {
+                            mDataReductionBreakdownView.onQueryDataUsageComplete(result);
+                        }
+                    });
+        }
+    }
+
+    private static NetworkStatsHistory getNetworkStatsHistory(long[] history, int days) {
+        if (days > history.length) days = history.length;
+        NetworkStatsHistory networkStatsHistory = new NetworkStatsHistory(
+                DateUtils.DAY_IN_MILLIS, days, NetworkStatsHistory.FIELD_RX_BYTES);
+
+        DataReductionProxySettings config = DataReductionProxySettings.getInstance();
+        long time = config.getDataReductionLastUpdateTime() - days * DateUtils.DAY_IN_MILLIS;
+        for (int i = history.length - days, bucket = 0; i < history.length; i++, bucket++) {
+            NetworkStats.Entry entry = new NetworkStats.Entry();
+            entry.rxBytes = history[i];
+            long startTime = time + (DateUtils.DAY_IN_MILLIS * bucket);
+            // Spread each day's record over the first hour of the day.
+            networkStatsHistory.recordData(startTime, startTime + DateUtils.HOUR_IN_MILLIS, entry);
+        }
+        return networkStatsHistory;
+    }
+
+    private void setDetailText() {
+        updateDetailData();
+        mPercentReductionTextView.setText(mPercentReductionPhrase);
+        mStartDateTextView.setText(mStartDatePhrase);
+        mEndDateTextView.setText(mEndDatePhrase);
+        if (mDataUsageTextView != null) mDataUsageTextView.setText(mReceivedTotalPhrase);
+        if (mDataSavingsTextView != null) mDataSavingsTextView.setText(mSavingsTotalPhrase);
+        if (mOriginalSizeTextView != null) mOriginalSizeTextView.setText(mOriginalTotalPhrase);
+        if (mReceivedSizeTextView != null) mReceivedSizeTextView.setText(mReceivedTotalPhrase);
+    }
+
+    /**
      * Sets up a data usage chart and text views containing data reduction statistics.
      * @param view The current view.
      */
     @Override
     protected void onBindView(View view) {
         super.onBindView(view);
-        if (mOriginalTotalPhrase == null) updateDetailData();
+        mDataUsageTextView = (TextView) view.findViewById(R.id.data_reduction_usage);
+        mDataSavingsTextView = (TextView) view.findViewById(R.id.data_reduction_savings);
         mOriginalSizeTextView = (TextView) view.findViewById(R.id.data_reduction_original_size);
-        mOriginalSizeTextView.setText(mOriginalTotalPhrase);
         mReceivedSizeTextView = (TextView) view.findViewById(R.id.data_reduction_compressed_size);
-        mReceivedSizeTextView.setText(mReceivedTotalPhrase);
         mPercentReductionTextView = (TextView) view.findViewById(R.id.data_reduction_percent);
-        mPercentReductionTextView.setText(mPercentReductionPhrase);
         mStartDateTextView = (TextView) view.findViewById(R.id.data_reduction_start_date);
-        mStartDateTextView.setText(mStartDatePhrase);
         mEndDateTextView = (TextView) view.findViewById(R.id.data_reduction_end_date);
-        mEndDateTextView.setText(mEndDatePhrase);
+        mDataReductionBreakdownView =
+                (DataReductionSiteBreakdownView) view.findViewById(R.id.breakdown);
+        updateReductionStatistics();
+        setDetailText();
 
         mChartDataUsageView = (ChartDataUsageView) view.findViewById(R.id.chart);
         mChartDataUsageView.bindOriginalNetworkStats(mOriginalNetworkStatsHistory);
@@ -122,6 +159,19 @@
         } else {
             dataReductionProxyUnreachableWarning.setVisibility(View.GONE);
         }
+
+        mResetStatisticsButton = (Button) view.findViewById(R.id.data_reduction_reset_statistics);
+        if (mResetStatisticsButton != null) {
+            mResetStatisticsButton.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View view) {
+                    DataReductionProxySettings.getInstance().clearDataSavingStatistics();
+                    updateReductionStatistics();
+                    setDetailText();
+                    notifyChanged();
+                }
+            });
+        }
     }
 
     /**
@@ -140,18 +190,20 @@
         final long now = mCurrentTime;
         final Context context = getContext();
 
-        NetworkStatsHistory.Entry originalEntry =
-                mOriginalNetworkStatsHistory.getValues(start, end, now, null);
-        // Only received bytes are tracked.
-        final long originalTotalBytes = originalEntry.rxBytes;
-        mOriginalTotalPhrase = Formatter.formatFileSize(context, originalTotalBytes);
-
         NetworkStatsHistory.Entry compressedEntry =
                 mReceivedNetworkStatsHistory.getValues(start, end, now, null);
         // Only received bytes are tracked.
         final long compressedTotalBytes = compressedEntry.rxBytes;
         mReceivedTotalPhrase = Formatter.formatFileSize(context, compressedTotalBytes);
 
+        NetworkStatsHistory.Entry originalEntry =
+                mOriginalNetworkStatsHistory.getValues(start, end, now, null);
+        // Only received bytes are tracked.
+        final long originalTotalBytes = originalEntry.rxBytes;
+        mOriginalTotalPhrase = Formatter.formatFileSize(context, originalTotalBytes);
+        mSavingsTotalPhrase =
+                Formatter.formatFileSize(context, originalTotalBytes - compressedTotalBytes);
+
         float percentage = 0.0f;
         if (originalTotalBytes > 0L && originalTotalBytes > compressedTotalBytes) {
             percentage = (originalTotalBytes - compressedTotalBytes) / (float) originalTotalBytes;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
index 45ffb7f..0004325 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
@@ -9,7 +9,9 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.graphics.Color;
 import android.graphics.Region;
+import android.os.Build;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 import android.util.AttributeSet;
@@ -17,6 +19,7 @@
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
+import android.view.Window;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
@@ -29,6 +32,7 @@
 import org.chromium.chrome.browser.TabLoadStatus;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.ntp.NativePageFactory;
+import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
@@ -402,6 +406,32 @@
     }
 
     /**
+     * Set the window's status bar color. On Android M and above, this will set the status bar color
+     * to the default theme color with dark icons except in the case of the tab switcher and
+     * incognito NTP. On Android versions < M, the status bar will always be black.
+     * @param window The Android window.
+     */
+    public void setStatusBarColor(Window window) {
+        Tab tab = getActiveTab();
+        boolean isInOverviewMode = tab != null && tab.getActivity().isInOverviewMode();
+        boolean isIncognitoNtp =
+                tab != null && NewTabPage.isNTPUrl(tab.getUrl()) && tab.isIncognito();
+        boolean isValidAndroidVersion = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
+
+        int color = ApiCompatibilityUtils.getColor(getResources(), R.color.default_primary_color);
+        setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+
+        // Special case the incognito NTP and the tab switcher.
+        if (!isValidAndroidVersion || isIncognitoNtp || isInOverviewMode) {
+            color = Color.BLACK;
+            // The light status bar flag is always set above, meaning XORing that value with the
+            // current flags will remove it.
+            setSystemUiVisibility(getSystemUiVisibility() ^ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+        }
+        ApiCompatibilityUtils.setStatusBarColor(window, color);
+    }
+
+    /**
      * @return Whether or not the toolbar Android View is hidden due to being scrolled off-screen.
      */
     private boolean isToolbarAndroidViewHidden() {
@@ -508,7 +538,7 @@
 
     @Override
     public Tab getActiveTab() {
-        return mTabModelSelector.getCurrentTab();
+        return mTabModelSelector == null ? null : mTabModelSelector.getCurrentTab();
     }
 
     @Override
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 372e2fb..f76d50ec 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -978,6 +978,41 @@
       <message name="IDS_DATA_REDUCTION_MENU_ITEM_SUMMARY" desc="Summary text for data reduction menu item.">
         <ph name="PERCENT">%1$s<ex>49%</ex></ph> data savings
       </message>
+      <message name="IDS_DATA_REDUCTION_SAVINGS_LABEL" desc="Data Reduction statistics data savings label">
+        Data Savings:
+      </message>
+      <message name="IDS_DATA_REDUCTION_USAGE_LABEL" desc="Data Reduction statistics data usage label">
+        Data Usage:
+      </message>
+      <message name="IDS_DATA_REDUCTION_PROXY_UNREACHABLE_WARN" desc="Warning message shown when Google data reduction proxy servers are not reachable.">
+        Chrome is unable to reach Google servers for data compression. Your data savings may be limited.
+      </message>
+      <message name="IDS_DATA_REDUCTION_DATA_USAGE_BREAKDOWN_TITLE" desc="Title for the data usage breakdown on the Data Reduction statistics page. The breakdown lists the top ten sites with the greatest amount of data usage or mobile data that was saved.">
+        Data Usage Breakdown
+      </message>
+      <message name="IDS_DATA_REDUCTION_BREAKDOWN_SITE_TITLE" desc="Title for the sites column on the Data Reduction statistics page. The breakdown lists the top ten sites with the greatest amount of data usage or mobile data that was saved.">
+        Site
+      </message>
+      <message name="IDS_DATA_REDUCTION_BREAKDOWN_USED_TITLE" desc="Title for the data used column on the Data Reduction statistics page. The breakdown lists the top ten sites with the greatest amount of data usage or mobile data that was saved.">
+        Used
+      </message>
+      <message name="IDS_DATA_REDUCTION_BREAKDOWN_SAVED_TITLE" desc="Title for the data saved column on the Data Reduction statistics page. The breakdown lists for the top ten sites with the greatest amount of data usage or mobile data that was saved. Data Saver allows users to to reduce their mobile data usage by compressing network traffic.">
+        Saved
+      </message>
+      <message name="IDS_DATA_REDUCTION_BREAKDOWN_REMAINING_SITES_LABEL" desc="Title for the the remaining sites on the Data Reduction statistics page. The breakdown lists the top ten sites with the greatest amount of data usage or mobile data that was saved and then groups the remaining sites together.">
+        Remaining sites (<ph name="NUMBER_OF_SITES">%1$d<ex>35</ex></ph>)
+      </message>
+      <message name="IDS_DATA_REDUCTION_USAGE_RESET_STATISTICS_BUTTON" desc="Text to be displayed on the button to reset the Data Reduction statistics.">
+        Reset Statistics
+      </message>
+      <message name="IDS_SAFE_BROWSING_DESCRIPTION" desc="Description text for safe browsing">
+        Chrome’s Safe Browsing system will also be used to detect malicious pages and protect you from phishing, malware, and harmful downloads.
+      </message>
+      <message name="IDS_DATA_REDUCTION_CAVEATS_DESCRIPTION" desc="Description text for usage caveats">
+        This feature may interfere with access to premium data services provided by your carrier.
+      </message>
+      
+      <!-- Old Data Saver stats page strings-->
       <message name="IDS_DATA_REDUCTION_STATS_TITLE" desc="Data reduction statistics title.">
         Data savings
       </message>
@@ -987,15 +1022,6 @@
       <message name="IDS_DATA_REDUCTION_COMPRESSED_SIZE_LABEL" desc="Data Reduction statistics compressed size label">
         After compression
       </message>
-      <message name="IDS_DATA_REDUCTION_PROXY_UNREACHABLE_WARN" desc="Warning message shown when Google data reduction proxy servers are not reachable.">
-        Chrome is unable to reach Google servers for data compression. Your data savings may be limited.
-      </message>
-      <message name="IDS_SAFE_BROWSING_DESCRIPTION" desc="Description text for safe browsing">
-        Chrome’s Safe Browsing system will also be used to detect malicious pages and protect you from phishing, malware, and harmful downloads.
-      </message>
-      <message name="IDS_DATA_REDUCTION_CAVEATS_DESCRIPTION" desc="Description text for usage caveats">
-        This feature may interfere with access to premium data services provided by your carrier.
-      </message>
 
       <!-- Data Saver Promo and FRE card -->
       <message name="IDS_DATA_REDUCTION_PROMO_TITLE" desc="The title for the promo inviting users to enable Data Saver" >
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 8d9dd54c..ded717d 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -854,11 +854,13 @@
   "java/src/org/chromium/chrome/browser/preferences/autofill/AutofillServerProfilePreferences.java",
   "java/src/org/chromium/chrome/browser/preferences/autofill/CreditCardNumberFormattingTextWatcher.java",
   "java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuFooter.java",
+  "java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionSiteBreakdownView.java",
   "java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreferences.java",
   "java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPromoScreen.java",
   "java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPromoUtils.java",
   "java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionProxyUma.java",
   "java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java",
+  "java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionDataUseItem.java",
   "java/src/org/chromium/chrome/browser/preferences/password/PasswordEntryEditor.java",
   "java/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferences.java",
   "java/src/org/chromium/chrome/browser/preferences/privacy/BandwidthType.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBasicCardTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBasicCardTest.java
index a2a2e53..317c0bc 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBasicCardTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBasicCardTest.java
@@ -77,7 +77,7 @@
         expectResultContains(new String[] {"true"});
 
         clickNodeAndWait("checkBasicMasterCard", mCanMakePaymentQueryResponded);
-        expectResultContains(new String[] {"Query quota exceeded"});
+        expectResultContains(new String[] {"Not allowed to check whether can make payment"});
 
         clickNodeAndWait("checkBasicVisa", mCanMakePaymentQueryResponded);
         expectResultContains(new String[] {"true"});
@@ -91,7 +91,7 @@
         expectResultContains(new String[] {"true"});
 
         clickNodeAndWait("checkBasicDebit", mCanMakePaymentQueryResponded);
-        expectResultContains(new String[] {"Query quota exceeded"});
+        expectResultContains(new String[] {"Not allowed to check whether can make payment"});
 
         clickNodeAndWait("checkBasicVisa", mCanMakePaymentQueryResponded);
         expectResultContains(new String[] {"true"});
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCcCanMakePaymentQueryTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCcCanMakePaymentQueryTest.java
index 9cb69a76..dd11ccbb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCcCanMakePaymentQueryTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCcCanMakePaymentQueryTest.java
@@ -49,7 +49,7 @@
 
         // Different queries are throttled for a period of time.
         clickNodeAndWait("other-buy", mCanMakePaymentQueryResponded);
-        expectResultContains(new String[]{"Query quota exceeded"});
+        expectResultContains(new String[] {"Not allowed to check whether can make payment"});
 
         // Repeating the same query again does not count against the quota.
         clickNodeAndWait("buy", mCanMakePaymentQueryResponded);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppCanMakePaymentQueryTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppCanMakePaymentQueryTest.java
index fd3b2502..92ce4bea 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppCanMakePaymentQueryTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppCanMakePaymentQueryTest.java
@@ -31,7 +31,7 @@
         expectResultContains(new String[] {"false, false"});
 
         clickNodeAndWait("otherBuy", mCanMakePaymentQueryResponded);
-        expectResultContains(new String[] {"false, QuotaExceededError"});
+        expectResultContains(new String[] {"false, NotAllowedError"});
     }
 
     @MediumTest
@@ -44,7 +44,7 @@
         installPaymentApp(HAVE_INSTRUMENTS, IMMEDIATE_RESPONSE);
 
         clickNodeAndWait("otherBuy", mCanMakePaymentQueryResponded);
-        expectResultContains(new String[] {"true, QuotaExceededError"});
+        expectResultContains(new String[] {"true, NotAllowedError"});
     }
 
     @MediumTest
@@ -56,7 +56,7 @@
         expectResultContains(new String[] {"false, false"});
 
         clickNodeAndWait("otherBuy", mCanMakePaymentQueryResponded);
-        expectResultContains(new String[] {"false, QuotaExceededError"});
+        expectResultContains(new String[] {"false, NotAllowedError"});
     }
 
     @MediumTest
@@ -68,7 +68,7 @@
         expectResultContains(new String[] {"false, false"});
 
         clickNodeAndWait("otherBuy", mCanMakePaymentQueryResponded);
-        expectResultContains(new String[] {"false, QuotaExceededError"});
+        expectResultContains(new String[] {"false, NotAllowedError"});
     }
 
     @MediumTest
@@ -80,7 +80,7 @@
         expectResultContains(new String[] {"true, true"});
 
         clickNodeAndWait("otherBuy", mCanMakePaymentQueryResponded);
-        expectResultContains(new String[] {"true, QuotaExceededError"});
+        expectResultContains(new String[] {"true, NotAllowedError"});
     }
 
     @MediumTest
@@ -92,6 +92,6 @@
         expectResultContains(new String[] {"true, true"});
 
         clickNodeAndWait("otherBuy", mCanMakePaymentQueryResponded);
-        expectResultContains(new String[] {"true, QuotaExceededError"});
+        expectResultContains(new String[] {"true, NotAllowedError"});
     }
 }
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index c85227e..79df369 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -5798,6 +5798,12 @@
   <message name="IDS_FIRST_RUN_NEXT_BUTTON" desc="Text on the first-run tutorial button leading to next step of tutorial.">
     Next
   </message>
+  <message name="IDS_FIRST_RUN_ACCESSIBLE_TITLE" desc="Accessible title of the first screen in the first run app.">
+    Welcome
+  </message>
+  <message name="IDS_FIRST_RUN_STEP_ACCESSIBLE_TITLE" desc="Accessible title for all first run app screens except for the first screen.">
+    Tour
+  </message>
 
   <!-- Network portal notification -->
   <message name="IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIRED" desc="Title for the system notification that current wired network is behind captive portal">
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 8022a99..983bfcf 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1121,6 +1121,8 @@
     "resources_util.h",
     "safe_browsing/safe_browsing_tab_observer.cc",
     "safe_browsing/safe_browsing_tab_observer.h",
+    "safe_browsing/srt_chrome_prompt_impl.cc",
+    "safe_browsing/srt_chrome_prompt_impl.h",
     "safe_browsing/srt_client_info_win.cc",
     "safe_browsing/srt_client_info_win.h",
     "safe_browsing/srt_fetcher_win.cc",
@@ -1444,6 +1446,7 @@
     "//components/captive_portal",
     "//components/certificate_reporting",
     "//components/certificate_transparency",
+    "//components/chrome_cleaner/public/interfaces",
     "//components/cloud_devices/common",
     "//components/component_updater",
     "//components/content_settings/core/browser",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index d689ae41..aa20d19 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -35,6 +35,7 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/features.h"
+#include "chrome/common/pause_tabs_field_trial.h"
 #include "chrome/grit/chromium_strings.h"
 #include "components/autofill/core/browser/autofill_experiments.h"
 #include "components/autofill/core/common/autofill_switches.h"
@@ -99,6 +100,7 @@
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/android/chrome_feature_list.h"
+#include "components/feature_engagement_tracker/public/feature_constants.h"
 #else  // OS_ANDROID
 #include "ui/message_center/message_center_switches.h"
 #endif  // OS_ANDROID
@@ -713,6 +715,36 @@
         {"Learning", kSpeculativeResourcePrefetchingLearning,
          arraysize(kSpeculativeResourcePrefetchingLearning), nullptr}};
 
+#if defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) || \
+    defined(OS_WIN)
+const FeatureEntry::FeatureParam kPauseBackgroundTabsMinimalEngagment[] = {
+    {pausetabs::kFeatureName, pausetabs::kModeParamMinimal}};
+
+const FeatureEntry::FeatureParam kPauseBackgroundTabsLowEngagment[] = {
+    {pausetabs::kFeatureName, pausetabs::kModeParamLow}};
+
+const FeatureEntry::FeatureParam kPauseBackgroundTabsMediumEngagment[] = {
+    {pausetabs::kFeatureName, pausetabs::kModeParamMedium}};
+
+const FeatureEntry::FeatureParam kPauseBackgroundTabsHighEngagment[] = {
+    {pausetabs::kFeatureName, pausetabs::kModeParamHigh}};
+
+const FeatureEntry::FeatureParam kPauseBackgroundTabsMaxEngagment[] = {
+    {pausetabs::kFeatureName, pausetabs::kModeParamMax}};
+
+const FeatureEntry::FeatureVariation kPauseBackgroundTabsVariations[] = {
+    {"minimal engagement", kPauseBackgroundTabsMinimalEngagment,
+     arraysize(kPauseBackgroundTabsMinimalEngagment), nullptr},
+    {"low engagement", kPauseBackgroundTabsLowEngagment,
+     arraysize(kPauseBackgroundTabsLowEngagment), nullptr},
+    {"medium engagement", kPauseBackgroundTabsMediumEngagment,
+     arraysize(kPauseBackgroundTabsMediumEngagment), nullptr},
+    {"high engagement", kPauseBackgroundTabsHighEngagment,
+     arraysize(kPauseBackgroundTabsHighEngagment), nullptr},
+    {"max engagement", kPauseBackgroundTabsMaxEngagment,
+     arraysize(kPauseBackgroundTabsMaxEngagment), nullptr}};
+#endif
+
 #if defined(OS_ANDROID)
 const FeatureEntry::FeatureParam
 kAutofillCreditCardPopupLayoutFeatureVariationIconAtStart[] = {
@@ -1529,6 +1561,11 @@
      flag_descriptions::kChromeHomeDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kChromeHomeFeature)},
 #endif  // OS_ANDROID
+#if defined(OS_ANDROID)
+    {"enable-iph-demo-mode", flag_descriptions::kEnableIphDemoMode,
+     flag_descriptions::kEnableIphDemoModeDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(feature_engagement_tracker::kIPHDemoMode)},
+#endif  // OS_ANDROID
     {"num-raster-threads", flag_descriptions::kNumRasterThreadsName,
      flag_descriptions::kNumRasterThreadsDescription, kOsAll,
      MULTI_VALUE_TYPE(kNumRasterThreadsChoices)},
@@ -2576,6 +2613,15 @@
      FEATURE_VALUE_TYPE(chrome::android::kCustomContextMenu)},
 #endif  // OS_ANDROID
 
+#if defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) || \
+    defined(OS_WIN)
+    {pausetabs::kFeatureName, flag_descriptions::kPauseBackgroundTabsName,
+     flag_descriptions::kPauseBackgroundTabsDescription, kOsDesktop,
+     FEATURE_WITH_PARAMS_VALUE_TYPE(pausetabs::kFeature,
+                                    kPauseBackgroundTabsVariations,
+                                    "PauseBackgroundTabs")},
+#endif
+
 #if defined(USE_ASH)
     {"ash-enable-smooth-screen-rotation",
      flag_descriptions::kAshEnableSmoothScreenRotationName,
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index e89f517..b1e79b3a 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -59,6 +59,7 @@
     &kCustomContextMenu,
     &kCustomFeedbackUi,
     &data_reduction_proxy::features::kDataReductionMainMenu,
+    &data_reduction_proxy::features::kDataReductionSiteBreakdown,
     &kFullscreenActivity,
     &kImportantSitesInCBD,
     &kImprovedA2HS,
diff --git a/chrome/browser/android/offline_pages/background_loader_offliner.cc b/chrome/browser/android/offline_pages/background_loader_offliner.cc
index 603263f8..f8f86b6 100644
--- a/chrome/browser/android/offline_pages/background_loader_offliner.cc
+++ b/chrome/browser/android/offline_pages/background_loader_offliner.cc
@@ -201,14 +201,11 @@
   return false;
 }
 
-void BackgroundLoaderOffliner::DocumentLoadedInFrame(
-    content::RenderFrameHost* render_host) {
-  // Inform snapshot controller if in main frame.
-  if (!render_host->GetParent())
-    snapshot_controller_->DocumentAvailableInMainFrame();
+void BackgroundLoaderOffliner::DocumentAvailableInMainFrame() {
+  snapshot_controller_->DocumentAvailableInMainFrame();
 }
 
-void BackgroundLoaderOffliner::DidStopLoading() {
+void BackgroundLoaderOffliner::DocumentOnLoadCompletedInMainFrame() {
   if (!pending_request_.get()) {
     DVLOG(1) << "DidStopLoading called even though no pending request.";
     return;
diff --git a/chrome/browser/android/offline_pages/background_loader_offliner.h b/chrome/browser/android/offline_pages/background_loader_offliner.h
index 608e88b..8d63c3c3 100644
--- a/chrome/browser/android/offline_pages/background_loader_offliner.h
+++ b/chrome/browser/android/offline_pages/background_loader_offliner.h
@@ -47,8 +47,8 @@
   bool HandleTimeout(const SavePageRequest& request) override;
 
   // WebContentsObserver implementation.
-  void DocumentLoadedInFrame(content::RenderFrameHost* render_frame) override;
-  void DidStopLoading() override;
+  void DocumentAvailableInMainFrame() override;
+  void DocumentOnLoadCompletedInMainFrame() override;
   void RenderProcessGone(base::TerminationStatus status) override;
   void WebContentsDestroyed() override;
   void DidFinishNavigation(
diff --git a/chrome/browser/android/offline_pages/background_loader_offliner_unittest.cc b/chrome/browser/android/offline_pages/background_loader_offliner_unittest.cc
index da2efcd4..d9078c4 100644
--- a/chrome/browser/android/offline_pages/background_loader_offliner_unittest.cc
+++ b/chrome/browser/android/offline_pages/background_loader_offliner_unittest.cc
@@ -147,14 +147,13 @@
   const base::HistogramTester& histograms() const { return histogram_tester_; }
   int64_t progress() { return progress_; }
 
-  void CompleteLoading() {
-    // For some reason, setting loading to True will call DidStopLoading
-    // on the observers.
-    offliner()->web_contents_tester()->TestSetIsLoading(true);
-  }
-
   void PumpLoop() { base::RunLoop().RunUntilIdle(); }
 
+  void CompleteLoading() {
+    offliner()->DocumentOnLoadCompletedInMainFrame();
+    PumpLoop();
+  }
+
  private:
   void OnCompletion(const SavePageRequest& request,
                     Offliner::RequestStatus status);
@@ -433,7 +432,7 @@
       "OfflinePages.Background.BackgroundLoadingFailedCode.async_loading",
       105,  // ERR_NAME_NOT_RESOLVED
       1);
-  offliner()->DidStopLoading();
+  CompleteLoading();
   PumpLoop();
 
   EXPECT_TRUE(completion_callback_called());
@@ -457,7 +456,7 @@
   offliner()->DidFinishNavigation(handle.get());
   // NavigationHandle is always destroyed after finishing navigation.
   handle.reset();
-  offliner()->DidStopLoading();
+  CompleteLoading();
   PumpLoop();
 
   EXPECT_TRUE(completion_callback_called());
@@ -473,7 +472,7 @@
   // First load
   CompleteLoading();
   // Second load
-  offliner()->DidStopLoading();
+  CompleteLoading();
   PumpLoop();
   model()->CompleteSavingAsSuccess();
   PumpLoop();
diff --git a/chrome/browser/android/vr_shell/BUILD.gn b/chrome/browser/android/vr_shell/BUILD.gn
index 753ae749..d73399c 100644
--- a/chrome/browser/android/vr_shell/BUILD.gn
+++ b/chrome/browser/android/vr_shell/BUILD.gn
@@ -47,8 +47,6 @@
       "vr_gl_util.h",
       "vr_input_manager.cc",
       "vr_input_manager.h",
-      "vr_math.cc",
-      "vr_math.h",
       "vr_omnibox.cc",
       "vr_omnibox.h",
       "vr_shell.cc",
@@ -128,9 +126,8 @@
 
     # Ensure libgvr static library appears before gcc library in linking order.
     # See https://crbug.com/704305 for details.
-    libs = [
-      "//third_party/gvr-android-sdk/libgvr_shim_static_${current_cpu}.a",
-    ]
+    libs =
+        [ "//third_party/gvr-android-sdk/libgvr_shim_static_${current_cpu}.a" ]
 
     data = [
       "test/data/sample_inline.gltf",
diff --git a/chrome/browser/android/vr_shell/non_presenting_gvr_delegate.cc b/chrome/browser/android/vr_shell/non_presenting_gvr_delegate.cc
index 79d92e3..f8cc3e1 100644
--- a/chrome/browser/android/vr_shell/non_presenting_gvr_delegate.cc
+++ b/chrome/browser/android/vr_shell/non_presenting_gvr_delegate.cc
@@ -155,9 +155,9 @@
     return;
   }
 
-  gvr::Sizei webvr_size = GvrDelegate::GetRecommendedWebVrSize(gvr_api_.get());
-  DVLOG(1) << __FUNCTION__ << ": resize recommended to " << webvr_size.width
-           << "x" << webvr_size.height;
+  gfx::Size webvr_size = GvrDelegate::GetRecommendedWebVrSize(gvr_api_.get());
+  DVLOG(1) << __FUNCTION__ << ": resize recommended to " << webvr_size.width()
+           << "x" << webvr_size.height();
   callback.Run(
       GvrDelegate::CreateVRDisplayInfo(gvr_api_.get(), webvr_size, device_id));
 }
diff --git a/chrome/browser/android/vr_shell/non_presenting_gvr_delegate.h b/chrome/browser/android/vr_shell/non_presenting_gvr_delegate.h
index c384edf..d921ea4 100644
--- a/chrome/browser/android/vr_shell/non_presenting_gvr_delegate.h
+++ b/chrome/browser/android/vr_shell/non_presenting_gvr_delegate.h
@@ -31,9 +31,9 @@
   void SubmitWebVRFrame(int16_t frame_index,
                         const gpu::MailboxHolder& mailbox) override {}
   void UpdateWebVRTextureBounds(int16_t frame_index,
-                                const gvr::Rectf& left_bounds,
-                                const gvr::Rectf& right_bounds,
-                                const gvr::Sizei& source_size) override {}
+                                const gfx::RectF& left_bounds,
+                                const gfx::RectF& right_bounds,
+                                const gfx::Size& source_size) override {}
   void OnVRVsyncProviderRequest(
       device::mojom::VRVSyncProviderRequest request) override;
   void UpdateVSyncInterval(int64_t timebase_nanos,
diff --git a/chrome/browser/android/vr_shell/ui_elements.cc b/chrome/browser/android/vr_shell/ui_elements.cc
index 82d1cdb0..c223271 100644
--- a/chrome/browser/android/vr_shell/ui_elements.cc
+++ b/chrome/browser/android/vr_shell/ui_elements.cc
@@ -10,25 +10,23 @@
 #include "base/time/time.h"
 #include "chrome/browser/android/vr_shell/animation.h"
 #include "chrome/browser/android/vr_shell/easing.h"
+#include "device/vr/vr_math.h"
 
 namespace vr_shell {
 
 namespace {
 
-bool GetRayPlaneDistance(const gvr::Vec3f& ray_origin,
-                         const gvr::Vec3f& ray_vector,
-                         const gvr::Vec3f& plane_origin,
-                         const gvr::Vec3f& plane_normal,
+bool GetRayPlaneDistance(const gfx::Point3F& ray_origin,
+                         const gfx::Vector3dF& ray_vector,
+                         const gfx::Point3F& plane_origin,
+                         const gfx::Vector3dF& plane_normal,
                          float* distance) {
-  float denom = vr_shell::VectorDot(ray_vector, plane_normal);
+  float denom = gfx::DotProduct(ray_vector, plane_normal);
   if (denom == 0) {
     return false;
   }
-  gvr::Vec3f rel;
-  rel.x = ray_origin.x - plane_origin.x;
-  rel.y = ray_origin.y - plane_origin.y;
-  rel.z = ray_origin.z - plane_origin.z;
-  *distance = -vr_shell::VectorDot(plane_normal, rel) / denom;
+  gfx::Vector3dF rel = ray_origin - plane_origin;
+  *distance = -gfx::DotProduct(plane_normal, rel) / denom;
   return true;
 }
 
@@ -39,59 +37,65 @@
 }
 
 void Transform::MakeIdentity() {
-  SetIdentityM(to_world);
+  vr::SetIdentityM(&to_world);
 }
 
-void Transform::Rotate(gvr::Quatf quat) {
+void Transform::Rotate(const vr::Quatf& quat) {
   // TODO(klausw): use specialized rotation code? Constructing the matrix
   // via axis-angle quaternion is inefficient.
-  gvr::Mat4f forward = QuatToMatrix(quat);
-  to_world = MatrixMul(forward, to_world);
+  vr::Mat4f forward;
+  vr::QuatToMatrix(quat, &forward);
+  vr::MatrixMul(forward, to_world, &to_world);
 }
 
-void Transform::Rotate(float ax, float ay, float az, float rad) {
-  Rotate(QuatFromAxisAngle({ax, ay, az}, rad));
+void Transform::Rotate(const vr::RotationAxisAngle& axis_angle) {
+  Rotate(vr::QuatFromAxisAngle(axis_angle));
 }
 
-void Transform::Translate(float tx, float ty, float tz) {
-  TranslateM(to_world, to_world, tx, ty, tz);
+void Transform::Translate(const gfx::Vector3dF& translation) {
+  vr::TranslateM(to_world, translation, &to_world);
 }
 
-void Transform::Scale(float sx, float sy, float sz) {
-  ScaleM(to_world, to_world, sx, sy, sz);
+void Transform::Scale(const gfx::Vector3dF& scale) {
+  vr::ScaleM(to_world, scale, &to_world);
 }
 
-const gvr::Mat4f& WorldRectangle::TransformMatrix() const {
+const vr::Mat4f& WorldRectangle::TransformMatrix() const {
   return transform_.to_world;
 }
 
-gvr::Vec3f WorldRectangle::GetCenter() const {
-  const gvr::Vec3f kOrigin = {0.0f, 0.0f, 0.0f};
-  return MatrixVectorMul(transform_.to_world, kOrigin);
+gfx::Point3F WorldRectangle::GetCenter() const {
+  const gfx::Point3F kOrigin(0.0f, 0.0f, 0.0f);
+  return kOrigin + vr::GetTranslation(transform_.to_world);
 }
 
-gvr::Vec2f WorldRectangle::GetUnitRectangleCoordinates(
-    const gvr::Vec3f& world_point) {
-  const gvr::Mat4f& transform = transform_.to_world;
-  gvr::Vec3f origin = MatrixVectorMul(transform, gvr::Vec3f({0, 0, 0}));
-  gvr::Vec3f xAxis = MatrixVectorMul(transform, gvr::Vec3f({1, 0, 0}));
-  gvr::Vec3f yAxis = MatrixVectorMul(transform, gvr::Vec3f({0, 1, 0}));
-  xAxis = VectorSubtract(xAxis, origin);
-  yAxis = VectorSubtract(yAxis, origin);
-  gvr::Vec3f point = VectorSubtract(world_point, origin);
+gfx::PointF WorldRectangle::GetUnitRectangleCoordinates(
+    const gfx::Point3F& world_point) {
+  // TODO(acondor): Simplify the math in this function.
+  const vr::Mat4f& transform = transform_.to_world;
+  gfx::Vector3dF origin =
+      vr::MatrixVectorMul(transform, gfx::Vector3dF(0, 0, 0));
+  gfx::Vector3dF x_axis =
+      vr::MatrixVectorMul(transform, gfx::Vector3dF(1, 0, 0));
+  gfx::Vector3dF y_axis =
+      vr::MatrixVectorMul(transform, gfx::Vector3dF(0, 1, 0));
+  x_axis.Subtract(origin);
+  y_axis.Subtract(origin);
+  gfx::Point3F point = world_point - origin;
+  gfx::Vector3dF v_point(point.x(), point.y(), point.z());
 
-  float x = VectorDot(point, xAxis) / VectorDot(xAxis, xAxis);
-  float y = VectorDot(point, yAxis) / VectorDot(yAxis, yAxis);
-  return {x, y};
+  float x = gfx::DotProduct(v_point, x_axis) / gfx::DotProduct(x_axis, x_axis);
+  float y = gfx::DotProduct(v_point, y_axis) / gfx::DotProduct(y_axis, y_axis);
+  return gfx::PointF(x, y);
 }
 
-gvr::Vec3f WorldRectangle::GetNormal() const {
-  const gvr::Vec3f kNormalOrig = {0.0f, 0.0f, -1.0f};
-  return MatrixVectorRotate(transform_.to_world, kNormalOrig);
+gfx::Vector3dF WorldRectangle::GetNormal() const {
+  const gfx::Vector3dF kNormalOrig = {0.0f, 0.0f, -1.0f};
+  return vr::MatrixVectorRotate(transform_.to_world, kNormalOrig);
 }
 
-bool WorldRectangle::GetRayDistance(const gvr::Vec3f& ray_origin,
-                                    const gvr::Vec3f& ray_vector,
+bool WorldRectangle::GetRayDistance(const gfx::Point3F& ray_origin,
+                                    const gfx::Vector3dF& ray_vector,
                                     float* distance) const {
   return GetRayPlaneDistance(ray_origin, ray_vector, GetCenter(), GetNormal(),
                              distance);
@@ -111,19 +115,19 @@
     if (animation.from.size() == 0) {
       switch (animation.property) {
         case Animation::COPYRECT:
-          animation.from.push_back(copy_rect.x);
-          animation.from.push_back(copy_rect.y);
-          animation.from.push_back(copy_rect.width);
-          animation.from.push_back(copy_rect.height);
+          animation.from.push_back(copy_rect.x());
+          animation.from.push_back(copy_rect.y());
+          animation.from.push_back(copy_rect.width());
+          animation.from.push_back(copy_rect.height());
           break;
         case Animation::SIZE:
-          animation.from.push_back(size.x);
-          animation.from.push_back(size.y);
+          animation.from.push_back(size.x());
+          animation.from.push_back(size.y());
           break;
         case Animation::SCALE:
-          animation.from.push_back(scale.x);
-          animation.from.push_back(scale.y);
-          animation.from.push_back(scale.z);
+          animation.from.push_back(scale.x());
+          animation.from.push_back(scale.y());
+          animation.from.push_back(scale.z());
           break;
         case Animation::ROTATION:
           animation.from.push_back(rotation.x);
@@ -132,9 +136,9 @@
           animation.from.push_back(rotation.angle);
           break;
         case Animation::TRANSLATION:
-          animation.from.push_back(translation.x);
-          animation.from.push_back(translation.y);
-          animation.from.push_back(translation.z);
+          animation.from.push_back(translation.x());
+          animation.from.push_back(translation.y());
+          animation.from.push_back(translation.z());
           break;
         case Animation::OPACITY:
           animation.from.push_back(opacity);
@@ -159,21 +163,16 @@
     switch (animation.property) {
       case Animation::COPYRECT:
         CHECK_EQ(animation.from.size(), 4u);
-        copy_rect.x = values[0];
-        copy_rect.y = values[1];
-        copy_rect.width = values[2];
-        copy_rect.height = values[3];
+        copy_rect.SetRect(values[0], values[1], values[2], values[3]);
         break;
       case Animation::SIZE:
         CHECK_EQ(animation.from.size(), 2u);
-        size.x = values[0];
-        size.y = values[1];
+        size.set_x(values[0]);
+        size.set_y(values[1]);
         break;
       case Animation::SCALE:
         CHECK_EQ(animation.from.size(), 3u);
-        scale.x = values[0];
-        scale.y = values[1];
-        scale.z = values[2];
+        scale = {values[0], values[1], values[2]};
         break;
       case Animation::ROTATION:
         CHECK_EQ(animation.from.size(), 4u);
@@ -184,9 +183,7 @@
         break;
       case Animation::TRANSLATION:
         CHECK_EQ(animation.from.size(), 3u);
-        translation.x = values[0];
-        translation.y = values[1];
-        translation.z = values[2];
+        translation = {values[0], values[1], values[2]};
         break;
       case Animation::OPACITY:
         CHECK_EQ(animation.from.size(), 1u);
diff --git a/chrome/browser/android/vr_shell/ui_elements.h b/chrome/browser/android/vr_shell/ui_elements.h
index b502f5d..a5990688 100644
--- a/chrome/browser/android/vr_shell/ui_elements.h
+++ b/chrome/browser/android/vr_shell/ui_elements.h
@@ -10,8 +10,7 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "chrome/browser/android/vr_shell/vr_math.h"
-#include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
+#include "device/vr/vr_types.h"
 
 namespace base {
 class TimeTicks;
@@ -53,27 +52,27 @@
   Transform();
 
   void MakeIdentity();
-  void Rotate(gvr::Quatf quat);
-  void Rotate(float ax, float ay, float az, float rad);
-  void Translate(float tx, float ty, float tz);
-  void Scale(float sx, float sy, float sz);
+  void Rotate(const vr::Quatf& quat);
+  void Rotate(const vr::RotationAxisAngle& axis_angle);
+  void Translate(const gfx::Vector3dF& translation);
+  void Scale(const gfx::Vector3dF& scale);
 
-  gvr::Mat4f to_world;
+  vr::Mat4f to_world;
 };
 
 class WorldRectangle {
  public:
-  const gvr::Mat4f& TransformMatrix() const;
+  const vr::Mat4f& TransformMatrix() const;
   Transform* mutable_transform() { return &transform_; }
 
-  gvr::Vec3f GetCenter() const;
-  gvr::Vec3f GetNormal() const;
+  gfx::Point3F GetCenter() const;
+  gfx::Vector3dF GetNormal() const;
 
   // Computes the distance from |ray_origin| to this rectangles's plane, along
   // |ray_vector|. Returns true and populates |distance| if the calculation is
   // possible, and false if the ray is parallel to the plane.
-  bool GetRayDistance(const gvr::Vec3f& ray_origin,
-                      const gvr::Vec3f& ray_vector,
+  bool GetRayDistance(const gfx::Point3F& ray_origin,
+                      const gfx::Vector3dF& ray_vector,
                       float* distance) const;
 
   // Projects a 3D world point onto the X and Y axes of the transformed
@@ -81,7 +80,7 @@
   // rectangle. This allows beam intersection points to be mapped to sprite
   // pixel coordinates. Points that fall onto the rectangle will generate X and
   // Y values on the interval [-0.5, 0.5].
-  gvr::Vec2f GetUnitRectangleCoordinates(const gvr::Vec3f& world_point);
+  gfx::PointF GetUnitRectangleCoordinates(const gfx::Point3F& world_point);
 
  private:
   Transform transform_;
@@ -120,20 +119,20 @@
   bool lock_to_fov = false;
 
   // Specifies the region (in pixels) of a texture to render.
-  Recti copy_rect = {0, 0, 0, 0};
+  gfx::Rect copy_rect = {0, 0, 0, 0};
 
   // The size of the object.  This does not affect children.
-  gvr::Vec3f size = {1.0f, 1.0f, 1.0f};
+  gfx::Vector3dF size = {1.0f, 1.0f, 1.0f};
 
   // The scale of the object, and its children.
-  gvr::Vec3f scale = {1.0f, 1.0f, 1.0f};
+  gfx::Vector3dF scale = {1.0f, 1.0f, 1.0f};
 
   // The rotation of the object, and its children.
-  RotationAxisAngle rotation = {1.0f, 0.0f, 0.0f, 0.0f};
+  vr::RotationAxisAngle rotation = {1.0f, 0.0f, 0.0f, 0.0f};
 
   // The translation of the object, and its children.  Translation is applied
   // after rotation and scaling.
-  gvr::Vec3f translation = {0.0f, 0.0f, 0.0f};
+  gfx::Vector3dF translation = {0.0f, 0.0f, 0.0f};
 
   // The opacity of the object (between 0.0 and 1.0).
   float opacity = 1.0f;
@@ -155,8 +154,8 @@
 
   Fill fill = Fill::NONE;
 
-  Colorf edge_color = {1.0f, 1.0f, 1.0f, 1.0f};
-  Colorf center_color = {1.0f, 1.0f, 1.0f, 1.0f};
+  vr::Colorf edge_color = {1.0f, 1.0f, 1.0f, 1.0f};
+  vr::Colorf center_color = {1.0f, 1.0f, 1.0f, 1.0f};
 
   int gridline_count = 1;
 
diff --git a/chrome/browser/android/vr_shell/ui_elements_unittest.cc b/chrome/browser/android/vr_shell/ui_elements_unittest.cc
index fd509d3..56f200e 100644
--- a/chrome/browser/android/vr_shell/ui_elements_unittest.cc
+++ b/chrome/browser/android/vr_shell/ui_elements_unittest.cc
@@ -9,18 +9,19 @@
 #include "base/macros.h"
 #include "chrome/browser/android/vr_shell/animation.h"
 #include "chrome/browser/android/vr_shell/easing.h"
+#include "device/vr/vr_types.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#define EXPECT_VEC3F_EQ(a, b) \
-  EXPECT_FLOAT_EQ(a.x, b.x);  \
-  EXPECT_FLOAT_EQ(a.y, b.y);  \
-  EXPECT_FLOAT_EQ(a.z, b.z);
+#define EXPECT_VEC3F_EQ(a, b)    \
+  EXPECT_FLOAT_EQ(a.x(), b.x()); \
+  EXPECT_FLOAT_EQ(a.y(), b.y()); \
+  EXPECT_FLOAT_EQ(a.z(), b.z());
 
-#define EXPECT_RECTF_EQ(a, b)        \
-  EXPECT_FLOAT_EQ(a.x, b.x);         \
-  EXPECT_FLOAT_EQ(a.y, b.y);         \
-  EXPECT_FLOAT_EQ(a.width, b.width); \
-  EXPECT_FLOAT_EQ(a.height, b.height);
+#define EXPECT_RECTF_EQ(a, b)            \
+  EXPECT_FLOAT_EQ(a.x(), b.x());         \
+  EXPECT_FLOAT_EQ(a.y(), b.y());         \
+  EXPECT_FLOAT_EQ(a.width(), b.width()); \
+  EXPECT_FLOAT_EQ(a.height(), b.height());
 
 #define EXPECT_ROTATION(a, b) \
   EXPECT_FLOAT_EQ(a.x, b.x);  \
@@ -51,23 +52,23 @@
       {20, 200, 2000, 20000}, usToTicks(50000), usToDelta(10000)));
   rect.animations.emplace_back(std::move(animation));
   rect.Animate(usToTicks(50000));
-  EXPECT_RECTF_EQ(rect.copy_rect, Rectf({10, 100, 1000, 10000}));
+  EXPECT_RECTF_EQ(rect.copy_rect, gfx::RectF(10, 100, 1000, 10000));
   rect.Animate(usToTicks(60000));
-  EXPECT_RECTF_EQ(rect.copy_rect, Rectf({20, 200, 2000, 20000}));
+  EXPECT_RECTF_EQ(rect.copy_rect, gfx::RectF(20, 200, 2000, 20000));
 }
 
 TEST(UiElements, AnimateSize) {
   ContentRectangle rect;
-  rect.size = {10, 100};
+  rect.size = {10, 100, 1};
   std::unique_ptr<Animation> animation(
       new Animation(0, Animation::Property::SIZE,
                     std::unique_ptr<easing::Easing>(new easing::Linear()), {},
                     {20, 200}, usToTicks(50000), usToDelta(10000)));
   rect.animations.emplace_back(std::move(animation));
   rect.Animate(usToTicks(50000));
-  EXPECT_VEC3F_EQ(rect.size, gvr::Vec3f({10, 100}));
+  EXPECT_VEC3F_EQ(rect.size, gfx::Vector3dF(10, 100, 1));
   rect.Animate(usToTicks(60000));
-  EXPECT_VEC3F_EQ(rect.size, gvr::Vec3f({20, 200}));
+  EXPECT_VEC3F_EQ(rect.size, gfx::Vector3dF(20, 200, 1));
 }
 
 TEST(UiElements, AnimateTranslation) {
@@ -79,9 +80,9 @@
                     {20, 200, 2000}, usToTicks(50000), usToDelta(10000)));
   rect.animations.emplace_back(std::move(animation));
   rect.Animate(usToTicks(50000));
-  EXPECT_VEC3F_EQ(rect.translation, gvr::Vec3f({10, 100, 1000}));
+  EXPECT_VEC3F_EQ(rect.translation, gfx::Vector3dF(10, 100, 1000));
   rect.Animate(usToTicks(60000));
-  EXPECT_VEC3F_EQ(rect.translation, gvr::Vec3f({20, 200, 2000}));
+  EXPECT_VEC3F_EQ(rect.translation, gfx::Vector3dF(20, 200, 2000));
 }
 
 TEST(UiElements, AnimateRotation) {
@@ -93,9 +94,9 @@
       {20, 200, 2000, 20000}, usToTicks(50000), usToDelta(10000)));
   rect.animations.emplace_back(std::move(animation));
   rect.Animate(usToTicks(50000));
-  EXPECT_ROTATION(rect.rotation, RotationAxisAngle({10, 100, 1000, 10000}));
+  EXPECT_ROTATION(rect.rotation, vr::RotationAxisAngle({10, 100, 1000, 10000}));
   rect.Animate(usToTicks(60000));
-  EXPECT_ROTATION(rect.rotation, RotationAxisAngle({20, 200, 2000, 20000}));
+  EXPECT_ROTATION(rect.rotation, vr::RotationAxisAngle({20, 200, 2000, 20000}));
 }
 
 TEST(UiElements, AnimationHasNoEffectBeforeScheduledStart) {
@@ -106,7 +107,7 @@
       {20, 200, 2000}, usToTicks(50000), usToDelta(10000)));
   rect.animations.emplace_back(std::move(animation));
   rect.Animate(usToTicks(49999));
-  EXPECT_VEC3F_EQ(rect.translation, gvr::Vec3f({0, 0, 0}));
+  EXPECT_VEC3F_EQ(rect.translation, gfx::Vector3dF(0, 0, 0));
 }
 
 TEST(UiElements, AnimationPurgedWhenDone) {
@@ -128,11 +129,11 @@
       {20, 200, 2000}, usToTicks(50000), usToDelta(10000)));
   rect.animations.emplace_back(std::move(animation));
   rect.Animate(usToTicks(50000));
-  EXPECT_VEC3F_EQ(rect.translation, gvr::Vec3f({10, 100, 1000}));
+  EXPECT_VEC3F_EQ(rect.translation, gfx::Vector3dF(10, 100, 1000));
   rect.Animate(usToTicks(55000));
-  EXPECT_VEC3F_EQ(rect.translation, gvr::Vec3f({15, 150, 1500}));
+  EXPECT_VEC3F_EQ(rect.translation, gfx::Vector3dF(15, 150, 1500));
   rect.Animate(usToTicks(60000));
-  EXPECT_VEC3F_EQ(rect.translation, gvr::Vec3f({20, 200, 2000}));
+  EXPECT_VEC3F_EQ(rect.translation, gfx::Vector3dF(20, 200, 2000));
 }
 
 TEST(UiElements, AnimationStartFromSpecifiedLocation) {
@@ -143,9 +144,9 @@
       {20, 200, 2000}, usToTicks(50000), usToDelta(10000)));
   rect.animations.emplace_back(std::move(animation));
   rect.Animate(usToTicks(50000));
-  EXPECT_VEC3F_EQ(rect.translation, gvr::Vec3f({10, 100, 1000}));
+  EXPECT_VEC3F_EQ(rect.translation, gfx::Vector3dF(10, 100, 1000));
   rect.Animate(usToTicks(60000));
-  EXPECT_VEC3F_EQ(rect.translation, gvr::Vec3f({20, 200, 2000}));
+  EXPECT_VEC3F_EQ(rect.translation, gfx::Vector3dF(20, 200, 2000));
 }
 
 // Ensure that when a new animation overlaps another of the same type, the
@@ -166,11 +167,11 @@
   rect.animations.emplace_back(std::move(animation));
   rect.animations.emplace_back(std::move(animation2));
   rect.Animate(usToTicks(55000));
-  EXPECT_VEC3F_EQ(rect.translation, gvr::Vec3f({10, 100, 1000}));
+  EXPECT_VEC3F_EQ(rect.translation, gfx::Vector3dF(10, 100, 1000));
   rect.Animate(usToTicks(60000));
-  EXPECT_VEC3F_EQ(rect.translation, gvr::Vec3f({30, 300, 3000}));
+  EXPECT_VEC3F_EQ(rect.translation, gfx::Vector3dF(30, 300, 3000));
   rect.Animate(usToTicks(65000));
-  EXPECT_VEC3F_EQ(rect.translation, gvr::Vec3f({50, 500, 5000}));
+  EXPECT_VEC3F_EQ(rect.translation, gfx::Vector3dF(50, 500, 5000));
 }
 
 }  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/ui_scene.cc b/chrome/browser/android/vr_shell/ui_scene.cc
index e6e1620..e602648 100644
--- a/chrome/browser/android/vr_shell/ui_scene.cc
+++ b/chrome/browser/android/vr_shell/ui_scene.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/android/vr_shell/animation.h"
 #include "chrome/browser/android/vr_shell/easing.h"
 #include "chrome/browser/android/vr_shell/ui_elements.h"
+#include "device/vr/vr_math.h"
 
 namespace vr_shell {
 
@@ -46,7 +47,7 @@
 
 bool ParseColorf(const base::DictionaryValue& dict,
                  const std::string& key,
-                 Colorf* output) {
+                 vr::Colorf* output) {
   const base::DictionaryValue* item_dict;
   if (dict.GetDictionary(key, &item_dict)) {
     double value;
@@ -149,10 +150,10 @@
   float x_offset;
   switch (x_anchoring) {
     case XLEFT:
-      x_offset = -0.5f * parent.size.x;
+      x_offset = -0.5f * parent.size.x();
       break;
     case XRIGHT:
-      x_offset = 0.5f * parent.size.x;
+      x_offset = 0.5f * parent.size.x();
       break;
     case XNONE:
       x_offset = 0.0f;
@@ -161,16 +162,16 @@
   float y_offset;
   switch (y_anchoring) {
     case YTOP:
-      y_offset = 0.5f * parent.size.y;
+      y_offset = 0.5f * parent.size.y();
       break;
     case YBOTTOM:
-      y_offset = -0.5f * parent.size.y;
+      y_offset = -0.5f * parent.size.y();
       break;
     case YNONE:
       y_offset = 0.0f;
       break;
   }
-  transform->Translate(x_offset, y_offset, 0);
+  transform->Translate(gfx::Vector3dF(x_offset, y_offset, 0));
 }
 
 }  // namespace
@@ -373,7 +374,7 @@
   return !GetHeadLockedElements().empty();
 }
 
-const Colorf& UiScene::GetBackgroundColor() const {
+const vr::Colorf& UiScene::GetBackgroundColor() const {
   return background_color_;
 }
 
@@ -406,7 +407,7 @@
 
   Transform* transform = element->mutable_transform();
   transform->MakeIdentity();
-  transform->Scale(element->size.x, element->size.y, element->size.z);
+  transform->Scale(element->size);
   element->computed_opacity = element->opacity;
   element->computed_lock_to_fov = element->lock_to_fov;
 
@@ -414,23 +415,22 @@
   // and it's children, if applicable.
   Transform* inheritable = &element->inheritable_transform;
   inheritable->MakeIdentity();
-  inheritable->Scale(element->scale.x, element->scale.y, element->scale.z);
-  inheritable->Rotate(element->rotation.x, element->rotation.y,
-                      element->rotation.z, element->rotation.angle);
-  inheritable->Translate(element->translation.x, element->translation.y,
-                         element->translation.z);
+  inheritable->Scale(element->scale);
+  inheritable->Rotate(element->rotation);
+  inheritable->Translate(element->translation);
   if (parent) {
     ApplyAnchoring(*parent, element->x_anchoring, element->y_anchoring,
                    inheritable);
     ApplyRecursiveTransforms(parent);
-    inheritable->to_world = MatrixMul(parent->inheritable_transform.to_world,
-                                      inheritable->to_world);
+    vr::MatrixMul(parent->inheritable_transform.to_world, inheritable->to_world,
+                  &inheritable->to_world);
 
     element->computed_opacity *= parent->opacity;
     element->computed_lock_to_fov = parent->lock_to_fov;
   }
 
-  transform->to_world = MatrixMul(inheritable->to_world, transform->to_world);
+  vr::MatrixMul(inheritable->to_world, transform->to_world,
+                &transform->to_world);
   element->dirty = false;
 }
 
@@ -452,19 +452,27 @@
   ParseFloat(dict, "opacity", &element->opacity);
 
   DCHECK(!(element->lock_to_fov && element->parent_id != -1));
-
-  ParseFloat(dict, "sizeX", &element->size.x);
-  ParseFloat(dict, "sizeY", &element->size.y);
-  ParseFloat(dict, "scaleX", &element->scale.x);
-  ParseFloat(dict, "scaleY", &element->scale.y);
-  ParseFloat(dict, "scaleZ", &element->scale.z);
+  float val;
+  ParseFloat(dict, "sizeX", &val);
+  element->size.set_x(val);
+  ParseFloat(dict, "sizeY", &val);
+  element->size.set_y(val);
+  ParseFloat(dict, "scaleX", &val);
+  element->scale.set_x(val);
+  ParseFloat(dict, "scaleY", &val);
+  element->scale.set_y(val);
+  ParseFloat(dict, "scaleZ", &val);
+  element->scale.set_z(val);
+  ParseFloat(dict, "translationX", &val);
+  element->translation.set_x(val);
+  ParseFloat(dict, "translationY", &val);
+  element->translation.set_y(val);
+  ParseFloat(dict, "translationZ", &val);
+  element->translation.set_z(val);
   ParseFloat(dict, "rotationX", &element->rotation.x);
   ParseFloat(dict, "rotationY", &element->rotation.y);
   ParseFloat(dict, "rotationZ", &element->rotation.z);
   ParseFloat(dict, "rotationAngle", &element->rotation.angle);
-  ParseFloat(dict, "translationX", &element->translation.x);
-  ParseFloat(dict, "translationY", &element->translation.y);
-  ParseFloat(dict, "translationZ", &element->translation.z);
 
   if (ParseInt(dict, "xAnchoring", &element->x_anchoring)) {
     CHECK_GE(element->parent_id, 0);
@@ -481,12 +489,17 @@
       content_element_ = nullptr;
     }
 
+    int val;
     switch (element->fill) {
       case Fill::SPRITE:
-        CHECK(ParseInt(dict, "copyRectX", &element->copy_rect.x));
-        CHECK(ParseInt(dict, "copyRectY", &element->copy_rect.y));
-        CHECK(ParseInt(dict, "copyRectWidth", &element->copy_rect.width));
-        CHECK(ParseInt(dict, "copyRectHeight", &element->copy_rect.height));
+        CHECK(ParseInt(dict, "copyRectX", &val));
+        element->copy_rect.set_x(val);
+        CHECK(ParseInt(dict, "copyRectY", &val));
+        element->copy_rect.set_y(val);
+        CHECK(ParseInt(dict, "copyRectWidth", &val));
+        element->copy_rect.set_width(val);
+        CHECK(ParseInt(dict, "copyRectHeight", &val));
+        element->copy_rect.set_height(val);
         break;
       case Fill::OPAQUE_GRADIENT:
         CHECK(ParseColorf(dict, "edgeColor", &element->edge_color));
diff --git a/chrome/browser/android/vr_shell/ui_scene.h b/chrome/browser/android/vr_shell/ui_scene.h
index 9d33480..91b0c3b 100644
--- a/chrome/browser/android/vr_shell/ui_scene.h
+++ b/chrome/browser/android/vr_shell/ui_scene.h
@@ -9,7 +9,7 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "chrome/browser/android/vr_shell/vr_math.h"
+#include "device/vr/vr_types.h"
 
 namespace base {
 class DictionaryValue;
@@ -73,7 +73,7 @@
   std::vector<const ContentRectangle*> GetHeadLockedElements() const;
   bool HasVisibleHeadLockedElements() const;
 
-  const Colorf& GetBackgroundColor() const;
+  const vr::Colorf& GetBackgroundColor() const;
   float GetBackgroundDistance() const;
   bool GetWebVrRenderingEnabled() const;
 
@@ -84,7 +84,7 @@
 
   std::vector<std::unique_ptr<ContentRectangle>> ui_elements_;
   ContentRectangle* content_element_ = nullptr;
-  Colorf background_color_ = {0.1f, 0.1f, 0.1f, 1.0f};
+  vr::Colorf background_color_ = {0.1f, 0.1f, 0.1f, 1.0f};
   float background_distance_ = 10.0f;
   bool webvr_rendering_enabled_ = true;
 
diff --git a/chrome/browser/android/vr_shell/ui_scene_unittest.cc b/chrome/browser/android/vr_shell/ui_scene_unittest.cc
index 36a1e52..427626c 100644
--- a/chrome/browser/android/vr_shell/ui_scene_unittest.cc
+++ b/chrome/browser/android/vr_shell/ui_scene_unittest.cc
@@ -14,15 +14,16 @@
 #include "chrome/browser/android/vr_shell/animation.h"
 #include "chrome/browser/android/vr_shell/easing.h"
 #include "chrome/browser/android/vr_shell/ui_elements.h"
-#include "chrome/browser/android/vr_shell/vr_math.h"
+#include "device/vr/vr_math.h"
+#include "device/vr/vr_types.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #define TOLERANCE 0.0001
 
-#define EXPECT_VEC3F_NEAR(a, b)     \
-  EXPECT_NEAR(a.x, b.x, TOLERANCE); \
-  EXPECT_NEAR(a.y, b.y, TOLERANCE); \
-  EXPECT_NEAR(a.z, b.z, TOLERANCE);
+#define EXPECT_VEC3F_NEAR(a, b)         \
+  EXPECT_NEAR(a.x(), b.x(), TOLERANCE); \
+  EXPECT_NEAR(a.y(), b.y(), TOLERANCE); \
+  EXPECT_NEAR(a.z(), b.z(), TOLERANCE);
 
 namespace vr_shell {
 
@@ -132,14 +133,14 @@
   scene.AddUiElement(std::move(element));
   const ContentRectangle* child = scene.GetUiElementById(1);
 
-  const gvr::Vec3f origin({0, 0, 0});
-  const gvr::Vec3f point({1, 0, 0});
+  const gfx::Vector3dF origin(0, 0, 0);
+  const gfx::Vector3dF point(1, 0, 0);
 
   scene.UpdateTransforms(usToTicks(0));
-  auto new_origin = MatrixVectorMul(child->TransformMatrix(), origin);
-  auto new_point = MatrixVectorMul(child->TransformMatrix(), point);
-  EXPECT_VEC3F_NEAR(gvr::Vec3f({6, 10, 0}), new_origin);
-  EXPECT_VEC3F_NEAR(gvr::Vec3f({0, 10, 0}), new_point);
+  auto new_origin = vr::MatrixVectorMul(child->TransformMatrix(), origin);
+  auto new_point = vr::MatrixVectorMul(child->TransformMatrix(), point);
+  EXPECT_VEC3F_NEAR(gfx::Vector3dF(6, 10, 0), new_origin);
+  EXPECT_VEC3F_NEAR(gfx::Vector3dF(0, 10, 0), new_point);
 }
 
 TEST(UiScene, Opacity) {
@@ -209,8 +210,8 @@
 
   scene.UpdateTransforms(usToTicks(0));
   const ContentRectangle* child = scene.GetUiElementById(1);
-  EXPECT_NEAR(child->GetCenter().x, GetParam().expected_x, TOLERANCE);
-  EXPECT_NEAR(child->GetCenter().y, GetParam().expected_y, TOLERANCE);
+  EXPECT_NEAR(child->GetCenter().x(), GetParam().expected_x, TOLERANCE);
+  EXPECT_NEAR(child->GetCenter().y(), GetParam().expected_y, TOLERANCE);
   scene.RemoveUiElement(1);
 }
 
@@ -278,27 +279,27 @@
   EXPECT_EQ(element->y_anchoring, YAnchoring::YTOP);
   EXPECT_FLOAT_EQ(element->opacity, 0.357);
 
-  EXPECT_EQ(element->copy_rect.x, 100);
-  EXPECT_EQ(element->copy_rect.y, 101);
-  EXPECT_EQ(element->copy_rect.width, 102);
-  EXPECT_EQ(element->copy_rect.height, 103);
+  EXPECT_EQ(element->copy_rect.x(), 100);
+  EXPECT_EQ(element->copy_rect.y(), 101);
+  EXPECT_EQ(element->copy_rect.width(), 102);
+  EXPECT_EQ(element->copy_rect.height(), 103);
 
-  EXPECT_FLOAT_EQ(element->size.x, 200);
-  EXPECT_FLOAT_EQ(element->size.y, 201);
-  EXPECT_FLOAT_EQ(element->size.z, 1);
+  EXPECT_FLOAT_EQ(element->size.x(), 200);
+  EXPECT_FLOAT_EQ(element->size.y(), 201);
+  EXPECT_FLOAT_EQ(element->size.z(), 1);
 
-  EXPECT_FLOAT_EQ(element->scale.x, 300);
-  EXPECT_FLOAT_EQ(element->scale.y, 301);
-  EXPECT_FLOAT_EQ(element->scale.z, 302);
+  EXPECT_FLOAT_EQ(element->scale.x(), 300);
+  EXPECT_FLOAT_EQ(element->scale.y(), 301);
+  EXPECT_FLOAT_EQ(element->scale.z(), 302);
 
   EXPECT_FLOAT_EQ(element->rotation.x, 400);
   EXPECT_FLOAT_EQ(element->rotation.y, 401);
   EXPECT_FLOAT_EQ(element->rotation.z, 402);
   EXPECT_FLOAT_EQ(element->rotation.angle, 403);
 
-  EXPECT_FLOAT_EQ(element->translation.x, 500);
-  EXPECT_FLOAT_EQ(element->translation.y, 501);
-  EXPECT_FLOAT_EQ(element->translation.z, 502);
+  EXPECT_FLOAT_EQ(element->translation.x(), 500);
+  EXPECT_FLOAT_EQ(element->translation.y(), 501);
+  EXPECT_FLOAT_EQ(element->translation.z(), 502);
 
   dict.Clear();
   dict.SetInteger("id", 12);
@@ -340,10 +341,10 @@
   const auto* element = scene.GetUiElementById(9);
 
   EXPECT_EQ(element->fill, Fill::SPRITE);
-  EXPECT_EQ(element->copy_rect.x, 1);
-  EXPECT_EQ(element->copy_rect.y, 2);
-  EXPECT_EQ(element->copy_rect.width, 3);
-  EXPECT_EQ(element->copy_rect.height, 4);
+  EXPECT_EQ(element->copy_rect.x(), 1);
+  EXPECT_EQ(element->copy_rect.y(), 2);
+  EXPECT_EQ(element->copy_rect.width(), 3);
+  EXPECT_EQ(element->copy_rect.height(), 4);
 
   // Test OPAQUE_GRADIENT filling.
   dict.Clear();
diff --git a/chrome/browser/android/vr_shell/vr_controller.cc b/chrome/browser/android/vr_shell/vr_controller.cc
index 25228b9..177b3b6 100644
--- a/chrome/browser/android/vr_shell/vr_controller.cc
+++ b/chrome/browser/android/vr_shell/vr_controller.cc
@@ -10,7 +10,7 @@
 
 #include "base/logging.h"
 #include "base/time/time.h"
-#include "chrome/browser/android/vr_shell/vr_math.h"
+#include "device/vr/vr_math.h"
 #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr.h"
 #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_controller.h"
 
@@ -41,46 +41,12 @@
 
 constexpr int kMaxNumOfExtrapolations = 2;
 
-static constexpr gvr::Vec3f kControllerPosition = {0.2f, -0.5f, -0.15f};
+static constexpr gfx::Point3F kControllerPosition = {0.2f, -0.5f, -0.15f};
 
-class Vector {
- public:
-  static inline void ClampTouchpadPosition(gvr::Vec2f* position) {
-    position->x = std::min(std::max(0.0f, position->x), 1.0f);
-    position->y = std::min(std::max(0.0f, position->y), 1.0f);
-  }
-
-  static inline void SetZero(gvr::Vec2f* v) {
-    v->x = 0;
-    v->y = 0;
-  }
-
-  static inline gvr::Vec2f Subtract(gvr::Vec2f v1, gvr::Vec2f v2) {
-    gvr::Vec2f result;
-    result.x = v1.x - v2.x;
-    result.y = v1.y - v2.y;
-    return result;
-  }
-
-  static inline gvr::Vec2f Add(gvr::Vec2f v1, gvr::Vec2f v2) {
-    gvr::Vec2f result;
-    result.x = v1.x + v2.x;
-    result.y = v1.y + v2.y;
-    return result;
-  }
-
-  static inline bool Equal(const gvr::Vec2f v1, const gvr::Vec2f v2) {
-    return (std::abs(v1.x - v2.x) < kDelta) && (std::abs(v1.y - v2.y) < kDelta);
-  }
-
-  static inline gvr::Vec2f ScalarMult(gvr::Vec2f v, float scalar) {
-    gvr::Vec2f vect_prod;
-    vect_prod.x = v.x * scalar;
-    vect_prod.y = v.y * scalar;
-    return vect_prod;
-  }
-
-};  // Vector
+void ClampTouchpadPosition(gfx::Vector2dF* position) {
+  position->set_x(std::min(std::max(0.0f, position->x()), 1.0f));
+  position->set_y(std::min(std::max(0.0f, position->y()), 1.0f));
+}
 
 }  // namespace
 
@@ -108,13 +74,19 @@
   device::GvrGamepadData pad;
 
   pad.timestamp = controller_state_->GetLastOrientationTimestamp();
-  pad.touch_pos = controller_state_->GetTouchPos();
-  pad.orientation = controller_state_->GetOrientation();
+  pad.touch_pos.set_x(TouchPosX());
+  pad.touch_pos.set_y(TouchPosY());
+  pad.orientation = Orientation();
 
   // Use orientation to rotate acceleration/gyro into seated space.
-  gvr::Mat4f pose_mat = QuatToMatrix(pad.orientation);
-  pad.accel = MatrixVectorMul(pose_mat, controller_state_->GetAccel());
-  pad.gyro = MatrixVectorMul(pose_mat, controller_state_->GetGyro());
+  vr::Mat4f pose_mat;
+  vr::QuatToMatrix(pad.orientation, &pose_mat);
+  const gvr::Vec3f& accel = controller_state_->GetAccel();
+  const gvr::Vec3f& gyro = controller_state_->GetGyro();
+  pad.accel =
+      vr::MatrixVectorMul(pose_mat, gfx::Vector3dF(accel.x, accel.y, accel.z));
+  pad.gyro =
+      vr::MatrixVectorMul(pose_mat, gfx::Vector3dF(gyro.x, gyro.y, gyro.z));
 
   pad.is_touching = controller_state_->IsTouching();
   pad.controller_button_pressed =
@@ -136,22 +108,24 @@
   return controller_state_->GetTouchPos().y;
 }
 
-gvr::Quatf VrController::Orientation() const {
-  return controller_state_->GetOrientation();
+vr::Quatf VrController::Orientation() const {
+  const gvr::Quatf& orientation = controller_state_->GetOrientation();
+  return *reinterpret_cast<vr::Quatf*>(const_cast<gvr::Quatf*>(&orientation));
 }
 
-gvr::Mat4f VrController::GetTransform() const {
+void VrController::GetTransform(vr::Mat4f* out) const {
   // TODO(acondor): Position and orientation needs to be obtained
   // from an elbow model.
   // Placing the controller in a fixed position for now.
-  gvr::Mat4f mat;
-  SetIdentityM(mat);
+  vr::SetIdentityM(out);
   // Changing rotation point.
-  TranslateM(mat, mat, 0, 0, 0.05);
-  mat = MatrixMul(QuatToMatrix(Orientation()), mat);
-  TranslateM(mat, mat, kControllerPosition.x, kControllerPosition.y,
-             kControllerPosition.z - 0.05);
-  return mat;
+  vr::TranslateM(*out, gfx::Vector3dF(0, 0, 0.05), out);
+  vr::Mat4f quat_to_matrix;
+  vr::QuatToMatrix(Orientation(), &quat_to_matrix);
+  vr::MatrixMul(quat_to_matrix, *out, out);
+  gfx::Vector3dF translation(kControllerPosition.x(), kControllerPosition.y(),
+                             kControllerPosition.z() - 0.05);
+  vr::TranslateM(*out, translation, out);
 }
 
 VrControllerModel::State VrController::GetModelState() const {
@@ -206,8 +180,7 @@
   CHECK(touch_info_ != nullptr) << "touch_info_ not initialized properly.";
   if (IsTouching() && state_ == SCROLLING &&
       (controller_state_->GetLastTouchTimestamp() == last_touch_timestamp_ ||
-       (Vector::Equal(cur_touch_point_->position,
-                      prev_touch_point_->position) &&
+       (cur_touch_point_->position == prev_touch_point_->position &&
         extrapolated_touch_ < kMaxNumOfExtrapolations))) {
     extrapolated_touch_++;
     touch_position_changed_ = true;
@@ -216,13 +189,13 @@
         (gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos -
          last_timestamp_nanos_) /
         kNanoSecondsPerSecond;
-    touch_info_->touch_point.position.x =
-        cur_touch_point_->position.x + overall_velocity_.x * duration;
-    touch_info_->touch_point.position.y =
-        cur_touch_point_->position.y + overall_velocity_.y * duration;
+    touch_info_->touch_point.position.set_x(cur_touch_point_->position.x() +
+                                            overall_velocity_.x() * duration);
+    touch_info_->touch_point.position.set_y(cur_touch_point_->position.y() +
+                                            overall_velocity_.y() * duration);
   } else {
     if (extrapolated_touch_ == kMaxNumOfExtrapolations) {
-      Vector::SetZero(&overall_velocity_);
+      overall_velocity_ = {0, 0};
     }
     extrapolated_touch_ = 0;
   }
@@ -279,17 +252,17 @@
 
   if (gesture_list.back()->GetType() == WebInputEvent::kGestureScrollEnd) {
     if (!ButtonDownHappened(gvr::kControllerButtonClick) &&
-        (last_velocity_.x != 0.0 || last_velocity_.y != 0.0)) {
+        (last_velocity_.x() != 0.0 || last_velocity_.y() != 0.0)) {
       std::unique_ptr<WebGestureEvent> fling(new WebGestureEvent(
           WebInputEvent::kGestureFlingStart, WebInputEvent::kNoModifiers,
           gesture_list.back()->TimeStampSeconds()));
       fling->source_device = blink::kWebGestureDeviceTouchpad;
       if (IsHorizontalGesture()) {
         fling->data.fling_start.velocity_x =
-            last_velocity_.x * kDisplacementScaleFactor;
+            last_velocity_.x() * kDisplacementScaleFactor;
       } else {
         fling->data.fling_start.velocity_y =
-            last_velocity_.y * kDisplacementScaleFactor;
+            last_velocity_.y() * kDisplacementScaleFactor;
       }
       gesture_list.push_back(std::move(fling));
     }
@@ -352,9 +325,9 @@
     gesture->SetType(WebInputEvent::kGestureScrollBegin);
     UpdateGestureParameters();
     gesture->data.scroll_begin.delta_x_hint =
-        displacement_.x * kDisplacementScaleFactor;
+        displacement_.x() * kDisplacementScaleFactor;
     gesture->data.scroll_begin.delta_y_hint =
-        displacement_.y * kDisplacementScaleFactor;
+        displacement_.y() * kDisplacementScaleFactor;
     gesture->data.scroll_begin.delta_hint_units =
         blink::WebGestureEvent::ScrollUnits::kPrecisePixels;
   }
@@ -372,23 +345,23 @@
     UpdateGestureParameters();
     if (IsHorizontalGesture()) {
       gesture->data.scroll_update.delta_x =
-          displacement_.x * kDisplacementScaleFactor;
+          displacement_.x() * kDisplacementScaleFactor;
     } else {
       gesture->data.scroll_update.delta_y =
-          displacement_.y * kDisplacementScaleFactor;
+          displacement_.y() * kDisplacementScaleFactor;
     }
     last_velocity_ = overall_velocity_;
   }
 }
 
 bool VrController::IsHorizontalGesture() {
-  return std::abs(last_velocity_.x) > std::abs(last_velocity_.y);
+  return std::abs(last_velocity_.x()) > std::abs(last_velocity_.y());
 }
 
-bool VrController::InSlop(const gvr::Vec2f touch_position) {
-  return (std::abs(touch_position.x - init_touch_point_->position.x) <
+bool VrController::InSlop(const gfx::Vector2dF touch_position) {
+  return (std::abs(touch_position.x() - init_touch_point_->position.x()) <
           kSlopHorizontal) &&
-         (std::abs(touch_position.y - init_touch_point_->position.y) <
+         (std::abs(touch_position.y() - init_touch_point_->position.y()) <
           kSlopVertical);
 }
 
@@ -401,29 +374,28 @@
   cur_touch_point_.reset(new TouchPoint);
   init_touch_point_.reset(new TouchPoint);
   touch_info_.reset(new TouchInfo);
-  Vector::SetZero(&overall_velocity_);
-  Vector::SetZero(&last_velocity_);
+  overall_velocity_ = {0, 0};
+  last_velocity_ = {0, 0};
 }
 
 void VrController::UpdateGestureParameters() {
-  displacement_ = Vector::Subtract(touch_info_->touch_point.position,
-                                   prev_touch_point_->position);
+  displacement_ =
+      touch_info_->touch_point.position - prev_touch_point_->position;
 }
 
 bool VrController::UpdateCurrentTouchpoint() {
   touch_info_->touch_up = TouchUpHappened();
   touch_info_->touch_down = TouchDownHappened();
   touch_info_->is_touching = IsTouching();
-  touch_info_->touch_point.position.x = TouchPosX();
-  touch_info_->touch_point.position.y = TouchPosY();
-  Vector::ClampTouchpadPosition(&touch_info_->touch_point.position);
+  touch_info_->touch_point.position.set_x(TouchPosX());
+  touch_info_->touch_point.position.set_y(TouchPosY());
+  ClampTouchpadPosition(&touch_info_->touch_point.position);
   touch_info_->touch_point.timestamp =
       gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos;
 
   if (IsTouching() || TouchUpHappened()) {
     // Update the touch point when the touch position has changed.
-    if (!Vector::Equal(cur_touch_point_->position,
-                       touch_info_->touch_point.position)) {
+    if (cur_touch_point_->position != touch_info_->touch_point.position) {
       prev_touch_point_.swap(cur_touch_point_);
       cur_touch_point_.reset(new TouchPoint);
       cur_touch_point_->position = touch_info_->touch_point.position;
@@ -443,16 +415,15 @@
   if (duration < kDelta)
     return;
 
-  gvr::Vec2f displacement = Vector::Subtract(touch_info_->touch_point.position,
-                                             prev_touch_point_->position);
+  const gfx::Vector2dF& displacement =
+      touch_info_->touch_point.position - prev_touch_point_->position;
 
-  gvr::Vec2f velocity = Vector::ScalarMult(displacement, 1 / duration);
+  const gfx::Vector2dF& velocity = ScaleVector2d(displacement, (1 / duration));
 
   float weight = duration / (kRC + duration);
 
-  overall_velocity_ =
-      Vector::Add(Vector::ScalarMult(overall_velocity_, 1 - weight),
-                  Vector::ScalarMult(velocity, weight));
+  overall_velocity_ = ScaleVector2d(overall_velocity_, (1 - weight)) +
+                      ScaleVector2d(velocity, weight);
 }
 
 }  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/vr_controller.h b/chrome/browser/android/vr_shell/vr_controller.h
index 3b9ff1c..9cc0b3d 100644
--- a/chrome/browser/android/vr_shell/vr_controller.h
+++ b/chrome/browser/android/vr_shell/vr_controller.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "chrome/browser/android/vr_shell/vr_controller_model.h"
 #include "device/vr/android/gvr/gvr_gamepad_data_provider.h"
+#include "device/vr/vr_types.h"
 #include "third_party/WebKit/public/platform/WebGestureEvent.h"
 #include "third_party/WebKit/public/platform/WebInputEvent.h"
 #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
@@ -52,9 +53,9 @@
 
   float TouchPosY();
 
-  gvr::Quatf Orientation() const;
+  vr::Quatf Orientation() const;
 
-  gvr::Mat4f GetTransform() const;
+  void GetTransform(vr::Mat4f* out) const;
 
   VrControllerModel::State GetModelState() const;
 
@@ -76,7 +77,7 @@
   };
 
   struct TouchPoint {
-    gvr::Vec2f position;
+    gfx::Vector2dF position;
     int64_t timestamp;
   };
 
@@ -111,7 +112,7 @@
 
   // Returns true if the touch position is within the slop of the initial touch
   // point, false otherwise.
-  bool InSlop(const gvr::Vec2f touch_position);
+  bool InSlop(const gfx::Vector2dF touch_position);
 
   // Returns true if the gesture is in horizontal direction.
   bool IsHorizontalGesture();
@@ -155,13 +156,13 @@
   std::unique_ptr<TouchPoint> init_touch_point_;
 
   // Overall velocity
-  gvr::Vec2f overall_velocity_;
+  gfx::Vector2dF overall_velocity_;
 
   // Last velocity that is used for fling and direction detection
-  gvr::Vec2f last_velocity_;
+  gfx::Vector2dF last_velocity_;
 
   // Displacement of the touch point from the previews to the current touch
-  gvr::Vec2f displacement_;
+  gfx::Vector2dF displacement_;
 
   int64_t last_touch_timestamp_ = 0;
   int64_t last_timestamp_nanos_ = 0;
diff --git a/chrome/browser/android/vr_shell/vr_gl_util.cc b/chrome/browser/android/vr_shell/vr_gl_util.cc
index ad362a1..1c68ee1 100644
--- a/chrome/browser/android/vr_shell/vr_gl_util.cc
+++ b/chrome/browser/android/vr_shell/vr_gl_util.cc
@@ -7,7 +7,7 @@
 namespace vr_shell {
 
 // This code is adapted from the GVR Treasure Hunt demo source.
-std::array<float, 16> MatrixToGLArray(const gvr::Mat4f& matrix) {
+std::array<float, 16> MatrixToGLArray(const vr::Mat4f& matrix) {
   // Note that this performs a *transpose* to a column-major matrix array, as
   // expected by GL. The input matrix has translation components at [i][3] for
   // use with row vectors and premultiplied transforms. In the output, the
@@ -15,29 +15,19 @@
   std::array<float, 16> result;
   for (int i = 0; i < 4; ++i) {
     for (int j = 0; j < 4; ++j) {
-      result[j * 4 + i] = matrix.m[i][j];
+      result[j * 4 + i] = matrix[i][j];
     }
   }
   return result;
 }
 
 // This code is adapted from the GVR Treasure Hunt demo source.
-gvr::Rectf ModulateRect(const gvr::Rectf& rect, float width, float height) {
-  gvr::Rectf result = {rect.left * width, rect.right * width,
-                       rect.bottom * height, rect.top * height};
-  return result;
-}
-
-// This code is adapted from the GVR Treasure Hunt demo source.
-gvr::Recti CalculatePixelSpaceRect(const gvr::Sizei& texture_size,
-                                   const gvr::Rectf& texture_rect) {
-  float width = static_cast<float>(texture_size.width);
-  float height = static_cast<float>(texture_size.height);
-  gvr::Rectf rect = ModulateRect(texture_rect, width, height);
-  gvr::Recti result = {
-      static_cast<int>(rect.left), static_cast<int>(rect.right),
-      static_cast<int>(rect.bottom), static_cast<int>(rect.top)};
-  return result;
+gfx::Rect CalculatePixelSpaceRect(const gfx::Size& texture_size,
+                                  const gfx::RectF& texture_rect) {
+  const gfx::RectF rect =
+      ScaleRect(texture_rect, static_cast<float>(texture_size.width()),
+                static_cast<float>(texture_size.height()));
+  return gfx::Rect(rect.x(), rect.y(), rect.width(), rect.height());
 }
 
 GLuint CompileShader(GLenum shader_type,
diff --git a/chrome/browser/android/vr_shell/vr_gl_util.h b/chrome/browser/android/vr_shell/vr_gl_util.h
index 61e8e99..59052929 100644
--- a/chrome/browser/android/vr_shell/vr_gl_util.h
+++ b/chrome/browser/android/vr_shell/vr_gl_util.h
@@ -8,17 +8,15 @@
 #include <array>
 #include <string>
 
-#include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
+#include "device/vr/vr_types.h"
 #include "ui/gl/gl_bindings.h"
 
 namespace vr_shell {
 
-std::array<float, 16> MatrixToGLArray(const gvr::Mat4f& matrix);
+std::array<float, 16> MatrixToGLArray(const vr::Mat4f& matrix);
 
-gvr::Rectf ModulateRect(const gvr::Rectf& rect, float width, float height);
-
-gvr::Recti CalculatePixelSpaceRect(const gvr::Sizei& texture_size,
-                                   const gvr::Rectf& texture_rect);
+gfx::Rect CalculatePixelSpaceRect(const gfx::Size& texture_size,
+                                  const gfx::RectF& texture_rect);
 
 // Compile a shader.
 GLuint CompileShader(GLenum shader_type,
diff --git a/chrome/browser/android/vr_shell/vr_math.cc b/chrome/browser/android/vr_shell/vr_math.cc
deleted file mode 100644
index 289a145..0000000
--- a/chrome/browser/android/vr_shell/vr_math.cc
+++ /dev/null
@@ -1,250 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/android/vr_shell/vr_math.h"
-
-#include <cmath>
-
-#include "base/logging.h"
-
-namespace vr_shell {
-
-// Internal matrix layout:
-//
-//   m[0][0], m[0][1], m[0][2], m[0][3],
-//   m[1][0], m[1][1], m[1][2], m[1][3],
-//   m[2][0], m[2][1], m[2][2], m[2][3],
-//   m[3][0], m[3][1], m[3][2], m[3][3],
-//
-// The translation component is in the right column m[i][3].
-//
-// The bottom row m[3][i] is (0, 0, 0, 1) for non-perspective transforms.
-//
-// These matrices are intended to be used to premultiply column vectors
-// for transforms, so successive transforms need to be left-multiplied.
-
-void SetIdentityM(gvr::Mat4f& mat) {
-  float* m = reinterpret_cast<float*>(mat.m);
-  for (int i = 0; i < 16; i++) {
-    m[i] = 0;
-  }
-  for (int i = 0; i < 16; i += 5) {
-    m[i] = 1.0f;
-  }
-}
-
-// Left multiply a translation matrix.
-void TranslateM(gvr::Mat4f& tmat, gvr::Mat4f& mat, float x, float y, float z) {
-  if (&tmat != &mat) {
-    for (int i = 0; i < 4; ++i) {
-      for (int j = 0; j < 4; ++j) {
-        tmat.m[i][j] = mat.m[i][j];
-      }
-    }
-  }
-  tmat.m[0][3] += x;
-  tmat.m[1][3] += y;
-  tmat.m[2][3] += z;
-}
-
-// Left multiply a scale matrix.
-void ScaleM(gvr::Mat4f& tmat,
-            const gvr::Mat4f& mat,
-            float x,
-            float y,
-            float z) {
-  if (&tmat != &mat) {
-    for (int i = 0; i < 4; ++i) {
-      for (int j = 0; j < 3; ++j) {
-        tmat.m[i][j] = mat.m[i][j];
-      }
-    }
-  }
-  // Multiply all rows including translation components.
-  for (int j = 0; j < 4; ++j) {
-    tmat.m[0][j] *= x;
-    tmat.m[1][j] *= y;
-    tmat.m[2][j] *= z;
-  }
-}
-
-gvr::Vec3f MatrixVectorMul(const gvr::Mat4f& m, const gvr::Vec3f& v) {
-  gvr::Vec3f res;
-  res.x = m.m[0][0] * v.x + m.m[0][1] * v.y + m.m[0][2] * v.z + m.m[0][3];
-  res.y = m.m[1][0] * v.x + m.m[1][1] * v.y + m.m[1][2] * v.z + m.m[1][3];
-  res.z = m.m[2][0] * v.x + m.m[2][1] * v.y + m.m[2][2] * v.z + m.m[2][3];
-  return res;
-}
-
-// Rotation only, ignore translation components.
-gvr::Vec3f MatrixVectorRotate(const gvr::Mat4f& m, const gvr::Vec3f& v) {
-  gvr::Vec3f res;
-  res.x = m.m[0][0] * v.x + m.m[0][1] * v.y + m.m[0][2] * v.z;
-  res.y = m.m[1][0] * v.x + m.m[1][1] * v.y + m.m[1][2] * v.z;
-  res.z = m.m[2][0] * v.x + m.m[2][1] * v.y + m.m[2][2] * v.z;
-  return res;
-}
-
-gvr::Mat4f MatrixMul(const gvr::Mat4f& matrix1, const gvr::Mat4f& matrix2) {
-  gvr::Mat4f result;
-  for (int i = 0; i < 4; ++i) {
-    for (int j = 0; j < 4; ++j) {
-      result.m[i][j] = 0.0f;
-      for (int k = 0; k < 4; ++k) {
-        result.m[i][j] += matrix1.m[i][k] * matrix2.m[k][j];
-      }
-    }
-  }
-  return result;
-}
-
-gvr::Mat4f PerspectiveMatrixFromView(const gvr::Rectf& fov,
-                                     float z_near,
-                                     float z_far) {
-  gvr::Mat4f result;
-  const float x_left = -std::tan(fov.left * M_PI / 180.0f) * z_near;
-  const float x_right = std::tan(fov.right * M_PI / 180.0f) * z_near;
-  const float y_bottom = -std::tan(fov.bottom * M_PI / 180.0f) * z_near;
-  const float y_top = std::tan(fov.top * M_PI / 180.0f) * z_near;
-
-  DCHECK(x_left < x_right && y_bottom < y_top && z_near < z_far &&
-         z_near > 0.0f && z_far > 0.0f);
-  const float X = (2 * z_near) / (x_right - x_left);
-  const float Y = (2 * z_near) / (y_top - y_bottom);
-  const float A = (x_right + x_left) / (x_right - x_left);
-  const float B = (y_top + y_bottom) / (y_top - y_bottom);
-  const float C = (z_near + z_far) / (z_near - z_far);
-  const float D = (2 * z_near * z_far) / (z_near - z_far);
-
-  for (int i = 0; i < 4; ++i) {
-    for (int j = 0; j < 4; ++j) {
-      result.m[i][j] = 0.0f;
-    }
-  }
-  result.m[0][0] = X;
-  result.m[0][2] = A;
-  result.m[1][1] = Y;
-  result.m[1][2] = B;
-  result.m[2][2] = C;
-  result.m[2][3] = D;
-  result.m[3][2] = -1;
-
-  return result;
-}
-
-gvr::Vec3f GetForwardVector(const gvr::Mat4f& matrix) {
-  // Same as multiplying the inverse of the rotation component of the matrix by
-  // (0, 0, -1, 0).
-  return {-matrix.m[2][0], -matrix.m[2][1], -matrix.m[2][2]};
-}
-
-gvr::Vec3f GetTranslation(const gvr::Mat4f& matrix) {
-  return {matrix.m[0][3], matrix.m[1][3], matrix.m[2][3]};
-}
-
-float VectorLength(const gvr::Vec3f& vec) {
-  return sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z);
-}
-
-gvr::Vec3f VectorSubtract(const gvr::Vec3f& a, const gvr::Vec3f& b) {
-  return {a.x - b.x, a.y - b.y, a.z - b.z};
-}
-
-float NormalizeVector(gvr::Vec3f& vec) {
-  float len = VectorLength(vec);
-  vec.x /= len;
-  vec.y /= len;
-  vec.z /= len;
-  return len;
-}
-
-float VectorDot(const gvr::Vec3f& a, const gvr::Vec3f& b) {
-  return a.x * b.x + a.y * b.y + a.z * b.z;
-}
-
-void NormalizeQuat(gvr::Quatf& quat) {
-  float len = sqrt(quat.qx * quat.qx + quat.qy * quat.qy + quat.qz * quat.qz +
-                   quat.qw * quat.qw);
-  quat.qx /= len;
-  quat.qy /= len;
-  quat.qz /= len;
-  quat.qw /= len;
-}
-
-gvr::Quatf QuatFromAxisAngle(const gvr::Vec3f& axis, float angle) {
-  // Rotation angle is the product of |angle| and the magnitude of |axis|.
-  gvr::Vec3f normal = axis;
-  float length = NormalizeVector(normal);
-  angle *= length;
-
-  gvr::Quatf res;
-  float s = sin(angle / 2);
-  res.qx = normal.x * s;
-  res.qy = normal.y * s;
-  res.qz = normal.z * s;
-  res.qw = cos(angle / 2);
-  return res;
-}
-
-gvr::Quatf QuatMultiply(const gvr::Quatf& a, const gvr::Quatf& b) {
-  gvr::Quatf res;
-  res.qw = a.qw * b.qw - a.qx * b.qx - a.qy * b.qy - a.qz * b.qz;
-  res.qx = a.qw * b.qx + a.qx * b.qw + a.qy * b.qz - a.qz * b.qy;
-  res.qy = a.qw * b.qy - a.qx * b.qz + a.qy * b.qw + a.qz * b.qx;
-  res.qz = a.qw * b.qz + a.qx * b.qy - a.qy * b.qx + a.qz * b.qw;
-  return res;
-}
-
-gvr::Mat4f QuatToMatrix(const gvr::Quatf& quat) {
-  const float x2 = quat.qx * quat.qx;
-  const float y2 = quat.qy * quat.qy;
-  const float z2 = quat.qz * quat.qz;
-  const float xy = quat.qx * quat.qy;
-  const float xz = quat.qx * quat.qz;
-  const float xw = quat.qx * quat.qw;
-  const float yz = quat.qy * quat.qz;
-  const float yw = quat.qy * quat.qw;
-  const float zw = quat.qz * quat.qw;
-
-  const float m11 = 1.0f - 2.0f * y2 - 2.0f * z2;
-  const float m12 = 2.0f * (xy - zw);
-  const float m13 = 2.0f * (xz + yw);
-  const float m21 = 2.0f * (xy + zw);
-  const float m22 = 1.0f - 2.0f * x2 - 2.0f * z2;
-  const float m23 = 2.0f * (yz - xw);
-  const float m31 = 2.0f * (xz - yw);
-  const float m32 = 2.0f * (yz + xw);
-  const float m33 = 1.0f - 2.0f * x2 - 2.0f * y2;
-
-  return {{{m11, m12, m13, 0.0f},
-           {m21, m22, m23, 0.0f},
-           {m31, m32, m33, 0.0f},
-           {0.0f, 0.0f, 0.0f, 1.0f}}};
-}
-
-gvr::Vec3f GetRayPoint(const gvr::Vec3f& rayOrigin,
-                       const gvr::Vec3f& rayVector,
-                       float scale) {
-  gvr::Vec3f v;
-  v.x = rayOrigin.x + scale * rayVector.x;
-  v.y = rayOrigin.y + scale * rayVector.y;
-  v.z = rayOrigin.z + scale * rayVector.z;
-  return v;
-}
-
-float Distance(const gvr::Vec3f& vec1, const gvr::Vec3f& vec2) {
-  return VectorLength(VectorSubtract(vec1, vec2));
-}
-
-bool XZAngle(const gvr::Vec3f& vec1, const gvr::Vec3f& vec2, float* angle) {
-  float len1 = VectorLength(vec1);
-  float len2 = VectorLength(vec2);
-  if (len1 == 0 || len2 == 0)
-    return false;
-  float cross_p = vec1.x * vec2.z - vec1.z * vec2.x;
-  *angle = asin(cross_p / (len1 * len2));
-  return true;
-}
-
-}  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/vr_math.h b/chrome/browser/android/vr_shell/vr_math.h
deleted file mode 100644
index 4b520c29..0000000
--- a/chrome/browser/android/vr_shell/vr_math.h
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_ANDROID_VR_SHELL_VR_MATH_H_
-#define CHROME_BROWSER_ANDROID_VR_SHELL_VR_MATH_H_
-
-#include <array>
-
-#include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
-
-namespace vr_shell {
-
-// 2D rectangles. Unlike gvr::Rectf and gvr::Recti, these have width and height
-// rather than right and top.
-typedef struct Recti {
-  int x;
-  int y;
-  int width;
-  int height;
-} Recti;
-
-typedef struct Rectf {
-  float x;
-  float y;
-  float width;
-  float height;
-} Rectf;
-
-typedef struct RotationAxisAngle {
-  float x;
-  float y;
-  float z;
-  float angle;
-} RotationAxisAngle;
-
-typedef struct Colorf {
-  float r;
-  float g;
-  float b;
-  float a;
-} Colorf;
-
-void SetIdentityM(gvr::Mat4f& mat);
-void TranslateM(gvr::Mat4f& tmat, gvr::Mat4f& mat, float x, float y, float z);
-void ScaleM(gvr::Mat4f& tmat, const gvr::Mat4f& mat, float x, float y, float z);
-
-// Util functions that are copied from the treasure_hunt NDK demo in
-// third_party/gvr-andoir-sdk/ folder.
-gvr::Vec3f MatrixVectorMul(const gvr::Mat4f& m, const gvr::Vec3f& v);
-gvr::Vec3f MatrixVectorRotate(const gvr::Mat4f& m, const gvr::Vec3f& v);
-gvr::Mat4f MatrixMul(const gvr::Mat4f& matrix1, const gvr::Mat4f& matrix2);
-gvr::Mat4f PerspectiveMatrixFromView(const gvr::Rectf& fov,
-                                     float z_near,
-                                     float z_far);
-
-// Provides the direction the head is looking towards as a 3x1 unit vector.
-gvr::Vec3f GetForwardVector(const gvr::Mat4f& matrix);
-
-gvr::Vec3f GetTranslation(const gvr::Mat4f& matrix);
-
-gvr::Mat4f QuatToMatrix(const gvr::Quatf& quat);
-
-float VectorLength(const gvr::Vec3f& vec);
-gvr::Vec3f VectorSubtract(const gvr::Vec3f& a, const gvr::Vec3f& b);
-float VectorDot(const gvr::Vec3f& a, const gvr::Vec3f& b);
-
-// Normalize a vector, and return its original length.
-float NormalizeVector(gvr::Vec3f& vec);
-
-void NormalizeQuat(gvr::Quatf& quat);
-
-gvr::Quatf QuatFromAxisAngle(const gvr::Vec3f& axis, float angle);
-
-gvr::Vec3f GetRayPoint(const gvr::Vec3f& rayOrigin,
-                       const gvr::Vec3f& rayVector,
-                       float scale);
-
-float Distance(const gvr::Vec3f& vec1, const gvr::Vec3f& vec2);
-
-// Angle between the vectors' projections to the XZ plane.
-bool XZAngle(const gvr::Vec3f& vec1, const gvr::Vec3f& vec2, float* angle);
-
-}  // namespace vr_shell
-
-#endif  // CHROME_BROWSER_ANDROID_VR_SHELL_VR_MATH_H_
diff --git a/chrome/browser/android/vr_shell/vr_shell.cc b/chrome/browser/android/vr_shell/vr_shell.cc
index f4547ff..eaedca5 100644
--- a/chrome/browser/android/vr_shell/vr_shell.cc
+++ b/chrome/browser/android/vr_shell/vr_shell.cc
@@ -370,9 +370,9 @@
 }
 
 void VrShell::UpdateWebVRTextureBounds(int16_t frame_index,
-                                       const gvr::Rectf& left_bounds,
-                                       const gvr::Rectf& right_bounds,
-                                       const gvr::Sizei& source_size) {
+                                       const gfx::RectF& left_bounds,
+                                       const gfx::RectF& right_bounds,
+                                       const gfx::Size& source_size) {
   PostToGlThreadWhenReady(base::Bind(&VrShellGl::UpdateWebVRTextureBounds,
                                      gl_thread_->GetVrShellGl(), frame_index,
                                      left_bounds, right_bounds, source_size));
diff --git a/chrome/browser/android/vr_shell/vr_shell.h b/chrome/browser/android/vr_shell/vr_shell.h
index bf668d5..ae3d136 100644
--- a/chrome/browser/android/vr_shell/vr_shell.h
+++ b/chrome/browser/android/vr_shell/vr_shell.h
@@ -202,9 +202,9 @@
   void SubmitWebVRFrame(int16_t frame_index,
                         const gpu::MailboxHolder& mailbox) override;
   void UpdateWebVRTextureBounds(int16_t frame_index,
-                                const gvr::Rectf& left_bounds,
-                                const gvr::Rectf& right_bounds,
-                                const gvr::Sizei& source_size) override;
+                                const gfx::RectF& left_bounds,
+                                const gfx::RectF& right_bounds,
+                                const gfx::Size& source_size) override;
   void OnVRVsyncProviderRequest(
       device::mojom::VRVSyncProviderRequest request) override;
   void UpdateVSyncInterval(int64_t timebase_nanos,
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.cc b/chrome/browser/android/vr_shell/vr_shell_gl.cc
index 4191c59..a795a86f 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.cc
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.cc
@@ -19,12 +19,12 @@
 #include "chrome/browser/android/vr_shell/ui_scene.h"
 #include "chrome/browser/android/vr_shell/vr_controller.h"
 #include "chrome/browser/android/vr_shell/vr_gl_util.h"
-#include "chrome/browser/android/vr_shell/vr_math.h"
 #include "chrome/browser/android/vr_shell/vr_shell.h"
 #include "chrome/browser/android/vr_shell/vr_shell_renderer.h"
 #include "device/vr/android/gvr/gvr_delegate.h"
 #include "device/vr/android/gvr/gvr_device.h"
 #include "device/vr/android/gvr/gvr_gamepad_data_provider.h"
+#include "device/vr/vr_math.h"
 #include "third_party/WebKit/public/platform/WebInputEvent.h"
 #include "third_party/WebKit/public/platform/WebMouseEvent.h"
 #include "ui/gl/android/scoped_java_surface.h"
@@ -48,11 +48,11 @@
 // Angle (radians) the beam down from the controller axis, for wrist comfort.
 static constexpr float kErgoAngleOffset = 0.26f;
 
-static constexpr gvr::Vec3f kOrigin = {0.0f, 0.0f, 0.0f};
+static constexpr gfx::Point3F kOrigin = {0.0f, 0.0f, 0.0f};
 
 // In lieu of an elbow model, we assume a position for the user's hand.
 // TODO(mthiesse): Handedness options.
-static constexpr gvr::Vec3f kHandPosition = {0.2f, -0.5f, -0.2f};
+static constexpr gfx::Point3F kHandPosition = {0.2f, -0.5f, -0.2f};
 
 // Fraction of the distance to the object the cursor is drawn at to avoid
 // rounding errors drawing the cursor behind the object.
@@ -68,7 +68,7 @@
 // Pixel dimensions and field of view for the head-locked content. This
 // is currently sized to fit the WebVR "insecure transport" warnings,
 // adjust it as needed if there is additional content.
-static constexpr gvr::Sizei kHeadlockedBufferDimensions = {1024, 1024};
+static constexpr gfx::Size kHeadlockedBufferDimensions = {1024, 1024};
 static constexpr gvr::Rectf kHeadlockedBufferFov = {20.f, 20.f, 20.f, 20.f};
 
 // The GVR viewport list has two entries (left eye and right eye) for each
@@ -87,10 +87,10 @@
 // Generate a quaternion representing the rotation from the negative Z axis
 // (0, 0, -1) to a specified vector. This is an optimized version of a more
 // general vector-to-vector calculation.
-gvr::Quatf GetRotationFromZAxis(gvr::Vec3f vec) {
-  vr_shell::NormalizeVector(vec);
-  gvr::Quatf quat;
-  quat.qw = 1.0f - vec.z;
+vr::Quatf GetRotationFromZAxis(gfx::Vector3dF vec) {
+  vr::NormalizeVector(&vec);
+  vr::Quatf quat;
+  quat.qw = 1.0f - vec.z();
   if (quat.qw < 1e-6f) {
     // Degenerate case: vectors are exactly opposite. Replace by an
     // arbitrary 180 degree rotation to avoid invalid normalization.
@@ -99,10 +99,10 @@
     quat.qz = 0.0f;
     quat.qw = 0.0f;
   } else {
-    quat.qx = vec.y;
-    quat.qy = -vec.x;
+    quat.qx = vec.y();
+    quat.qy = -vec.x();
     quat.qz = 0.0f;
-    vr_shell::NormalizeQuat(quat);
+    vr::NormalizeQuat(&quat);
   }
   return quat;
 }
@@ -133,6 +133,22 @@
   callback.Run(std::move(info));
 }
 
+void MatfToGvrMat(const vr::Mat4f& in, gvr::Mat4f* out) {
+  // If our std::array implementation doesn't have any non-data members, we can
+  // just cast the gvr matrix to an std::array.
+  static_assert(sizeof(in) == sizeof(*out),
+                "Cannot reinterpret gvr::Mat4f as vr::Matf");
+  *out = *reinterpret_cast<gvr::Mat4f*>(const_cast<vr::Mat4f*>(&in));
+}
+
+void GvrMatToMatf(const gvr::Mat4f& in, vr::Mat4f* out) {
+  // If our std::array implementation doesn't have any non-data members, we can
+  // just cast the gvr matrix to an std::array.
+  static_assert(sizeof(in) == sizeof(*out),
+                "Cannot reinterpret gvr::Mat4f as vr::Matf");
+  *out = *reinterpret_cast<vr::Mat4f*>(const_cast<gvr::Mat4f*>(&in));
+}
+
 }  // namespace
 
 VrShellGl::VrShellGl(
@@ -229,16 +245,16 @@
       &VrShellGl::OnContentFrameAvailable, weak_ptr_factory_.GetWeakPtr()));
   webvr_surface_texture_->SetFrameAvailableCallback(base::Bind(
       &VrShellGl::OnWebVRFrameAvailable, weak_ptr_factory_.GetWeakPtr()));
-  ui_surface_texture_->SetDefaultBufferSize(ui_tex_physical_size_.width,
-                                            ui_tex_physical_size_.height);
+  ui_surface_texture_->SetDefaultBufferSize(ui_tex_physical_size_.width(),
+                                            ui_tex_physical_size_.height());
   content_surface_texture_->SetDefaultBufferSize(
-      content_tex_physical_size_.width, content_tex_physical_size_.height);
+      content_tex_physical_size_.width(), content_tex_physical_size_.height());
   InitializeRenderer();
 
-  gvr::Sizei webvr_size =
+  gfx::Size webvr_size =
       device::GvrDelegate::GetRecommendedWebVrSize(gvr_api_.get());
-  DVLOG(1) << __FUNCTION__ << ": resize initial to " << webvr_size.width << "x"
-           << webvr_size.height;
+  DVLOG(1) << __FUNCTION__ << ": resize initial to " << webvr_size.width()
+           << "x" << webvr_size.height();
 
   CreateOrResizeWebVRSurface(webvr_size);
 
@@ -264,7 +280,7 @@
                             ui_surface_->j_surface().obj()));
 }
 
-void VrShellGl::CreateOrResizeWebVRSurface(const gvr::Sizei& size) {
+void VrShellGl::CreateOrResizeWebVRSurface(const gfx::Size& size) {
   if (!webvr_surface_texture_) {
     DLOG(ERROR) << "No WebVR surface texture available";
     return;
@@ -276,16 +292,16 @@
     return;
   }
 
-  if (!size.width || !size.height) {
+  if (!size.width() || !size.height()) {
     // Invalid size, defer until a new size arrives on a future bounds update.
     return;
   }
 
-  webvr_surface_texture_->SetDefaultBufferSize(size.width, size.height);
+  webvr_surface_texture_->SetDefaultBufferSize(size.width(), size.height());
   webvr_surface_size_ = size;
 
   if (mailbox_bridge_) {
-    mailbox_bridge_->ResizeSurface(size.width, size.height);
+    mailbox_bridge_->ResizeSurface(size.width(), size.height());
   } else {
     mailbox_bridge_ = base::MakeUnique<MailboxToSurfaceBridge>();
     mailbox_bridge_->CreateSurface(webvr_surface_texture_.get());
@@ -389,21 +405,26 @@
 
 void VrShellGl::InitializeRenderer() {
   gvr_api_->InitializeGl();
-  webvr_head_pose_.assign(kPoseRingBufferSize,
-                          gvr_api_->GetHeadSpaceFromStartSpaceRotation(
-                              gvr::GvrApi::GetTimePointNow()));
+  vr::Mat4f head_pose;
+  device::GvrDelegate::GetGvrPoseWithNeckModel(gvr_api_.get(), &head_pose);
+  webvr_head_pose_.assign(kPoseRingBufferSize, head_pose);
 
   std::vector<gvr::BufferSpec> specs;
   // For kFramePrimaryBuffer (primary VrShell and WebVR content)
   specs.push_back(gvr_api_->CreateBufferSpec());
-  render_size_primary_ = specs[kFramePrimaryBuffer].GetSize();
+  gvr::Sizei render_size_primary = specs[kFramePrimaryBuffer].GetSize();
+  render_size_primary_ = {render_size_primary.width,
+                          render_size_primary.height};
   render_size_vrshell_ = render_size_primary_;
 
   // For kFrameHeadlockedBuffer (for WebVR insecure content warning).
   // Set this up at fixed resolution, the (smaller) FOV gets set below.
   specs.push_back(gvr_api_->CreateBufferSpec());
-  specs.back().SetSize(kHeadlockedBufferDimensions);
-  render_size_headlocked_ = specs[kFrameHeadlockedBuffer].GetSize();
+  specs.back().SetSize({kHeadlockedBufferDimensions.width(),
+                        kHeadlockedBufferDimensions.height()});
+  gvr::Sizei render_size_headlocked = specs[kFrameHeadlockedBuffer].GetSize();
+  render_size_headlocked_ = {render_size_headlocked.width,
+                             render_size_headlocked.height};
 
   swap_chain_.reset(new gvr::SwapChain(gvr_api_->CreateSwapChain(specs)));
 
@@ -468,7 +489,7 @@
       FROM_HERE, base::Bind(&VrShell::UpdateGamepadData, weak_vr_shell_, pad));
 }
 
-void VrShellGl::HandleControllerInput(const gvr::Vec3f& forward_vector) {
+void VrShellGl::HandleControllerInput(const gfx::Vector3dF& forward_vector) {
   if (ShouldDrawWebVr()) {
     // Process screen touch events for Cardboard button compatibility.
     // Also send tap events for controller "touchpad click" events.
@@ -487,7 +508,7 @@
     }
   }
 
-  gvr::Vec3f ergo_neutral_pose;
+  gfx::Vector3dF ergo_neutral_pose;
   if (!controller_->IsConnected()) {
     // No controller detected, set up a gaze cursor that tracks the
     // forward direction.
@@ -498,8 +519,10 @@
     controller_quat_ = controller_->Orientation();
   }
 
-  gvr::Mat4f mat = QuatToMatrix(controller_quat_);
-  gvr::Vec3f controller_direction = MatrixVectorMul(mat, ergo_neutral_pose);
+  vr::Mat4f mat;
+  QuatToMatrix(controller_quat_, &mat);
+  gfx::Vector3dF controller_direction =
+      vr::MatrixVectorMul(mat, ergo_neutral_pose);
 
   HandleControllerAppButtonActivity(controller_direction);
 
@@ -522,13 +545,14 @@
   // that the sphere is centered at the controller, rather than the eye, for
   // simplicity.
   float distance = scene_->GetBackgroundDistance();
-  target_point_ = GetRayPoint(kHandPosition, controller_direction, distance);
-  gvr::Vec3f eye_to_target = target_point_;
-  NormalizeVector(eye_to_target);
+  target_point_ =
+      vr::GetRayPoint(kHandPosition, controller_direction, distance);
+  gfx::Vector3dF eye_to_target = target_point_ - kOrigin;
+  vr::NormalizeVector(&eye_to_target);
 
   // Determine which UI element (if any) intersects the line between the eyes
   // and the controller target position.
-  float closest_element_distance = VectorLength(target_point_);
+  float closest_element_distance = (target_point_ - kOrigin).Length();
   target_element_ = nullptr;
   float target_x;
   float target_y;
@@ -544,13 +568,13 @@
     if (distance_to_plane < 0 || distance_to_plane >= closest_element_distance)
       continue;
 
-    gvr::Vec3f plane_intersection_point =
-        GetRayPoint(kOrigin, eye_to_target, distance_to_plane);
-    gvr::Vec2f unit_xy_point =
+    gfx::Point3F plane_intersection_point =
+        vr::GetRayPoint(kOrigin, eye_to_target, distance_to_plane);
+    gfx::PointF unit_xy_point =
         plane->GetUnitRectangleCoordinates(plane_intersection_point);
 
-    float x = 0.5f + unit_xy_point.x;
-    float y = 0.5f - unit_xy_point.y;
+    float x = 0.5f + unit_xy_point.x();
+    float y = 0.5f - unit_xy_point.y();
     if (x < 0.0f || x >= 1.0f || y < 0.0f || y >= 1.0f)
       continue;
 
@@ -568,16 +592,17 @@
   int pixel_y = 0;
 
   if (target_element_ != nullptr) {
-    Rectf pixel_rect;
+    gfx::RectF pixel_rect;
     if (target_element_->fill == Fill::CONTENT) {
-      pixel_rect = {0, 0, content_tex_css_width_, content_tex_css_height_};
+      pixel_rect.SetRect(0, 0, content_tex_css_width_, content_tex_css_height_);
     } else {
-      pixel_rect = {target_element_->copy_rect.x, target_element_->copy_rect.y,
-                    target_element_->copy_rect.width,
-                    target_element_->copy_rect.height};
+      pixel_rect.SetRect(target_element_->copy_rect.x(),
+                         target_element_->copy_rect.y(),
+                         target_element_->copy_rect.width(),
+                         target_element_->copy_rect.height());
     }
-    pixel_x = pixel_rect.x + pixel_rect.width * target_x;
-    pixel_y = pixel_rect.y + pixel_rect.height * target_y;
+    pixel_x = pixel_rect.x() + pixel_rect.width() * target_x;
+    pixel_y = pixel_rect.y() + pixel_rect.height() * target_y;
 
     switch (target_element_->fill) {
       case Fill::CONTENT:
@@ -594,7 +619,7 @@
 }
 
 void VrShellGl::HandleControllerAppButtonActivity(
-    const gvr::Vec3f& controller_direction) {
+    const gfx::Vector3dF& controller_direction) {
   // Note that button up/down state is transient, so ButtonDownHappened only
   // returns true for a single frame (and we're guaranteed not to miss it).
   if (controller_->ButtonDownHappened(
@@ -610,8 +635,8 @@
     // VrShellGl.
     UiInterface::Direction direction = UiInterface::NONE;
     float gesture_xz_angle;
-    if (XZAngle(controller_start_direction_, controller_direction,
-                &gesture_xz_angle)) {
+    if (vr::XZAngle(controller_start_direction_, controller_direction,
+                    &gesture_xz_angle)) {
       if (fabs(gesture_xz_angle) > kMinAppButtonGestureAngleRad) {
         direction =
             gesture_xz_angle < 0 ? UiInterface::LEFT : UiInterface::RIGHT;
@@ -746,10 +771,17 @@
         break;
 
       const WebVrBounds& bounds = pending_bounds_.front().second;
-      webvr_left_viewport_->SetSourceUv(bounds.left_bounds);
-      webvr_right_viewport_->SetSourceUv(bounds.right_bounds);
+      const gfx::RectF& left = bounds.left_bounds;
+      const gfx::RectF& right = bounds.right_bounds;
+      gvr::Rectf gvr_left_bounds = {left.x(), left.x() + left.width(),
+                                    left.y() + left.height(), left.y()};
+      webvr_left_viewport_->SetSourceUv(gvr_left_bounds);
+      gvr::Rectf gvr_right_bounds = {right.x(), right.x() + right.width(),
+                                     right.y() + right.height(), right.y()};
+      webvr_right_viewport_->SetSourceUv(gvr_right_bounds);
       DVLOG(1) << __FUNCTION__ << ": resize from pending_bounds to "
-               << bounds.source_size.width << "x" << bounds.source_size.height;
+               << bounds.source_size.width() << "x"
+               << bounds.source_size.height();
       CreateOrResizeWebVRSurface(bounds.source_size);
       pending_bounds_.pop();
     }
@@ -758,7 +790,7 @@
     buffer_viewport_list_->SetBufferViewport(GVR_RIGHT_EYE,
                                              *webvr_right_viewport_);
     if (render_size_primary_ != webvr_surface_size_) {
-      if (!webvr_surface_size_.width) {
+      if (!webvr_surface_size_.width()) {
         // Don't try to resize to 0x0 pixels, drop frames until we get a
         // valid size.
         return;
@@ -766,14 +798,18 @@
 
       render_size_primary_ = webvr_surface_size_;
       DVLOG(1) << __FUNCTION__ << ": resize GVR to "
-               << render_size_primary_.width << "x"
-               << render_size_primary_.height;
-      swap_chain_->ResizeBuffer(kFramePrimaryBuffer, render_size_primary_);
+               << render_size_primary_.width() << "x"
+               << render_size_primary_.height();
+      swap_chain_->ResizeBuffer(
+          kFramePrimaryBuffer,
+          {render_size_primary_.width(), render_size_primary_.height()});
     }
   } else {
     if (render_size_primary_ != render_size_vrshell_) {
       render_size_primary_ = render_size_vrshell_;
-      swap_chain_->ResizeBuffer(kFramePrimaryBuffer, render_size_primary_);
+      swap_chain_->ResizeBuffer(
+          kFramePrimaryBuffer,
+          {render_size_primary_.width(), render_size_primary_.height()});
     }
   }
 
@@ -789,7 +825,7 @@
     DrawWebVr();
   }
 
-  gvr::Mat4f head_pose;
+  vr::Mat4f head_pose;
 
   // When using async reprojection, we need to know which pose was
   // used in the WebVR app for drawing this frame and supply it when
@@ -801,7 +837,7 @@
                   "kPoseRingBufferSize must be a power of 2");
     head_pose = webvr_head_pose_[frame_index % kPoseRingBufferSize];
   } else {
-    head_pose = device::GvrDelegate::GetGvrPoseWithNeckModel(gvr_api_.get());
+    device::GvrDelegate::GetGvrPoseWithNeckModel(gvr_api_.get(), &head_pose);
   }
 
   // Update the render position of all UI elements (including desktop).
@@ -812,7 +848,7 @@
     // for both the gamepad API and UI input handling.
     TRACE_EVENT0("gpu", "VrShellGl::UpdateController");
     UpdateController();
-    HandleControllerInput(GetForwardVector(head_pose));
+    HandleControllerInput(vr::GetForwardVector(head_pose));
   }
 
   DrawWorldElements(head_pose);
@@ -828,7 +864,9 @@
 
   {
     TRACE_EVENT0("gpu", "VrShellGl::Submit");
-    frame.Submit(*buffer_viewport_list_, head_pose);
+    gvr::Mat4f mat;
+    MatfToGvrMat(head_pose, &mat);
+    frame.Submit(*buffer_viewport_list_, mat);
   }
 
   // No need to swap buffers for surfaceless rendering.
@@ -839,7 +877,7 @@
   }
 }
 
-void VrShellGl::DrawWorldElements(const gvr::Mat4f& head_pose) {
+void VrShellGl::DrawWorldElements(const vr::Mat4f& head_pose) {
   TRACE_EVENT0("gpu", "VrShellGl::DrawWorldElements");
 
   if (ShouldDrawWebVr()) {
@@ -856,7 +894,7 @@
     glEnable(GL_DEPTH_TEST);
     glDepthMask(GL_TRUE);
 
-    const Colorf& backgroundColor = scene_->GetBackgroundColor();
+    const vr::Colorf& backgroundColor = scene_->GetBackgroundColor();
     glClearColor(backgroundColor.r, backgroundColor.g, backgroundColor.b,
                  backgroundColor.a);
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@@ -881,15 +919,15 @@
 
   glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-  gvr::Mat4f identity_matrix;
-  SetIdentityM(identity_matrix);
+  vr::Mat4f identity_matrix;
+  vr::SetIdentityM(&identity_matrix);
   DrawUiView(identity_matrix, elements, render_size_headlocked_,
              kViewportListHeadlockedOffset, false);
 }
 
-void VrShellGl::DrawUiView(const gvr::Mat4f& head_pose,
+void VrShellGl::DrawUiView(const vr::Mat4f& head_pose,
                            const std::vector<const ContentRectangle*>& elements,
-                           const gvr::Sizei& render_size,
+                           const gfx::Size& render_size,
                            int viewport_offset,
                            bool draw_cursor) {
   TRACE_EVENT0("gpu", "VrShellGl::DrawUiView");
@@ -900,19 +938,25 @@
     buffer_viewport_list_->GetBufferViewport(eye + viewport_offset,
                                              buffer_viewport_.get());
 
-    const gvr::Mat4f eye_view_matrix =
-        MatrixMul(gvr_api_->GetEyeFromHeadMatrix(eye), head_pose);
+    vr::Mat4f eye_view_matrix;
+    vr::Mat4f eye_matrix;
+    GvrMatToMatf(gvr_api_->GetEyeFromHeadMatrix(eye), &eye_matrix);
+    vr::MatrixMul(eye_matrix, head_pose, &eye_view_matrix);
 
-    gvr::Recti pixel_rect =
-        CalculatePixelSpaceRect(render_size, buffer_viewport_->GetSourceUv());
-    glViewport(pixel_rect.left, pixel_rect.bottom,
-               pixel_rect.right - pixel_rect.left,
-               pixel_rect.top - pixel_rect.bottom);
+    gvr::Rectf gvr_rect = buffer_viewport_->GetSourceUv();
+    gfx::RectF rect(gvr_rect.left, gvr_rect.top, gvr_rect.right - gvr_rect.left,
+                    gvr_rect.bottom - gvr_rect.top);
+    gfx::Rect pixel_rect = CalculatePixelSpaceRect(render_size, rect);
+    glViewport(pixel_rect.x(), pixel_rect.y(), pixel_rect.width(),
+               pixel_rect.height());
 
-    const gvr::Mat4f render_matrix =
-        MatrixMul(PerspectiveMatrixFromView(buffer_viewport_->GetSourceFov(),
-                                            kZNear, kZFar),
-                  eye_view_matrix);
+    vr::Mat4f render_matrix;
+    vr::Mat4f perspective_matrix;
+    gvr::Rectf fov = buffer_viewport_->GetSourceFov();
+    vr::PerspectiveMatrixFromView(
+        {fov.left, fov.top, fov.right - fov.left, fov.bottom - fov.top}, kZNear,
+        kZFar, &perspective_matrix);
+    vr::MatrixMul(perspective_matrix, eye_view_matrix, &render_matrix);
 
     DrawElements(render_matrix, elementsInDrawOrder);
     if (draw_cursor) {
@@ -923,21 +967,19 @@
 }
 
 void VrShellGl::DrawElements(
-    const gvr::Mat4f& view_proj_matrix,
+    const vr::Mat4f& view_proj_matrix,
     const std::vector<const ContentRectangle*>& elements) {
   for (const auto* rect : elements) {
-    gvr::Mat4f transform = MatrixMul(view_proj_matrix, rect->TransformMatrix());
+    vr::Mat4f transform;
+    vr::MatrixMul(view_proj_matrix, rect->TransformMatrix(), &transform);
 
     switch (rect->fill) {
       case Fill::SPRITE: {
-        Rectf copy_rect;
-        copy_rect.x = static_cast<float>(rect->copy_rect.x) / ui_tex_css_width_;
-        copy_rect.y =
-            static_cast<float>(rect->copy_rect.y) / ui_tex_css_height_;
-        copy_rect.width =
-            static_cast<float>(rect->copy_rect.width) / ui_tex_css_width_;
-        copy_rect.height =
-            static_cast<float>(rect->copy_rect.height) / ui_tex_css_height_;
+        gfx::RectF copy_rect(
+            static_cast<float>(rect->copy_rect.x()) / ui_tex_css_width_,
+            static_cast<float>(rect->copy_rect.y()) / ui_tex_css_height_,
+            static_cast<float>(rect->copy_rect.width()) / ui_tex_css_width_,
+            static_cast<float>(rect->copy_rect.height()) / ui_tex_css_height_);
         jint texture_handle = ui_texture_id_;
         vr_shell_renderer_->GetTexturedQuadRenderer()->AddQuad(
             texture_handle, transform, copy_rect, rect->computed_opacity);
@@ -958,7 +1000,7 @@
         break;
       }
       case Fill::CONTENT: {
-        Rectf copy_rect = {0, 0, 1, 1};
+        gfx::RectF copy_rect = {0, 0, 1, 1};
         jint texture_handle = content_texture_id_;
         vr_shell_renderer_->GetTexturedQuadRenderer()->AddQuad(
             texture_handle, transform, copy_rect, rect->computed_opacity);
@@ -973,7 +1015,7 @@
 }
 
 std::vector<const ContentRectangle*> VrShellGl::GetElementsInDrawOrder(
-    const gvr::Mat4f& view_matrix,
+    const vr::Mat4f& view_matrix,
     const std::vector<const ContentRectangle*>& elements) {
   typedef std::pair<float, const ContentRectangle*> DistanceElementPair;
   std::vector<DistanceElementPair> zOrderedElementPairs;
@@ -981,9 +1023,11 @@
 
   for (const auto* element : elements) {
     // Distance is the abs(z) value in view space.
-    gvr::Vec3f element_position = GetTranslation(element->TransformMatrix());
+    gfx::Vector3dF element_position =
+        vr::GetTranslation(element->TransformMatrix());
+
     float distance =
-        std::fabs(MatrixVectorMul(view_matrix, element_position).z);
+        std::fabs(vr::MatrixVectorMul(view_matrix, element_position).z());
     zOrderedElementPairs.push_back(std::make_pair(distance, element));
   }
 
@@ -1007,81 +1051,90 @@
   return zOrderedElements;
 }
 
-void VrShellGl::DrawCursor(const gvr::Mat4f& render_matrix) {
-  gvr::Mat4f mat;
-  SetIdentityM(mat);
+void VrShellGl::DrawCursor(const vr::Mat4f& render_matrix) {
+  vr::Mat4f mat;
+  vr::SetIdentityM(&mat);
 
   // Draw the reticle.
 
   // Scale the pointer to have a fixed FOV size at any distance.
-  const float eye_to_target = Distance(target_point_, kOrigin);
-  ScaleM(mat, mat, kReticleWidth * eye_to_target,
-         kReticleHeight * eye_to_target, 1.0f);
+  const float eye_to_target =
+      std::sqrt(target_point_.SquaredDistanceTo(kOrigin));
+  vr::ScaleM(
+      mat,
+      {kReticleWidth * eye_to_target, kReticleHeight * eye_to_target, 1.0f},
+      &mat);
 
-  gvr::Quatf rotation;
+  vr::Quatf rotation;
   if (target_element_ != nullptr) {
     // Make the reticle planar to the element it's hitting.
     rotation = GetRotationFromZAxis(target_element_->GetNormal());
   } else {
     // Rotate the cursor to directly face the eyes.
-    rotation = GetRotationFromZAxis(target_point_);
+    rotation = GetRotationFromZAxis(target_point_ - kOrigin);
   }
-  mat = MatrixMul(QuatToMatrix(rotation), mat);
+  vr::Mat4f rotation_mat;
+  vr::QuatToMatrix(rotation, &rotation_mat);
+  vr::MatrixMul(rotation_mat, mat, &mat);
 
+  gfx::Point3F target_point = ScalePoint(target_point_, kReticleOffset);
   // Place the pointer slightly in front of the plane intersection point.
-  TranslateM(mat, mat, target_point_.x * kReticleOffset,
-             target_point_.y * kReticleOffset,
-             target_point_.z * kReticleOffset);
+  vr::TranslateM(mat, target_point - kOrigin, &mat);
 
-  gvr::Mat4f transform = MatrixMul(render_matrix, mat);
+  vr::Mat4f transform;
+  vr::MatrixMul(render_matrix, mat, &transform);
   vr_shell_renderer_->GetReticleRenderer()->Draw(transform);
 
   // Draw the laser.
 
   // Find the length of the beam (from hand to target).
-  const float laser_length = Distance(kHandPosition, target_point_);
+  const float laser_length =
+      std::sqrt(kHandPosition.SquaredDistanceTo(target_point));
 
   // Build a beam, originating from the origin.
-  SetIdentityM(mat);
+  vr::SetIdentityM(&mat);
 
   // Move the beam half its height so that its end sits on the origin.
-  TranslateM(mat, mat, 0.0f, 0.5f, 0.0f);
-  ScaleM(mat, mat, kLaserWidth, laser_length, 1);
+  vr::TranslateM(mat, {0.0f, 0.5f, 0.0f}, &mat);
+  vr::ScaleM(mat, {kLaserWidth, laser_length, 1}, &mat);
 
   // Tip back 90 degrees to flat, pointing at the scene.
-  const gvr::Quatf q = QuatFromAxisAngle({1.0f, 0.0f, 0.0f}, -M_PI / 2);
-  mat = MatrixMul(QuatToMatrix(q), mat);
+  const vr::Quatf quat = vr::QuatFromAxisAngle({1.0f, 0.0f, 0.0f, -M_PI / 2});
+  vr::QuatToMatrix(quat, &rotation_mat);
+  vr::MatrixMul(rotation_mat, mat, &mat);
 
-  const gvr::Vec3f beam_direction = {target_point_.x - kHandPosition.x,
-                                     target_point_.y - kHandPosition.y,
-                                     target_point_.z - kHandPosition.z};
-  const gvr::Mat4f beam_direction_mat =
-      QuatToMatrix(GetRotationFromZAxis(beam_direction));
+  const gfx::Vector3dF beam_direction = target_point_ - kHandPosition;
+
+  vr::Mat4f beam_direction_mat;
+  vr::QuatToMatrix(GetRotationFromZAxis(beam_direction), &beam_direction_mat);
 
   // Render multiple faces to make the laser appear cylindrical.
   const int faces = 4;
   for (int i = 0; i < faces; i++) {
     // Rotate around Z.
     const float angle = M_PI * 2 * i / faces;
-    const gvr::Quatf rot = QuatFromAxisAngle({0.0f, 0.0f, 1.0f}, angle);
-    gvr::Mat4f face_transform = MatrixMul(QuatToMatrix(rot), mat);
-
+    const vr::Quatf rot = vr::QuatFromAxisAngle({0.0f, 0.0f, 1.0f, angle});
+    vr::Mat4f face_transform;
+    vr::QuatToMatrix(rot, &face_transform);
+    vr::MatrixMul(face_transform, mat, &face_transform);
     // Orient according to target direction.
-    face_transform = MatrixMul(beam_direction_mat, face_transform);
+    vr::MatrixMul(beam_direction_mat, face_transform, &face_transform);
 
     // Move the beam origin to the hand.
-    TranslateM(face_transform, face_transform, kHandPosition.x, kHandPosition.y,
-               kHandPosition.z);
+    vr::TranslateM(face_transform, kHandPosition - kOrigin, &face_transform);
 
-    transform = MatrixMul(render_matrix, face_transform);
+    vr::MatrixMul(render_matrix, face_transform, &transform);
     vr_shell_renderer_->GetLaserRenderer()->Draw(transform);
   }
 }
 
-void VrShellGl::DrawController(const gvr::Mat4f& view_proj_matrix) {
+void VrShellGl::DrawController(const vr::Mat4f& view_proj_matrix) {
   if (!vr_shell_renderer_->GetControllerRenderer()->IsSetUp())
     return;
-  auto transform = MatrixMul(view_proj_matrix, controller_->GetTransform());
+  vr::Mat4f controller_transform;
+  controller_->GetTransform(&controller_transform);
+  vr::Mat4f transform;
+  vr::MatrixMul(view_proj_matrix, controller_transform, &transform);
   auto state = controller_->GetModelState();
   vr_shell_renderer_->GetControllerRenderer()->Draw(state, transform);
 }
@@ -1108,7 +1161,7 @@
   // it's not supported on older devices such as Nexus 5X.
   glClear(GL_COLOR_BUFFER_BIT);
 
-  glViewport(0, 0, webvr_surface_size_.width, webvr_surface_size_.height);
+  glViewport(0, 0, webvr_surface_size_.width(), webvr_surface_size_.height());
   vr_shell_renderer_->GetWebVrRenderer()->Draw(webvr_texture_id_);
 }
 
@@ -1138,12 +1191,16 @@
 }
 
 void VrShellGl::UpdateWebVRTextureBounds(int16_t frame_index,
-                                         const gvr::Rectf& left_bounds,
-                                         const gvr::Rectf& right_bounds,
-                                         const gvr::Sizei& source_size) {
+                                         const gfx::RectF& left_bounds,
+                                         const gfx::RectF& right_bounds,
+                                         const gfx::Size& source_size) {
   if (frame_index < 0) {
-    webvr_left_viewport_->SetSourceUv(left_bounds);
-    webvr_right_viewport_->SetSourceUv(right_bounds);
+    gvr::Rectf left = {left_bounds.x(), left_bounds.right(),
+                       left_bounds.bottom(), left_bounds.y()};
+    webvr_left_viewport_->SetSourceUv(left);
+    gvr::Rectf right = {right_bounds.x(), right_bounds.right(),
+                        right_bounds.bottom(), right_bounds.y()};
+    webvr_right_viewport_->SetSourceUv(right);
     CreateOrResizeWebVRSurface(source_size);
   } else {
     pending_bounds_.emplace(
@@ -1160,8 +1217,8 @@
 void VrShellGl::ContentPhysicalBoundsChanged(int width, int height) {
   if (content_surface_texture_.get())
     content_surface_texture_->SetDefaultBufferSize(width, height);
-  content_tex_physical_size_.width = width;
-  content_tex_physical_size_.height = height;
+  content_tex_physical_size_.set_width(width);
+  content_tex_physical_size_.set_height(height);
 }
 
 void VrShellGl::UIBoundsChanged(int width, int height) {
@@ -1172,8 +1229,8 @@
 void VrShellGl::UIPhysicalBoundsChanged(int width, int height) {
   if (ui_surface_texture_.get())
     ui_surface_texture_->SetDefaultBufferSize(width, height);
-  ui_tex_physical_size_.width = width;
-  ui_tex_physical_size_.height = height;
+  ui_tex_physical_size_.set_width(width);
+  ui_tex_physical_size_.set_height(height);
 }
 
 base::WeakPtr<VrShellGl> VrShellGl::GetWeakPtr() {
@@ -1259,7 +1316,7 @@
 
   TRACE_EVENT1("input", "VrShellGl::SendVSync", "frame", frame_index);
 
-  gvr::Mat4f head_mat;
+  vr::Mat4f head_mat;
   device::mojom::VRPosePtr pose =
       device::GvrDelegate::GetVRPosePtrWithNeckModel(gvr_api_.get(), &head_mat);
 
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.h b/chrome/browser/android/vr_shell/vr_shell_gl.h
index 68d7a5cd..97dbddf 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.h
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.h
@@ -17,6 +17,7 @@
 #include "base/single_thread_task_runner.h"
 #include "chrome/browser/android/vr_shell/vr_controller_model.h"
 #include "device/vr/vr_service.mojom.h"
+#include "device/vr/vr_types.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr.h"
 #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
@@ -51,11 +52,13 @@
 struct ContentRectangle;
 
 struct WebVrBounds {
-  WebVrBounds(gvr::Rectf left, gvr::Rectf right, gvr::Sizei size)
+  WebVrBounds(const gfx::RectF& left,
+              const gfx::RectF& right,
+              const gfx::Size& size)
       : left_bounds(left), right_bounds(right), source_size(size) {}
-  gvr::Rectf left_bounds;
-  gvr::Rectf right_bounds;
-  gvr::Sizei source_size;
+  gfx::RectF left_bounds;
+  gfx::RectF right_bounds;
+  gfx::Size source_size;
 };
 
 // This class manages all GLThread owned objects and GL rendering for VrShell.
@@ -83,7 +86,7 @@
   void OnResume();
 
   void SetWebVrMode(bool enabled);
-  void CreateOrResizeWebVRSurface(const gvr::Sizei& size);
+  void CreateOrResizeWebVRSurface(const gfx::Size& size);
   void CreateContentSurface();
   void ContentBoundsChanged(int width, int height);
   void ContentPhysicalBoundsChanged(int width, int height);
@@ -94,9 +97,9 @@
   void SetControllerModel(std::unique_ptr<VrControllerModel> model);
 
   void UpdateWebVRTextureBounds(int16_t frame_index,
-                                const gvr::Rectf& left_bounds,
-                                const gvr::Rectf& right_bounds,
-                                const gvr::Sizei& source_size);
+                                const gfx::RectF& left_bounds,
+                                const gfx::RectF& right_bounds,
+                                const gfx::Size& source_size);
 
   void UpdateScene(std::unique_ptr<base::ListValue> commands);
 
@@ -114,28 +117,28 @@
   void GvrInit(gvr_context* gvr_api);
   void InitializeRenderer();
   void DrawFrame(int16_t frame_index);
-  void DrawWorldElements(const gvr::Mat4f& head_pose);
+  void DrawWorldElements(const vr::Mat4f& head_pose);
   void DrawHeadLockedElements();
-  void DrawUiView(const gvr::Mat4f& head_pose,
+  void DrawUiView(const vr::Mat4f& head_pose,
                   const std::vector<const ContentRectangle*>& elements,
-                  const gvr::Sizei& render_size,
+                  const gfx::Size& render_size,
                   int viewport_offset,
                   bool draw_cursor);
-  void DrawElements(const gvr::Mat4f& view_proj_matrix,
+  void DrawElements(const vr::Mat4f& view_proj_matrix,
                     const std::vector<const ContentRectangle*>& elements);
   std::vector<const ContentRectangle*> GetElementsInDrawOrder(
-      const gvr::Mat4f& view_matrix,
+      const vr::Mat4f& view_matrix,
       const std::vector<const ContentRectangle*>& elements);
-  void DrawCursor(const gvr::Mat4f& render_matrix);
-  void DrawController(const gvr::Mat4f& view_proj_matrix);
+  void DrawCursor(const vr::Mat4f& render_matrix);
+  void DrawController(const vr::Mat4f& view_proj_matrix);
   bool ShouldDrawWebVr();
   void DrawWebVr();
   bool WebVrPoseByteIsValid(int pose_index_byte);
 
   void UpdateController();
-  void HandleControllerInput(const gvr::Vec3f& forward_vector);
+  void HandleControllerInput(const gfx::Vector3dF& forward_vector);
   void HandleControllerAppButtonActivity(
-      const gvr::Vec3f& controller_direction);
+      const gfx::Vector3dF& controller_direction);
   void SendEventsToTarget(InputTarget input_target, int pixel_x, int pixel_y);
   void SendGesture(InputTarget input_target,
                    std::unique_ptr<blink::WebInputEvent> event);
@@ -186,19 +189,19 @@
   std::unique_ptr<MailboxToSurfaceBridge> mailbox_bridge_;
 
   // Current sizes for the render buffers.
-  gvr::Sizei render_size_primary_;
-  gvr::Sizei render_size_headlocked_;
+  gfx::Size render_size_primary_;
+  gfx::Size render_size_headlocked_;
 
   // Intended render_size_primary_ for use by VrShell, so that it
   // can be restored after exiting WebVR mode.
-  gvr::Sizei render_size_vrshell_;
+  gfx::Size render_size_vrshell_;
 
   std::unique_ptr<VrShellRenderer> vr_shell_renderer_;
 
   bool touch_pending_ = false;
-  gvr::Quatf controller_quat_;
+  vr::Quatf controller_quat_;
 
-  gvr::Vec3f target_point_;
+  gfx::Point3F target_point_;
   const ContentRectangle* target_element_ = nullptr;
   InputTarget current_input_target_ = InputTarget::NONE;
   InputTarget current_scroll_target = InputTarget::NONE;
@@ -206,11 +209,11 @@
   int ui_tex_css_height_ = 0;
   int content_tex_css_width_ = 0;
   int content_tex_css_height_ = 0;
-  gvr::Sizei content_tex_physical_size_ = {0, 0};
-  gvr::Sizei webvr_surface_size_ = {0, 0};
-  gvr::Sizei ui_tex_physical_size_ = {0, 0};
+  gfx::Size content_tex_physical_size_ = {0, 0};
+  gfx::Size webvr_surface_size_ = {0, 0};
+  gfx::Size ui_tex_physical_size_ = {0, 0};
 
-  std::vector<gvr::Mat4f> webvr_head_pose_;
+  std::vector<vr::Mat4f> webvr_head_pose_;
   bool web_vr_mode_;
   bool ready_to_draw_ = false;
   bool surfaceless_rendering_;
@@ -237,7 +240,7 @@
   uint16_t last_frame_index_ = -1;
 
   // Attributes for gesture detection while holding app button.
-  gvr::Vec3f controller_start_direction_;
+  gfx::Vector3dF controller_start_direction_;
 
   base::WeakPtrFactory<VrShellGl> weak_ptr_factory_;
 
diff --git a/chrome/browser/android/vr_shell/vr_shell_renderer.cc b/chrome/browser/android/vr_shell/vr_shell_renderer.cc
index 0898d6c..7951074d 100644
--- a/chrome/browser/android/vr_shell/vr_shell_renderer.cc
+++ b/chrome/browser/android/vr_shell/vr_shell_renderer.cc
@@ -267,7 +267,7 @@
 BaseQuadRenderer::~BaseQuadRenderer() = default;
 
 void BaseQuadRenderer::PrepareToDraw(GLuint view_proj_matrix_handle,
-                                     const gvr::Mat4f& view_proj_matrix) {
+                                     const vr::Mat4f& view_proj_matrix) {
   glUseProgram(program_handle_);
 
   // Pass in model view project matrix.
@@ -311,13 +311,14 @@
 }
 
 void TexturedQuadRenderer::AddQuad(int texture_data_handle,
-                                   const gvr::Mat4f& view_proj_matrix,
-                                   const Rectf& copy_rect,
+                                   const vr::Mat4f& view_proj_matrix,
+                                   const gfx::RectF& copy_rect,
                                    float opacity) {
   TexturedQuad quad;
   quad.texture_data_handle = texture_data_handle;
   quad.view_proj_matrix = view_proj_matrix;
-  quad.copy_rect = copy_rect;
+  quad.copy_rect = {copy_rect.x(), copy_rect.y(), copy_rect.width(),
+                    copy_rect.height()};
   quad.opacity = opacity;
   quad_queue_.push(quad);
 }
@@ -461,7 +462,7 @@
       glGetUniformLocation(program_handle_, "mid_ring_opacity");
 }
 
-void ReticleRenderer::Draw(const gvr::Mat4f& view_proj_matrix) {
+void ReticleRenderer::Draw(const vr::Mat4f& view_proj_matrix) {
   PrepareToDraw(model_view_proj_matrix_handle_, view_proj_matrix);
 
   glUniform4f(color_handle_, kReticleColor[0], kReticleColor[1],
@@ -502,7 +503,7 @@
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 }
 
-void LaserRenderer::Draw(const gvr::Mat4f& view_proj_matrix) {
+void LaserRenderer::Draw(const vr::Mat4f& view_proj_matrix) {
   PrepareToDraw(model_view_proj_matrix_handle_, view_proj_matrix);
 
   // Link texture data with texture unit.
@@ -577,7 +578,7 @@
 }
 
 void ControllerRenderer::Draw(VrControllerModel::State state,
-                              const gvr::Mat4f& view_proj_matrix) {
+                              const vr::Mat4f& view_proj_matrix) {
   glUseProgram(program_handle_);
 
   glUniformMatrix4fv(model_view_proj_matrix_handle_, 1, false,
@@ -617,9 +618,9 @@
   opacity_handle_ = glGetUniformLocation(program_handle_, "u_Opacity");
 }
 
-void GradientQuadRenderer::Draw(const gvr::Mat4f& view_proj_matrix,
-                                const Colorf& edge_color,
-                                const Colorf& center_color,
+void GradientQuadRenderer::Draw(const vr::Mat4f& view_proj_matrix,
+                                const vr::Colorf& edge_color,
+                                const vr::Colorf& center_color,
                                 float opacity) {
   PrepareToDraw(model_view_proj_matrix_handle_, view_proj_matrix);
 
@@ -651,9 +652,9 @@
   opacity_handle_ = glGetUniformLocation(program_handle_, "u_Opacity");
 }
 
-void GradientGridRenderer::Draw(const gvr::Mat4f& view_proj_matrix,
-                                const Colorf& edge_color,
-                                const Colorf& center_color,
+void GradientGridRenderer::Draw(const vr::Mat4f& view_proj_matrix,
+                                const vr::Colorf& edge_color,
+                                const vr::Colorf& center_color,
                                 int gridline_count,
                                 float opacity) {
   // In case the tile number changed we have to regenerate the grid lines.
diff --git a/chrome/browser/android/vr_shell/vr_shell_renderer.h b/chrome/browser/android/vr_shell/vr_shell_renderer.h
index 81811d3b..9871479 100644
--- a/chrome/browser/android/vr_shell/vr_shell_renderer.h
+++ b/chrome/browser/android/vr_shell/vr_shell_renderer.h
@@ -11,8 +11,7 @@
 
 #include "base/macros.h"
 #include "chrome/browser/android/vr_shell/vr_controller_model.h"
-#include "chrome/browser/android/vr_shell/vr_math.h"
-#include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
+#include "device/vr/vr_types.h"
 #include "ui/gl/gl_bindings.h"
 
 namespace vr_shell {
@@ -47,6 +46,13 @@
   float z;
 };
 
+struct RectF {
+  float x;
+  float y;
+  float width;
+  float height;
+};
+
 struct Line3d {
   Vertex3d start;
   Vertex3d end;
@@ -54,8 +60,8 @@
 
 struct TexturedQuad {
   int texture_data_handle;
-  gvr::Mat4f view_proj_matrix;
-  Rectf copy_rect;
+  vr::Mat4f view_proj_matrix;
+  RectF copy_rect;
   float opacity;
 };
 
@@ -82,7 +88,7 @@
 
  protected:
   void PrepareToDraw(GLuint view_proj_matrix_handle,
-                     const gvr::Mat4f& view_proj_matrix);
+                     const vr::Mat4f& view_proj_matrix);
 
   static GLuint vertex_buffer_;
 
@@ -96,8 +102,8 @@
 
   // Draw the content rect in the texture quad.
   void AddQuad(int texture_data_handle,
-               const gvr::Mat4f& view_proj_matrix,
-               const Rectf& copy_rect,
+               const vr::Mat4f& view_proj_matrix,
+               const gfx::RectF& copy_rect,
                float opacity);
 
   void Flush();
@@ -132,7 +138,7 @@
   ReticleRenderer();
   ~ReticleRenderer() override;
 
-  void Draw(const gvr::Mat4f& view_proj_matrix);
+  void Draw(const vr::Mat4f& view_proj_matrix);
 
  private:
   GLuint model_view_proj_matrix_handle_;
@@ -152,7 +158,7 @@
   LaserRenderer();
   ~LaserRenderer() override;
 
-  void Draw(const gvr::Mat4f& view_proj_matrix);
+  void Draw(const vr::Mat4f& view_proj_matrix);
 
  private:
   GLuint model_view_proj_matrix_handle_;
@@ -171,7 +177,7 @@
   ~ControllerRenderer() override;
 
   void SetUp(std::unique_ptr<VrControllerModel> model);
-  void Draw(VrControllerModel::State state, const gvr::Mat4f& view_proj_matrix);
+  void Draw(VrControllerModel::State state, const vr::Mat4f& view_proj_matrix);
   bool IsSetUp() const { return setup_; }
 
  private:
@@ -202,9 +208,9 @@
   GradientQuadRenderer();
   ~GradientQuadRenderer() override;
 
-  void Draw(const gvr::Mat4f& view_proj_matrix,
-            const Colorf& edge_color,
-            const Colorf& center_color,
+  void Draw(const vr::Mat4f& view_proj_matrix,
+            const vr::Colorf& edge_color,
+            const vr::Colorf& center_color,
             float opacity);
 
  private:
@@ -222,9 +228,9 @@
   GradientGridRenderer();
   ~GradientGridRenderer() override;
 
-  void Draw(const gvr::Mat4f& view_proj_matrix,
-            const Colorf& edge_color,
-            const Colorf& center_color,
+  void Draw(const vr::Mat4f& view_proj_matrix,
+            const vr::Colorf& edge_color,
+            const vr::Colorf& center_color,
             int gridline_count,
             float opacity);
 
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 7b32099..23b1f50 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -453,7 +453,7 @@
       <include name="IDR_PROFILER_JS" file="resources\profiler\profiler.js" flattenhtml="true" type="BINDATA" compress="gzip" />
       <include name="IDR_SITE_ENGAGEMENT_HTML" file="resources\engagement\site_engagement.html" flattenhtml="true" type="BINDATA" compress="gzip" />
       <include name="IDR_SITE_ENGAGEMENT_JS" file="resources\engagement\site_engagement.js" flattenhtml="true" type="BINDATA" compress="gzip" />
-      <include name="IDR_SITE_ENGAGEMENT_MOJO_JS" file="${root_gen_dir}\chrome\browser\engagement\site_engagement.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" />
+      <include name="IDR_SITE_ENGAGEMENT_MOJO_JS" file="${root_gen_dir}\chrome\browser\engagement\site_engagement_details.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" />
       <include name="IDR_URL_MOJO_JS" file="${root_gen_dir}\url\mojo\url.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" />
       <include name="IDR_SYNC_CONFIRMATION_CSS" file="resources\signin\sync_confirmation\sync_confirmation.css" type="BINDATA" />
       <include name="IDR_SYNC_CONFIRMATION_HTML" file="resources\signin\sync_confirmation\sync_confirmation.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
diff --git a/chrome/browser/chrome_content_browser_manifest_overlay.json b/chrome/browser/chrome_content_browser_manifest_overlay.json
index 90322a9..8ff6293 100644
--- a/chrome/browser/chrome_content_browser_manifest_overlay.json
+++ b/chrome/browser/chrome_content_browser_manifest_overlay.json
@@ -55,7 +55,7 @@
           // TODO(beng): These should be moved to a separate capability.
           "mojom::OmniboxPageHandler",
           "mojom::PluginsPageHandler",
-          "mojom::SiteEngagementUIHandler",
+          "mojom::SiteEngagementDetailsProvider",
           "mojom::UsbInternalsPageHandler"
         ]
       }
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index e549841b..1d0e4a4 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1228,12 +1228,11 @@
     "printing/cups_print_job_notification.h",
     "printing/cups_print_job_notification_manager.cc",
     "printing/cups_print_job_notification_manager.h",
-    "printing/fake_printer_discoverer.cc",
-    "printing/fake_printer_discoverer.h",
     "printing/ppd_provider_factory.cc",
     "printing/ppd_provider_factory.h",
     "printing/printer_configurer.cc",
     "printing/printer_configurer.h",
+    "printing/printer_discoverer.cc",
     "printing/printer_discoverer.h",
     "printing/printers_manager.cc",
     "printing/printers_manager.h",
diff --git a/chrome/browser/chromeos/extensions/first_run_private_api.cc b/chrome/browser/chromeos/extensions/first_run_private_api.cc
index 55e45fb0..53bf546e 100644
--- a/chrome/browser/chromeos/extensions/first_run_private_api.cc
+++ b/chrome/browser/chromeos/extensions/first_run_private_api.cc
@@ -53,6 +53,9 @@
   localized_strings->SetString(
       "closeButton",
       l10n_util::GetStringUTF16(IDS_CLOSE));
+  localized_strings->SetString(
+      "accessibleTitle",
+      l10n_util::GetStringUTF16(IDS_FIRST_RUN_ACCESSIBLE_TITLE));
 
   const std::string& app_locale = g_browser_process->GetApplicationLocale();
   webui::SetLoadTimeDataDefaults(app_locale, localized_strings.get());
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index 68980b7..7f3715c 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -29,7 +29,6 @@
 #include "chrome/browser/browser_process_platform_part_chromeos.h"
 #include "chrome/browser/browser_shutdown.h"
 #include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/chromeos/arc/arc_service_launcher.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/base/locale_util.h"
@@ -139,12 +138,6 @@
 // for, waiting for a session restore to finish.
 static const int kMaxRestartDelaySeconds = 10;
 
-// ChromeVox tutorial URL (used in place of "getting started" url when
-// accessibility is enabled).
-const char kChromeVoxTutorialURLPattern[] =
-    "chrome-extension://mndnfokpggljbaajbnioimlmbfngpief/"
-    "cvox2/background/panel.html?tutorial";
-
 void InitLocaleAndInputMethodsForNewUser(
     UserSessionManager* session_manager,
     Profile* profile,
@@ -1313,15 +1306,6 @@
   bool can_show_getstarted_guide = user_manager->GetActiveUser()->GetType() ==
                                    user_manager::USER_TYPE_REGULAR;
 
-  // Skip the default first-run behavior for public accounts.
-  if (!user_manager->IsLoggedInAsPublicAccount()) {
-    if (AccessibilityManager::Get()->IsSpokenFeedbackEnabled()) {
-      const char* url = kChromeVoxTutorialURLPattern;
-      start_urls.push_back(url);
-      can_show_getstarted_guide = false;
-    }
-  }
-
   // Only show getting started guide for a new user.
   const bool should_show_getstarted_guide = user_manager->IsCurrentUserNew();
 
diff --git a/chrome/browser/chromeos/printer_detector/cups_printer_detector.cc b/chrome/browser/chromeos/printer_detector/cups_printer_detector.cc
index 05369a7..a8b1e8f 100644
--- a/chrome/browser/chromeos/printer_detector/cups_printer_detector.cc
+++ b/chrome/browser/chromeos/printer_detector/cups_printer_detector.cc
@@ -12,8 +12,11 @@
 #include "base/bind_helpers.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/observer_list_threadsafe.h"
 #include "base/scoped_observer.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/printer_detector/printer_detector.h"
 #include "chrome/browser/chromeos/printing/ppd_provider_factory.h"
@@ -72,18 +75,48 @@
                                 public device::UsbService::Observer {
  public:
   explicit CupsPrinterDetectorImpl(Profile* profile)
-      : profile_(profile), observer_(this), weak_ptr_factory_(this) {
+      : profile_(profile),
+        usb_observer_(this),
+        observer_list_(
+            new base::ObserverListThreadSafe<PrinterDetector::Observer>),
+        weak_ptr_factory_(this) {
     device::UsbService* usb_service =
         device::DeviceClient::Get()->GetUsbService();
     if (usb_service) {
-      observer_.Add(usb_service);
+      usb_observer_.Add(usb_service);
       usb_service->GetDevices(base::Bind(&CupsPrinterDetectorImpl::OnGetDevices,
                                          weak_ptr_factory_.GetWeakPtr()));
     }
   }
   ~CupsPrinterDetectorImpl() override = default;
 
+  // PrinterDetector interface function.
+  void AddObserver(PrinterDetector::Observer* observer) override {
+    observer_list_->AddObserver(observer);
+  }
+
+  // PrinterDetector interface function.
+  void RemoveObserver(PrinterDetector::Observer* observer) override {
+    observer_list_->RemoveObserver(observer);
+  }
+
+  // PrinterDetector interface function.
+  std::vector<Printer> GetPrinters() override {
+    base::AutoLock auto_lock(pp_lock_);
+    return GetPrintersLocked();
+  }
+
  private:
+  std::vector<Printer> GetPrintersLocked() {
+    pp_lock_.AssertAcquired();
+    std::vector<Printer> printers;
+    printers.reserve(present_printers_.size());
+    for (const auto& entry : present_printers_) {
+      printers.push_back(*entry.second);
+    }
+    return printers;
+  }
+
   // Callback for initial enumeration of usb devices.
   void OnGetDevices(
       const std::vector<scoped_refptr<device::UsbDevice>>& devices) {
@@ -105,24 +138,21 @@
     if (!UsbDeviceIsPrinter(*device)) {
       return;
     }
-    known_printers_.erase(device->guid());
-    // TODO(justincarlson):  Update failed printers.
-  }
 
-  // Returns the existing printer using this URI, if one exists, or
-  // null otherwise.
-  std::unique_ptr<Printer> FindExistingPrinter(const std::string& uri) {
-    // TODO(justincarlson): add a GetPrinterByURI to PrintersManager.
-    // https://crbug.com/700602
-    auto existing_printers =
-        PrintersManagerFactory::GetForBrowserContext(profile_)->GetPrinters();
-    for (std::unique_ptr<Printer>& printer : existing_printers) {
-      if (printer->uri() == uri) {
-        // Found a match, so use the existing configuration.
-        return std::move(printer);
-      }
+    base::AutoLock auto_lock(pp_lock_);
+    if (base::ContainsKey(present_printers_, device->guid())) {
+      present_printers_.erase(device->guid());
+      auto printers = GetPrintersLocked();
+      // We already have pp_lock_, so need to call the pre-locked version of
+      // GetPrinters to prevent deadlock.
+      observer_list_->Notify(
+          FROM_HERE, &PrinterDetector::Observer::OnAvailableUsbPrintersChanged,
+          GetPrintersLocked());
+    } else {
+      // If the device has been removed but it's not in present_printers_, it
+      // must still be in the setup flow.
+      deferred_printer_removals_.insert(device->guid());
     }
-    return nullptr;
   }
 
   // If this device is a printer and we haven't already tried to set it up,
@@ -132,20 +162,32 @@
                         bool hotplugged) {
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-    if (!UsbDeviceIsPrinter(*device) ||
-        base::ContainsKey(known_printers_, device->guid())) {
+    if (!UsbDeviceIsPrinter(*device)) {
       return;
     }
-    known_printers_.insert(device->guid());
 
+    // If we got this far, we want to try to auto-configure this printer.
     auto data = base::MakeUnique<SetUpPrinterData>();
     data->configurer = PrinterConfigurer::Create(profile_);
     data->device = device;
     data->hotplugged = hotplugged;
 
-    data->printer = FindExistingPrinter(UsbPrinterUri(*device));
-    if (data->printer != nullptr) {
+    data->printer = UsbDeviceToPrinter(*device);
+    if (data->printer == nullptr) {
+      // We've failed to understand this printer device, an error will already
+      // have been logged, so just bail.
+      return;
+    }
+
+    // If the user already has a configuration for this device, substitute that
+    // one for the one we generated automatically and skip the parts where we
+    // try to automagically figure out the driver.
+    std::unique_ptr<Printer> existing_printer_configuration =
+        PrintersManagerFactory::GetForBrowserContext(profile_)->GetPrinter(
+            data->printer->id());
+    if (existing_printer_configuration != nullptr) {
       data->is_new = false;
+      data->printer = std::move(existing_printer_configuration);
       OnPrinterResolved(std::move(data));
       return;
     }
@@ -155,10 +197,6 @@
     // TODO(justincarlson): Add a notification that we are attempting to set up
     // this printer at this point.
     data->is_new = true;
-    data->printer = UsbDeviceToPrinter(*device);
-    if (data->printer == nullptr) {
-      return;
-    }
 
     // Look for an exact match based on USB ids.
     scoped_refptr<PpdProvider> ppd_provider =
@@ -214,18 +252,30 @@
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
     if (result == PrinterSetupResult::SUCCESS) {
       if (data->is_new) {
+        // We aren't done with data->printer yet, so we have to copy it instead
+        // of moving it.
+        auto printer_copy = base::MakeUnique<Printer>(*data->printer);
         PrintersManagerFactory::GetForBrowserContext(profile_)->RegisterPrinter(
-            std::move(data->printer));
+            std::move(printer_copy));
       }
       // TODO(justincarlson): If the device was hotplugged, pop a timed
       // notification that says the printer is now available for printing.
     }
-    // TODO(justincarlson): If this doesn't succeed, Pop a notification that
+    // TODO(justincarlson): If this doesn't succeed, pop a notification that
     // tells the user automatic setup failed and offers to open the CUPS printer
     // configuration settings.
-    //
-    // TODO(justincarlson): If this doesn't succeed, Update the list of printers
-    // that failed to set up.
+
+    if (base::ContainsKey(deferred_printer_removals_, data->device->guid())) {
+      // The device was removed before we finished the flow, so just don't add
+      // it to present_printers_;
+      deferred_printer_removals_.erase(data->device->guid());
+    } else {
+      base::AutoLock auto_lock(pp_lock_);
+      present_printers_.emplace(data->device->guid(), std::move(data->printer));
+      observer_list_->Notify(
+          FROM_HERE, &PrinterDetector::Observer::OnAvailableUsbPrintersChanged,
+          GetPrintersLocked());
+    }
   }
 
   void SetNotificationUIManagerForTesting(
@@ -233,19 +283,33 @@
     LOG(FATAL) << "Not implemented for CUPS";
   }
 
-  // USB GUIDs of printers we've already dealt with.  There's an inherent race
-  // between initially querying all usb devices and receiving a notification
-  // about a new device, so this set lets us guarantee that we handle a given
-  // printer exactly once.
-  std::set<std::string> known_printers_;
+  // Map from USB GUID to Printer that we have detected as being currently
+  // plugged in and have finished processing.  Note present_printers_ may be
+  // accessed from multiple threads, so is protected by pp_lock_.
+  std::map<std::string, std::unique_ptr<Printer>> present_printers_;
+  base::Lock pp_lock_;
+
+  // If the usb device is removed before we've finished processing it, we'll
+  // defer the cleanup until the setup flow finishes.  This is the set of
+  // guids which have been removed before the flow finished.
+  std::set<std::string> deferred_printer_removals_;
 
   Profile* profile_;
-  ScopedObserver<device::UsbService, device::UsbService::Observer> observer_;
+  ScopedObserver<device::UsbService, device::UsbService::Observer>
+      usb_observer_;
+  scoped_refptr<base::ObserverListThreadSafe<PrinterDetector::Observer>>
+      observer_list_;
   base::WeakPtrFactory<CupsPrinterDetectorImpl> weak_ptr_factory_;
 };
 
 }  // namespace
 
+// Nop base class implementation of GetPrinters().  Because this is non-empty we
+// have to define it out-of-line.
+std::vector<Printer> PrinterDetector::GetPrinters() {
+  return std::vector<Printer>();
+}
+
 // static
 std::unique_ptr<PrinterDetector> PrinterDetector::CreateCups(Profile* profile) {
   return base::MakeUnique<CupsPrinterDetectorImpl>(profile);
diff --git a/chrome/browser/chromeos/printer_detector/printer_detector.h b/chrome/browser/chromeos/printer_detector/printer_detector.h
index e7a5b3d5..1789377 100644
--- a/chrome/browser/chromeos/printer_detector/printer_detector.h
+++ b/chrome/browser/chromeos/printer_detector/printer_detector.h
@@ -7,8 +7,10 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/macros.h"
+#include "chromeos/printing/printer_configuration.h"
 #include "components/keyed_service/core/keyed_service.h"
 
 class NotificationUIManager;
@@ -34,6 +36,15 @@
 // CUPS backend.
 class PrinterDetector : public KeyedService {
  public:
+  class Observer {
+   public:
+    virtual ~Observer() = default;
+
+    // The set of available printers has changed.
+    virtual void OnAvailableUsbPrintersChanged(
+        const std::vector<Printer>& printers) = 0;
+  };
+
   // Factory function for the Legacy implementation.
   static std::unique_ptr<PrinterDetector> CreateLegacy(Profile* profile);
 
@@ -41,6 +52,16 @@
   static std::unique_ptr<PrinterDetector> CreateCups(Profile* profile);
   ~PrinterDetector() override {}
 
+  // Observer management.  Note these are only implemented for the cups backend.
+  // TODO(justincarlson) - Change these all to pure virtual functions when the
+  // legacy backend is retired.
+
+  virtual void AddObserver(Observer* observer) {}
+  virtual void RemoveObserver(Observer* observer) {}
+
+  // Get the current set of detected printers.
+  virtual std::vector<Printer> GetPrinters();
+
  protected:
   PrinterDetector() = default;
 
diff --git a/chrome/browser/chromeos/printing/fake_printer_discoverer.cc b/chrome/browser/chromeos/printing/fake_printer_discoverer.cc
deleted file mode 100644
index a3b57e9..0000000
--- a/chrome/browser/chromeos/printing/fake_printer_discoverer.cc
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/printing/fake_printer_discoverer.h"
-
-#include <algorithm>
-#include <iterator>
-#include <memory>
-
-#include "base/bind.h"
-#include "base/memory/ptr_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/threading/sequenced_task_runner_handle.h"
-#include "base/time/time.h"
-#include "chrome/browser/chromeos/printing/printer_discoverer.h"
-
-namespace chromeos {
-
-// static
-std::unique_ptr<PrinterDiscoverer> PrinterDiscoverer::Create() {
-  return base::MakeUnique<FakePrinterDiscoverer>();
-}
-
-FakePrinterDiscoverer::~FakePrinterDiscoverer() {}
-
-bool FakePrinterDiscoverer::StartDiscovery() {
-  discovery_running_ = true;
-
-  for (auto* observer : observers_)
-    observer->OnDiscoveryStarted();
-
-  base::SequencedTaskRunnerHandle::Get()->PostNonNestableDelayedTask(
-      FROM_HERE, base::Bind(&FakePrinterDiscoverer::EmitPrinters,
-                            weak_ptr_factory_.GetWeakPtr(), 0, 2),
-      base::TimeDelta::FromMilliseconds(2000));
-  return true;
-}
-
-bool FakePrinterDiscoverer::StopDiscovery() {
-  discovery_running_ = false;
-  for (auto* observer : observers_)
-    observer->OnDiscoveryStopping();
-
-  return true;
-}
-
-void FakePrinterDiscoverer::AddObserver(PrinterDiscoverer::Observer* observer) {
-  auto found = std::find(observers_.begin(), observers_.end(), observer);
-  if (found == observers_.end())
-    observers_.push_back(observer);
-}
-
-void FakePrinterDiscoverer::RemoveObserver(
-    PrinterDiscoverer::Observer* observer) {
-  auto found = std::find(observers_.begin(), observers_.end(), observer);
-  if (found != observers_.end())
-    observers_.erase(found);
-}
-
-FakePrinterDiscoverer::FakePrinterDiscoverer()
-    : discovery_running_(false), weak_ptr_factory_(this) {
-  for (int i = 0; i < 12; i++) {
-    // printer doesn't have a ppd
-    printers_.emplace_back(base::StringPrintf("GUID%2d", i));
-    printers_[i].set_display_name(base::StringPrintf("PrinterName%2d", i));
-    printers_[i].set_description(
-        base::StringPrintf("Printer%2dDescription", i));
-    printers_[i].set_manufacturer("Chromium");
-    printers_[i].set_model(i % 3 == 0 ? "Inkjet" : "Laser Maker");
-    printers_[i].set_uri(
-        base::StringPrintf("lpd://192.168.1.%d:9100/bldg/printer", i));
-    printers_[i].set_uuid(
-        base::StringPrintf("UUID-%4d-%4d-%4d-UUID", i * 3, i * 2, i));
-  }
-}
-
-void FakePrinterDiscoverer::EmitPrinters(size_t start, size_t end) {
-  if (!discovery_running_ || start >= printers_.size() || end < start)
-    return;
-
-  size_t clipped_start = std::max(static_cast<size_t>(0), start);
-  size_t clipped_end = std::min(printers_.size(), end);
-  if (!observers_.empty()) {
-    std::vector<Printer> subset(printers_.cbegin() + clipped_start,
-                                printers_.cbegin() + clipped_end);
-
-    for (auto* observer : observers_)
-      observer->OnPrintersFound(subset);
-  }
-
-  // schedule another emit if elements remain.
-  if (end < printers_.size()) {
-    base::SequencedTaskRunnerHandle::Get()->PostNonNestableDelayedTask(
-        FROM_HERE,
-        base::Bind(&FakePrinterDiscoverer::EmitPrinters,
-                   weak_ptr_factory_.GetWeakPtr(), clipped_end, end + end),
-        base::TimeDelta::FromMilliseconds(2000));
-  } else {
-    // We're done.  Notify observers.
-    discovery_running_ = false;
-    for (auto* observer : observers_)
-      observer->OnDiscoveryDone();
-  }
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/fake_printer_discoverer.h b/chrome/browser/chromeos/printing/fake_printer_discoverer.h
deleted file mode 100644
index d7f857e..0000000
--- a/chrome/browser/chromeos/printing/fake_printer_discoverer.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_PRINTING_FAKE_PRINTER_DISCOVERER_H_
-#define CHROME_BROWSER_CHROMEOS_PRINTING_FAKE_PRINTER_DISCOVERER_H_
-
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/chromeos/printing/printer_discoverer.h"
-#include "chromeos/printing/printer_configuration.h"
-
-namespace chromeos {
-
-class FakePrinterDiscoverer : public PrinterDiscoverer {
- public:
-  FakePrinterDiscoverer();
-  ~FakePrinterDiscoverer() override;
-
-  bool StartDiscovery() override;
-  bool StopDiscovery() override;
-  void AddObserver(PrinterDiscoverer::Observer* observer) override;
-  void RemoveObserver(PrinterDiscoverer::Observer* observer) override;
-
- private:
-  void EmitPrinters(size_t start, size_t end);
-
-  std::vector<chromeos::Printer> printers_;
-  bool discovery_running_;
-  std::vector<PrinterDiscoverer::Observer*> observers_;
-
-  base::WeakPtrFactory<FakePrinterDiscoverer> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(FakePrinterDiscoverer);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_PRINTING_FAKE_PRINTER_DISCOVERER_H_
diff --git a/chrome/browser/chromeos/printing/printer_discoverer.cc b/chrome/browser/chromeos/printing/printer_discoverer.cc
new file mode 100644
index 0000000..e6f9159
--- /dev/null
+++ b/chrome/browser/chromeos/printing/printer_discoverer.cc
@@ -0,0 +1,119 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/printing/printer_discoverer.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/scoped_observer.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "chrome/browser/chromeos/printer_detector/printer_detector.h"
+#include "chrome/browser/chromeos/printer_detector/printer_detector_factory.h"
+#include "chrome/browser/chromeos/printing/printers_manager.h"
+#include "chrome/browser/chromeos/printing/printers_manager_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chromeos/printing/printer_configuration.h"
+
+namespace chromeos {
+namespace {
+
+// Implementation of PrinterDiscoverer interface.
+class PrinterDiscovererImpl : public PrinterDiscoverer,
+                              public PrinterDetector::Observer {
+ public:
+  explicit PrinterDiscovererImpl(Profile* profile)
+      : detector_observer_(this), profile_(profile), weak_ptr_factory_(this) {
+    PrinterDetector* detector =
+        PrinterDetectorFactory::GetInstance()->Get(profile);
+    DCHECK(detector);
+    detector_observer_.Add(detector);
+    usb_printers_ = detector->GetPrinters();
+  }
+  ~PrinterDiscovererImpl() override = default;
+
+  // PrinterDiscoverer interface function.
+  void AddObserver(PrinterDiscoverer::Observer* observer) override {
+    observer_list_.AddObserver(observer);
+    // WrappedOnPrintersFound is a simple wrapper around
+    // Observer::OnPrintersFound which lets us safely do the initial
+    // OnPrintersFound call to the registered observer.  This wrapper buys us
+    // weak_ptr semantics on 'this', and also guards against removal of the
+    // observer from the Discoverer before this callback is issued.
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(&PrinterDiscovererImpl::WrappedOnPrintersFound,
+                              weak_ptr_factory_.GetWeakPtr(), observer,
+                              GetAvailablePrinters()));
+  }
+
+  // PrinterDiscoverer interface function.
+  void RemoveObserver(PrinterDiscoverer::Observer* observer) override {
+    observer_list_.RemoveObserver(observer);
+  }
+
+  // PrinterDetector::Observer interface function.
+  void OnAvailableUsbPrintersChanged(
+      const std::vector<Printer>& printers) override {
+    usb_printers_ = printers;
+    std::vector<Printer> all_printers = GetAvailablePrinters();
+    for (PrinterDiscoverer::Observer& observer : observer_list_) {
+      observer.OnPrintersFound(all_printers);
+    }
+  }
+
+ private:
+  // Wrapper for doing the initial OnPrintersFound call on an observer of this
+  // object.
+  void WrappedOnPrintersFound(PrinterDiscoverer::Observer* observer,
+                              const std::vector<Printer>& printers) {
+    if (observer_list_.HasObserver(observer)) {
+      observer->OnPrintersFound(printers);
+      // Since USB is the only thing we're worried about at the moment, and we
+      // don't have to wait for those printers to be scanned, we can just tell
+      // the observer the initial scan is done now.  This will change when we're
+      // also doing network discovery -- we'll hold off on issuing this callback
+      // until the network discovery is done as well.
+      observer->OnDiscoveryInitialScanDone();
+    }
+  }
+
+  // Get the current set of discovered printers that are not already known
+  // to the user's PrintersManager.
+  std::vector<Printer> GetAvailablePrinters() {
+    // Only know about usb printers for now.  Eventually we'll add discovered
+    // network printers as well.
+    std::vector<Printer> ret;
+    PrintersManager* printers_manager =
+        PrintersManagerFactory::GetForBrowserContext(profile_);
+    if (!printers_manager) {
+      LOG(WARNING) << "Failing to get available printers because no "
+                      "PrintersManager exists.";
+      return ret;
+    }
+
+    for (const Printer& printer : usb_printers_) {
+      if (printers_manager->GetPrinter(printer.id()).get() == nullptr) {
+        ret.push_back(printer);
+      }
+    }
+    return ret;
+  }
+
+  std::vector<Printer> usb_printers_;
+  base::ObserverList<PrinterDiscoverer::Observer> observer_list_;
+  ScopedObserver<PrinterDetector, PrinterDetector::Observer> detector_observer_;
+  Profile* profile_;
+  base::WeakPtrFactory<PrinterDiscovererImpl> weak_ptr_factory_;
+};
+
+}  // namespace
+
+// static
+std::unique_ptr<PrinterDiscoverer> PrinterDiscoverer::CreateForProfile(
+    Profile* profile) {
+  return base::MakeUnique<PrinterDiscovererImpl>(profile);
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/printer_discoverer.h b/chrome/browser/chromeos/printing/printer_discoverer.h
index b0a8639..20fd8bc 100644
--- a/chrome/browser/chromeos/printing/printer_discoverer.h
+++ b/chrome/browser/chromeos/printing/printer_discoverer.h
@@ -10,50 +10,45 @@
 
 #include "chromeos/printing/printer_configuration.h"
 
+class Profile;
+
 namespace chromeos {
 
 // Interface for printer discovery.  Constructs Printer objects from USB and
-// zeroconf (DNS-SD) printers.
+// zeroconf (DNS-SD) printers.  All functions in this class must be called
+// from a sequenced context.
 class CHROMEOS_EXPORT PrinterDiscoverer {
  public:
   // Interface for objects interested in detected printers.
   class Observer {
    public:
-    // Called after discovery has started.
-    virtual void OnDiscoveryStarted() {}
+    virtual ~Observer() = default;
 
-    // Called when discovery is stopping.  OnPrintersFound will not be
-    // called after this occurs.
-    virtual void OnDiscoveryStopping() {}
+    // Called when we are done with the initial scan for printers.  We may
+    // still call OnPrintersFound if the set of available printers
+    // changes, but the user can conclude that if a printer is currently
+    // available and not in the list, we're not still looking for it.
+    virtual void OnDiscoveryInitialScanDone() = 0;
 
-    // Called after all printers have been discovered and the observers
-    // notified.
-    virtual void OnDiscoveryDone() {}
-
-    // Called with a collection of printers as they are discovered.  Printer
-    // objects must be copied if they are retained outside of the scope of this
-    // function.
-    virtual void OnPrintersFound(const std::vector<Printer>& printers) {}
+    // Called with a collection of printers as they are discovered.  On each
+    // call |printers| is the full set of known printers; it is not
+    // incremental; printers may be added or removed.
+    //
+    // Observers will get an OnPrintersFound callback after registration
+    // with the existing list of printers (which may be empty) and will get
+    // additional calls whenever the set of printers changes.
+    virtual void OnPrintersFound(const std::vector<Printer>& printers) = 0;
   };
 
   // Static factory
-  static std::unique_ptr<PrinterDiscoverer> Create();
+  static std::unique_ptr<PrinterDiscoverer> CreateForProfile(Profile* profile);
 
-  virtual ~PrinterDiscoverer() {}
-
-  // Begin scanning for printers.  Found printers will be reported to the
-  // attached observer.
-  virtual bool StartDiscovery() = 0;
-
-  // Stop scanning for printers.  Returns false if scanning could not be stopped
-  // and new results will continue to be be sent to observers.  Returns true if
-  // scanning was stopped successfully.  No calls to the attached observer will
-  // be made after this returns if it returned true.
-  virtual bool StopDiscovery() = 0;
+  virtual ~PrinterDiscoverer() = default;
 
   // Add an observer that will be notified of discovered printers.  Ownership of
   // |observer| is not taken by the discoverer.  It is an error to add an
-  // oberserver more than once.
+  // observer more than once.  Calls to |observer| methods will take place on
+  // the thread PrinterDiscoverer was instantiated on.
   virtual void AddObserver(PrinterDiscoverer::Observer* observer) = 0;
 
   // Remove an observer of printer discovery.
diff --git a/chrome/browser/chromeos/printing/printers_manager.h b/chrome/browser/chromeos/printing/printers_manager.h
index 04eb0dda..f31bd6c7 100644
--- a/chrome/browser/chromeos/printing/printers_manager.h
+++ b/chrome/browser/chromeos/printing/printers_manager.h
@@ -51,7 +51,8 @@
   // Returns printers from enterprise policy.
   std::vector<std::unique_ptr<Printer>> GetRecommendedPrinters() const;
 
-  // Returns the printer with id |printer_id|.
+  // Returns the printer with id |printer_id|, or nullptr if no such
+  // printer exists.
   std::unique_ptr<Printer> GetPrinter(const std::string& printer_id) const;
 
   // Adds or updates a printer. Printers are identified by the id field.  Use an
diff --git a/chrome/browser/engagement/BUILD.gn b/chrome/browser/engagement/BUILD.gn
index d0da319..4315a90 100644
--- a/chrome/browser/engagement/BUILD.gn
+++ b/chrome/browser/engagement/BUILD.gn
@@ -6,7 +6,7 @@
 
 mojom("mojo_bindings") {
   sources = [
-    "site_engagement.mojom",
+    "site_engagement_details.mojom",
   ]
 
   public_deps = [
diff --git a/chrome/browser/engagement/site_engagement.mojom b/chrome/browser/engagement/site_engagement.mojom
deleted file mode 100644
index a1ccb2ef..0000000
--- a/chrome/browser/engagement/site_engagement.mojom
+++ /dev/null
@@ -1,17 +0,0 @@
-// 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.
-
-module mojom;
-
-import "url/mojo/url.mojom";
-
-struct SiteEngagementInfo {
-  url.mojom.Url origin;
-  double score;
-};
-
-interface SiteEngagementUIHandler {
-  GetSiteEngagementInfo() => (array<SiteEngagementInfo> info);
-  SetSiteEngagementScoreForOrigin(url.mojom.Url origin, double score);
-};
diff --git a/chrome/browser/engagement/site_engagement_details.mojom b/chrome/browser/engagement/site_engagement_details.mojom
new file mode 100644
index 0000000..cf49499
--- /dev/null
+++ b/chrome/browser/engagement/site_engagement_details.mojom
@@ -0,0 +1,23 @@
+// 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.
+
+module mojom;
+
+import "url/mojo/url.mojom";
+
+struct SiteEngagementDetails {
+  url.mojom.Url origin;
+  double total_score;
+
+  // Details of the components which make up |score|. Note that these may
+  // sum to a value greater than |score| if it exceeds the maximum.
+  double base_score;
+  double installed_bonus;
+  double notifications_bonus;
+};
+
+interface SiteEngagementDetailsProvider {
+  GetSiteEngagementDetails() => (array<SiteEngagementDetails> info);
+  SetSiteEngagementBaseScoreForUrl(url.mojom.Url url, double score);
+};
diff --git a/chrome/browser/engagement/site_engagement_score.cc b/chrome/browser/engagement/site_engagement_score.cc
index 10fe452..cfedbf3 100644
--- a/chrome/browser/engagement/site_engagement_score.cc
+++ b/chrome/browser/engagement/site_engagement_score.cc
@@ -46,7 +46,7 @@
   std::unique_ptr<base::DictionaryValue> value =
       base::DictionaryValue::From(settings->GetWebsiteSetting(
           origin_url, origin_url, CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT,
-          std::string(), NULL));
+          content_settings::ResourceIdentifier(), NULL));
 
   if (value.get())
     return value;
@@ -270,8 +270,20 @@
   last_engagement_time_ = now;
 }
 
-double SiteEngagementScore::GetScore() const {
-  return std::min(DecayedScore() + BonusScore(), kMaxPoints);
+double SiteEngagementScore::GetTotalScore() const {
+  return std::min(
+      DecayedScore() + BonusIfShortcutLaunched() + BonusIfHasNotifications(),
+      kMaxPoints);
+}
+
+mojom::SiteEngagementDetails SiteEngagementScore::GetDetails() const {
+  mojom::SiteEngagementDetails engagement;
+  engagement.origin = origin_;
+  engagement.base_score = DecayedScore();
+  engagement.installed_bonus = BonusIfShortcutLaunched();
+  engagement.notifications_bonus = BonusIfHasNotifications();
+  engagement.total_score = GetTotalScore();
+  return engagement;
 }
 
 void SiteEngagementScore::Commit() {
@@ -280,14 +292,14 @@
     return;
 
   settings_map_->SetWebsiteSettingDefaultScope(
-      origin_, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(),
-      std::move(score_dict_));
+      origin_, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT,
+      content_settings::ResourceIdentifier(), std::move(score_dict_));
 }
 
 blink::mojom::EngagementLevel SiteEngagementScore::GetEngagementLevel() const {
   DCHECK_LT(GetMediumEngagementBoundary(), GetHighEngagementBoundary());
 
-  double score = GetScore();
+  double score = GetTotalScore();
   if (score == 0)
     return blink::mojom::EngagementLevel::NONE;
 
@@ -400,20 +412,23 @@
                            periods * GetDecayPoints());
 }
 
-double SiteEngagementScore::BonusScore() const {
-  double bonus = 0;
+double SiteEngagementScore::BonusIfShortcutLaunched() const {
   int days_since_shortcut_launch =
       (clock_->Now() - last_shortcut_launch_time_).InDays();
   if (days_since_shortcut_launch <= kMaxDaysSinceShortcutLaunch)
-    bonus += GetWebAppInstalledPoints();
+    return GetWebAppInstalledPoints();
+  return 0;
+}
 
+double SiteEngagementScore::BonusIfHasNotifications() const {
   // TODO(dominickn, raymes): call PermissionManager::GetPermissionStatus when
   // the PermissionManager is thread-safe.
-  if (settings_map_ && settings_map_->GetContentSetting(
-                           origin_, GURL(), CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
-                           std::string()) == CONTENT_SETTING_ALLOW) {
-    bonus += GetNotificationPermissionPoints();
+  if (settings_map_ &&
+      settings_map_->GetContentSetting(
+          origin_, GURL(), CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+          content_settings::ResourceIdentifier()) == CONTENT_SETTING_ALLOW) {
+    return GetNotificationPermissionPoints();
   }
 
-  return bonus;
+  return 0;
 }
diff --git a/chrome/browser/engagement/site_engagement_score.h b/chrome/browser/engagement/site_engagement_score.h
index 358f97d..517520a 100644
--- a/chrome/browser/engagement/site_engagement_score.h
+++ b/chrome/browser/engagement/site_engagement_score.h
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "base/time/time.h"
 #include "base/values.h"
+#include "chrome/browser/engagement/site_engagement_details.mojom.h"
 #include "third_party/WebKit/public/platform/site_engagement.mojom.h"
 #include "url/gurl.h"
 
@@ -137,7 +138,15 @@
   // possible score. Decays the score if it has not been updated recently
   // enough.
   void AddPoints(double points);
-  double GetScore() const;
+
+  // Returns the total score, taking into account the base, bonus and maximum
+  // values.
+  double GetTotalScore() const;
+
+  // Returns a structure containing the origin URL and score, and details
+  // of the base and bonus scores. Note that the |score| is limited to
+  // kMaxPoints, while the detailed scores are returned raw.
+  mojom::SiteEngagementDetails GetDetails() const;
 
   // Writes the values in this score into |settings_map_|.
   void Commit();
@@ -196,8 +205,11 @@
   // Determine the score, accounting for any decay.
   double DecayedScore() const;
 
-  // Determine any score bonus from having installed shortcuts.
-  double BonusScore() const;
+  // Determine bonus from being installed, and having been launched recently..
+  double BonusIfShortcutLaunched() const;
+
+  // Determine bonus from having been granted notifications permission.
+  double BonusIfHasNotifications() const;
 
   // Updates the content settings dictionary |score_dict| with the current score
   // fields. Returns true if |score_dict| changed, otherwise return false.
diff --git a/chrome/browser/engagement/site_engagement_score_unittest.cc b/chrome/browser/engagement/site_engagement_score_unittest.cc
index 51e3225..96268a2 100644
--- a/chrome/browser/engagement/site_engagement_score_unittest.cc
+++ b/chrome/browser/engagement/site_engagement_score_unittest.cc
@@ -9,7 +9,12 @@
 #include "base/macros.h"
 #include "base/test/simple_test_clock.h"
 #include "base/values.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/engagement/site_engagement_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -42,12 +47,12 @@
 
 }  // namespace
 
-class SiteEngagementScoreTest : public testing::Test {
+class SiteEngagementScoreTest : public ChromeRenderViewHostTestHarness {
  public:
   SiteEngagementScoreTest() : score_(&test_clock_, GURL(), nullptr) {}
 
   void SetUp() override {
-    testing::Test::SetUp();
+    ChromeRenderViewHostTestHarness::SetUp();
     // Disable the first engagement bonus for tests.
     SiteEngagementScore::SetParamValuesForTesting();
   }
@@ -114,10 +119,10 @@
     score_.AddPoints(SiteEngagementScore::GetNavigationPoints());
     EXPECT_EQ(std::min(SiteEngagementScore::GetMaxPointsPerDay(),
                        (i + 1) * SiteEngagementScore::GetNavigationPoints()),
-              score_.GetScore());
+              score_.GetTotalScore());
   }
 
-  EXPECT_EQ(SiteEngagementScore::GetMaxPointsPerDay(), score_.GetScore());
+  EXPECT_EQ(SiteEngagementScore::GetMaxPointsPerDay(), score_.GetTotalScore());
 }
 
 // Accumulate on the first day to max that day's engagement, then accumulate on
@@ -130,7 +135,7 @@
   for (int i = 0; i < kMoreAccumulationsThanNeededToMaxDailyEngagement; ++i)
     score_.AddPoints(SiteEngagementScore::GetNavigationPoints());
 
-  EXPECT_EQ(SiteEngagementScore::GetMaxPointsPerDay(), score_.GetScore());
+  EXPECT_EQ(SiteEngagementScore::GetMaxPointsPerDay(), score_.GetTotalScore());
 
   test_clock_.SetNow(later_date);
   for (int i = 0; i < kMoreAccumulationsThanNeededToMaxDailyEngagement; ++i) {
@@ -139,10 +144,11 @@
         std::min(SiteEngagementScore::GetMaxPointsPerDay(),
                  (i + 1) * SiteEngagementScore::GetNavigationPoints());
     EXPECT_EQ(day_score + SiteEngagementScore::GetMaxPointsPerDay(),
-              score_.GetScore());
+              score_.GetTotalScore());
   }
 
-  EXPECT_EQ(2 * SiteEngagementScore::GetMaxPointsPerDay(), score_.GetScore());
+  EXPECT_EQ(2 * SiteEngagementScore::GetMaxPointsPerDay(),
+            score_.GetTotalScore());
 }
 
 // Accumulate score on many consecutive days and ensure the score doesn't exceed
@@ -158,10 +164,10 @@
 
     EXPECT_EQ(std::min(SiteEngagementScore::kMaxPoints,
                        (i + 1) * SiteEngagementScore::GetMaxPointsPerDay()),
-              score_.GetScore());
+              score_.GetTotalScore());
   }
 
-  EXPECT_EQ(SiteEngagementScore::kMaxPoints, score_.GetScore());
+  EXPECT_EQ(SiteEngagementScore::kMaxPoints, score_.GetTotalScore());
 }
 
 // Accumulate a little on many consecutive days and ensure the score doesn't
@@ -180,10 +186,10 @@
         std::min(SiteEngagementScore::kMaxPoints,
                  (i + 1) * kLessAccumulationsThanNeededToMaxDailyEngagement *
                      SiteEngagementScore::GetNavigationPoints()),
-        score_.GetScore());
+        score_.GetTotalScore());
   }
 
-  EXPECT_EQ(SiteEngagementScore::kMaxPoints, score_.GetScore());
+  EXPECT_EQ(SiteEngagementScore::kMaxPoints, score_.GetTotalScore());
 }
 
 // Accumulate a bit, then check the score decays properly for a range of times.
@@ -199,14 +205,14 @@
       score_.AddPoints(SiteEngagementScore::GetNavigationPoints());
   }
 
-  EXPECT_EQ(SiteEngagementScore::kMaxPoints, score_.GetScore());
+  EXPECT_EQ(SiteEngagementScore::kMaxPoints, score_.GetTotalScore());
 
   // The score should not have decayed before the first decay period has
   // elapsed.
   test_clock_.SetNow(current_day +
                      base::TimeDelta::FromHours(
                          SiteEngagementScore::GetDecayPeriodInHours() - 1));
-  EXPECT_EQ(SiteEngagementScore::kMaxPoints, score_.GetScore());
+  EXPECT_EQ(SiteEngagementScore::kMaxPoints, score_.GetTotalScore());
 
   // The score should have decayed by one chunk after one decay period has
   // elapsed.
@@ -215,7 +221,7 @@
       base::TimeDelta::FromHours(SiteEngagementScore::GetDecayPeriodInHours()));
   EXPECT_EQ(
       SiteEngagementScore::kMaxPoints - SiteEngagementScore::GetDecayPoints(),
-      score_.GetScore());
+      score_.GetTotalScore());
 
   // The score should have decayed by the right number of chunks after a few
   // decay periods have elapsed.
@@ -226,14 +232,14 @@
   EXPECT_EQ(SiteEngagementScore::kMaxPoints -
                 kLessPeriodsThanNeededToDecayMaxScore *
                     SiteEngagementScore::GetDecayPoints(),
-            score_.GetScore());
+            score_.GetTotalScore());
 
   // The score should not decay below zero.
   test_clock_.SetNow(
       current_day +
       base::TimeDelta::FromHours(kMorePeriodsThanNeededToDecayMaxScore *
                                  SiteEngagementScore::GetDecayPeriodInHours()));
-  EXPECT_EQ(0, score_.GetScore());
+  EXPECT_EQ(0, score_.GetTotalScore());
 }
 
 // Test that any expected decays are applied before adding points.
@@ -251,7 +257,7 @@
 
   double initial_score = kLessDaysThanNeededToMaxTotalEngagement *
                          SiteEngagementScore::GetMaxPointsPerDay();
-  EXPECT_EQ(initial_score, score_.GetScore());
+  EXPECT_EQ(initial_score, score_.GetTotalScore());
 
   // Go forward a few decay periods.
   test_clock_.SetNow(
@@ -262,12 +268,12 @@
   double decayed_score = initial_score -
                          kLessPeriodsThanNeededToDecayMaxScore *
                              SiteEngagementScore::GetDecayPoints();
-  EXPECT_EQ(decayed_score, score_.GetScore());
+  EXPECT_EQ(decayed_score, score_.GetTotalScore());
 
   // Now add some points.
   score_.AddPoints(SiteEngagementScore::GetNavigationPoints());
   EXPECT_EQ(decayed_score + SiteEngagementScore::GetNavigationPoints(),
-            score_.GetScore());
+            score_.GetTotalScore());
 }
 
 // Test that going back in time is handled properly.
@@ -278,7 +284,7 @@
   for (int i = 0; i < kMoreAccumulationsThanNeededToMaxDailyEngagement; ++i)
     score_.AddPoints(SiteEngagementScore::GetNavigationPoints());
 
-  EXPECT_EQ(SiteEngagementScore::GetMaxPointsPerDay(), score_.GetScore());
+  EXPECT_EQ(SiteEngagementScore::GetMaxPointsPerDay(), score_.GetTotalScore());
 
   // Adding to the score on an earlier date should be treated like another day,
   // and should not cause any decay.
@@ -291,10 +297,11 @@
         std::min(SiteEngagementScore::GetMaxPointsPerDay(),
                  (i + 1) * SiteEngagementScore::GetNavigationPoints());
     EXPECT_EQ(day_score + SiteEngagementScore::GetMaxPointsPerDay(),
-              score_.GetScore());
+              score_.GetTotalScore());
   }
 
-  EXPECT_EQ(2 * SiteEngagementScore::GetMaxPointsPerDay(), score_.GetScore());
+  EXPECT_EQ(2 * SiteEngagementScore::GetMaxPointsPerDay(),
+            score_.GetTotalScore());
 }
 
 // Test that scores are read / written correctly from / to empty score
@@ -339,32 +346,32 @@
 
   // The first engagement event gets the bonus.
   score1.AddPoints(0.5);
-  EXPECT_EQ(1.0, score1.GetScore());
+  EXPECT_EQ(1.0, score1.GetTotalScore());
 
   // Subsequent events do not.
   score1.AddPoints(0.5);
-  EXPECT_EQ(1.5, score1.GetScore());
+  EXPECT_EQ(1.5, score1.GetTotalScore());
 
   // Bonuses are awarded independently between scores.
   score2.AddPoints(1.0);
-  EXPECT_EQ(1.5, score2.GetScore());
+  EXPECT_EQ(1.5, score2.GetTotalScore());
   score2.AddPoints(1.0);
-  EXPECT_EQ(2.5, score2.GetScore());
+  EXPECT_EQ(2.5, score2.GetTotalScore());
 
   test_clock_.SetNow(current_day + base::TimeDelta::FromDays(1));
 
   // The first event for the next day gets the bonus.
   score1.AddPoints(0.5);
-  EXPECT_EQ(2.5, score1.GetScore());
+  EXPECT_EQ(2.5, score1.GetTotalScore());
 
   // Subsequent events do not.
   score1.AddPoints(0.5);
-  EXPECT_EQ(3.0, score1.GetScore());
+  EXPECT_EQ(3.0, score1.GetTotalScore());
 
   score2.AddPoints(1.0);
-  EXPECT_EQ(4.0, score2.GetScore());
+  EXPECT_EQ(4.0, score2.GetTotalScore());
   score2.AddPoints(1.0);
-  EXPECT_EQ(5.0, score2.GetScore());
+  EXPECT_EQ(5.0, score2.GetTotalScore());
 }
 
 // Test that resetting a score has the correct properties.
@@ -373,26 +380,27 @@
 
   test_clock_.SetNow(current_day);
   score_.AddPoints(SiteEngagementScore::GetNavigationPoints());
-  EXPECT_EQ(SiteEngagementScore::GetNavigationPoints(), score_.GetScore());
+  EXPECT_EQ(SiteEngagementScore::GetNavigationPoints(), score_.GetTotalScore());
 
   current_day += base::TimeDelta::FromDays(7);
   test_clock_.SetNow(current_day);
 
   score_.Reset(20.0, current_day);
-  EXPECT_DOUBLE_EQ(20.0, score_.GetScore());
+  EXPECT_DOUBLE_EQ(20.0, score_.GetTotalScore());
   EXPECT_DOUBLE_EQ(0, score_.points_added_today_);
   EXPECT_EQ(current_day, score_.last_engagement_time_);
   EXPECT_TRUE(score_.last_shortcut_launch_time_.is_null());
 
   // Adding points after the reset should work as normal.
   score_.AddPoints(5);
-  EXPECT_EQ(25.0, score_.GetScore());
+  EXPECT_EQ(25.0, score_.GetTotalScore());
 
   // The decay should happen one decay period from the current time.
   test_clock_.SetNow(current_day +
                      base::TimeDelta::FromHours(
                          SiteEngagementScore::GetDecayPeriodInHours() + 1));
-  EXPECT_EQ(25.0 - SiteEngagementScore::GetDecayPoints(), score_.GetScore());
+  EXPECT_EQ(25.0 - SiteEngagementScore::GetDecayPoints(),
+            score_.GetTotalScore());
 
   // Ensure that manually setting a time works as expected.
   score_.AddPoints(5);
@@ -400,7 +408,7 @@
   base::Time now = test_clock_.Now();
   score_.Reset(10.0, now);
 
-  EXPECT_DOUBLE_EQ(10.0, score_.GetScore());
+  EXPECT_DOUBLE_EQ(10.0, score_.GetTotalScore());
   EXPECT_DOUBLE_EQ(0, score_.points_added_today_);
   EXPECT_EQ(now, score_.last_engagement_time_);
   EXPECT_TRUE(score_.last_shortcut_launch_time_.is_null());
@@ -413,7 +421,7 @@
   score_.Reset(15.0, now);
 
   // 5 bonus from the last shortcut launch.
-  EXPECT_DOUBLE_EQ(20.0, score_.GetScore());
+  EXPECT_DOUBLE_EQ(20.0, score_.GetTotalScore());
   EXPECT_DOUBLE_EQ(0, score_.points_added_today_);
   EXPECT_EQ(now, score_.last_engagement_time_);
   EXPECT_EQ(old_now, score_.last_shortcut_launch_time_);
@@ -431,19 +439,60 @@
   score_.AddPoints(2.0);
   current_day += base::TimeDelta::FromDays(7);
   test_clock_.SetNow(current_day);
-  EXPECT_DOUBLE_EQ(1.0, score_.GetScore());
+  EXPECT_DOUBLE_EQ(1.0, score_.GetTotalScore());
 
   // 3 decay periods, expect the score to be halved 3 times.
   score_.AddPoints(15.0);
   current_day += base::TimeDelta::FromDays(21);
   test_clock_.SetNow(current_day);
-  EXPECT_DOUBLE_EQ(2.0, score_.GetScore());
+  EXPECT_DOUBLE_EQ(2.0, score_.GetTotalScore());
 
   // Ensure point removal happens after proportional decay.
   score_.AddPoints(4.0);
-  EXPECT_DOUBLE_EQ(6.0, score_.GetScore());
+  EXPECT_DOUBLE_EQ(6.0, score_.GetTotalScore());
   SetParamValue(SiteEngagementScore::DECAY_POINTS, 2.0);
   current_day += base::TimeDelta::FromDays(7);
   test_clock_.SetNow(current_day);
-  EXPECT_NEAR(1.0, score_.GetScore(), kMaxRoundingDeviation);
+  EXPECT_NEAR(1.0, score_.GetTotalScore(), kMaxRoundingDeviation);
+}
+
+// Verify that GetDetails fills out all fields correctly.
+TEST_F(SiteEngagementScoreTest, GetDetails) {
+  // Advance the clock, otherwise Now() is the same as the null Time value.
+  test_clock_.Advance(base::TimeDelta::FromDays(365));
+
+  GURL url("http://www.google.com/");
+
+  // Replace |score_| with one with an actual URL, and with a settings map.
+  HostContentSettingsMap* settings_map =
+      HostContentSettingsMapFactory::GetForProfile(profile());
+  score_ = SiteEngagementScore(&test_clock_, url, settings_map);
+
+  // Initially all component scores should be zero.
+  mojom::SiteEngagementDetails details = score_.GetDetails();
+  EXPECT_DOUBLE_EQ(0.0, details.total_score);
+  EXPECT_DOUBLE_EQ(0.0, details.installed_bonus);
+  EXPECT_DOUBLE_EQ(0.0, details.notifications_bonus);
+  EXPECT_DOUBLE_EQ(0.0, details.base_score);
+  EXPECT_EQ(url, details.origin);
+
+  // Add notifications permission and verify that impacts the correct bonus,
+  // and the total.
+  settings_map->SetContentSettingDefaultScope(
+      url, url, CONTENT_SETTINGS_TYPE_NOTIFICATIONS, std::string(),
+      CONTENT_SETTING_ALLOW);
+  details = score_.GetDetails();
+  EXPECT_DOUBLE_EQ(details.notifications_bonus, details.total_score);
+  EXPECT_DOUBLE_EQ(0.0, details.installed_bonus);
+  EXPECT_LT(0.0, details.notifications_bonus);
+  EXPECT_DOUBLE_EQ(0.0, details.base_score);
+
+  // Simulate the app having been launched.
+  score_.set_last_shortcut_launch_time(test_clock_.Now());
+  details = score_.GetDetails();
+  EXPECT_DOUBLE_EQ(details.installed_bonus + details.notifications_bonus,
+                   details.total_score);
+  EXPECT_LT(0.0, details.installed_bonus);
+  EXPECT_LT(0.0, details.notifications_bonus);
+  EXPECT_DOUBLE_EQ(0.0, details.base_score);
 }
diff --git a/chrome/browser/engagement/site_engagement_service.cc b/chrome/browser/engagement/site_engagement_service.cc
index 43b83532..360d90b 100644
--- a/chrome/browser/engagement/site_engagement_service.cc
+++ b/chrome/browser/engagement/site_engagement_service.cc
@@ -8,7 +8,6 @@
 
 #include <algorithm>
 #include <utility>
-#include <vector>
 
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
@@ -52,13 +51,36 @@
 // Length of time between metrics logging.
 const int kMetricsIntervalInMinutes = 60;
 
-std::unique_ptr<ContentSettingsForOneType> GetEngagementContentSettings(
-    HostContentSettingsMap* settings_map) {
-  std::unique_ptr<ContentSettingsForOneType> engagement_settings(
-      new ContentSettingsForOneType);
-  settings_map->GetSettingsForOneType(CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT,
-                                      std::string(), engagement_settings.get());
-  return engagement_settings;
+// Helper for fetching content settings for one type.
+ContentSettingsForOneType GetContentSettingsFromProfile(
+    Profile* profile,
+    ContentSettingsType type) {
+  ContentSettingsForOneType content_settings;
+  HostContentSettingsMapFactory::GetForProfile(profile)->GetSettingsForOneType(
+      type, content_settings::ResourceIdentifier(), &content_settings);
+  return content_settings;
+}
+
+// Returns the combined list of origins which either have site engagement
+// data stored, or have other settings that would provide a score bonus.
+std::set<GURL> GetEngagementOriginsFromContentSettings(Profile* profile) {
+  std::set<GURL> urls;
+
+  // Fetch URLs of sites with engagement details stored.
+  for (const auto& site : GetContentSettingsFromProfile(
+           profile, CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT)) {
+    urls.insert(GURL(site.primary_pattern.ToString()));
+  }
+
+  // Fetch URLs of sites for which notifications are allowed.
+  for (const auto& site : GetContentSettingsFromProfile(
+           profile, CONTENT_SETTINGS_TYPE_NOTIFICATIONS)) {
+    if (site.setting != CONTENT_SETTING_ALLOW)
+      continue;
+    urls.insert(GURL(site.primary_pattern.ToString()));
+  }
+
+  return urls;
 }
 
 // Only accept a navigation event for engagement if it is one of:
@@ -104,8 +126,7 @@
     HostContentSettingsMap* settings,
     const GURL& origin) {
   auto clock = base::MakeUnique<base::DefaultClock>();
-  return SiteEngagementScore(clock.get(), origin, settings)
-      .GetScore();
+  return SiteEngagementScore(clock.get(), origin, settings).GetTotalScore();
 }
 
 SiteEngagementService::SiteEngagementService(Profile* profile)
@@ -139,21 +160,28 @@
   return CreateEngagementScore(url).GetEngagementLevel();
 }
 
-std::map<GURL, double> SiteEngagementService::GetScoreMap() const {
-  HostContentSettingsMap* settings_map =
-      HostContentSettingsMapFactory::GetForProfile(profile_);
-  std::unique_ptr<ContentSettingsForOneType> engagement_settings =
-      GetEngagementContentSettings(settings_map);
+std::vector<mojom::SiteEngagementDetails> SiteEngagementService::GetAllDetails()
+    const {
+  std::set<GURL> origins = GetEngagementOriginsFromContentSettings(profile_);
 
-  std::map<GURL, double> score_map;
-  for (const auto& site : *engagement_settings) {
-    GURL origin(site.primary_pattern.ToString());
+  std::vector<mojom::SiteEngagementDetails> details;
+  details.reserve(origins.size());
+  for (const GURL& origin : origins) {
     if (!origin.is_valid())
       continue;
-
-    score_map[origin] = GetScore(origin);
+    details.push_back(GetDetails(origin));
   }
 
+  return details;
+}
+
+std::map<GURL, double> SiteEngagementService::GetScoreMap() const {
+  std::map<GURL, double> score_map;
+  for (const GURL& origin : GetEngagementOriginsFromContentSettings(profile_)) {
+    if (!origin.is_valid())
+      continue;
+    score_map[origin] = GetScore(origin);
+  }
   return score_map;
 }
 
@@ -244,12 +272,17 @@
 }
 
 double SiteEngagementService::GetScore(const GURL& url) const {
+  return GetDetails(url).total_score;
+}
+
+mojom::SiteEngagementDetails SiteEngagementService::GetDetails(
+    const GURL& url) const {
   // Ensure that if engagement is stale, we clean things up before fetching the
   // score.
   if (IsLastEngagementStale())
     CleanupEngagementScores(true);
 
-  return CreateEngagementScore(url).GetScore();
+  return CreateEngagementScore(url).GetDetails();
 }
 
 double SiteEngagementService::GetTotalEngagementPoints() const {
@@ -317,11 +350,6 @@
 
 void SiteEngagementService::CleanupEngagementScores(
     bool update_last_engagement_time) const {
-  HostContentSettingsMap* settings_map =
-      HostContentSettingsMapFactory::GetForProfile(profile_);
-  std::unique_ptr<ContentSettingsForOneType> engagement_settings =
-      GetEngagementContentSettings(settings_map);
-
   // We want to rebase last engagement times relative to MaxDecaysPerScore
   // periods of decay in the past.
   base::Time now = clock_->Now();
@@ -341,7 +369,10 @@
   if (last_engagement_time > now)
     last_engagement_time = now;
 
-  for (const auto& site : *engagement_settings) {
+  HostContentSettingsMap* settings_map =
+      HostContentSettingsMapFactory::GetForProfile(profile_);
+  for (const auto& site : GetContentSettingsFromProfile(
+           profile_, CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT)) {
     GURL origin(site.primary_pattern.ToString());
 
     if (origin.is_valid()) {
@@ -376,14 +407,15 @@
         score.Commit();
       }
 
-      if (score.GetScore() > SiteEngagementScore::GetScoreCleanupThreshold())
+      if (score.GetTotalScore() >
+          SiteEngagementScore::GetScoreCleanupThreshold())
         continue;
     }
 
     // This origin has a score of 0. Wipe it from content settings.
     settings_map->SetWebsiteSettingDefaultScope(
-        origin, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(),
-        nullptr);
+        origin, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT,
+        content_settings::ResourceIdentifier(), nullptr);
   }
 
   // Set the last engagement time to be consistent with the scores. This will
@@ -577,16 +609,13 @@
 }
 
 int SiteEngagementService::OriginsWithMaxDailyEngagement() const {
-  HostContentSettingsMap* settings_map =
-      HostContentSettingsMapFactory::GetForProfile(profile_);
-  std::unique_ptr<ContentSettingsForOneType> engagement_settings =
-      GetEngagementContentSettings(settings_map);
-
   int total_origins = 0;
 
   // We cannot call GetScoreMap as we need the score objects, not raw scores.
-  for (const auto& site : *engagement_settings) {
+  for (const auto& site : GetContentSettingsFromProfile(
+           profile_, CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT)) {
     GURL origin(site.primary_pattern.ToString());
+
     if (!origin.is_valid())
       continue;
 
@@ -653,9 +682,11 @@
     // engagement is next accessed, it will decay back to the proportionally
     // reduced value rather than being decayed once here, and then once again
     // when it is next accessed.
+    // TODO(703848): Move the proportional decay logic into SiteEngagementScore,
+    // so it can decay raw_score_ directly, without the double-decay issue.
     SiteEngagementScore engagement_score = CreateEngagementScore(origin);
 
-    double new_score = proportion_remaining * engagement_score.GetScore();
+    double new_score = proportion_remaining * engagement_score.GetTotalScore();
     int hours_since_engagement = (now - last_visit).InHours();
     int periods =
         hours_since_engagement / SiteEngagementScore::GetDecayPeriodInHours();
diff --git a/chrome/browser/engagement/site_engagement_service.h b/chrome/browser/engagement/site_engagement_service.h
index 35c005d..b47e6ca 100644
--- a/chrome/browser/engagement/site_engagement_service.h
+++ b/chrome/browser/engagement/site_engagement_service.h
@@ -8,6 +8,7 @@
 #include <map>
 #include <memory>
 #include <set>
+#include <vector>
 
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
@@ -15,6 +16,7 @@
 #include "base/observer_list.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "chrome/browser/engagement/site_engagement_details.mojom.h"
 #include "chrome/browser/engagement/site_engagement_metrics.h"
 #include "chrome/browser/engagement/site_engagement_observer.h"
 #include "components/history/core/browser/history_service_observer.h"
@@ -116,7 +118,13 @@
   // Returns the engagement level of |url|.
   blink::mojom::EngagementLevel GetEngagementLevel(const GURL& url) const;
 
+  // Returns an array of engagement score details for all origins which have
+  // a score, whether due to direct engagement, or other factors that cause
+  // an engagement bonus to be applied.
+  std::vector<mojom::SiteEngagementDetails> GetAllDetails() const;
+
   // Returns a map of all stored origins and their engagement scores.
+  // TODO(703848): Migrate important sites impl off this and remove it
   std::map<GURL, double> GetScoreMap() const;
 
   // Update the engagement score of |url| for a notification interaction.
@@ -143,6 +151,9 @@
   void HelperCreated(SiteEngagementService::Helper* helper);
   void HelperDeleted(SiteEngagementService::Helper* helper);
 
+  // Returns the site engagement details for the specified |url|.
+  mojom::SiteEngagementDetails GetDetails(const GURL& url) const;
+
   // Overridden from SiteEngagementScoreProvider.
   double GetScore(const GURL& url) const override;
   double GetTotalEngagementPoints() const override;
diff --git a/chrome/browser/engagement/site_engagement_service_unittest.cc b/chrome/browser/engagement/site_engagement_service_unittest.cc
index e6c39a5..c65e9d6 100644
--- a/chrome/browser/engagement/site_engagement_service_unittest.cc
+++ b/chrome/browser/engagement/site_engagement_service_unittest.cc
@@ -548,16 +548,16 @@
       HostContentSettingsMapFactory::GetForProfile(profile());
 
   settings_map->SetContentSettingDefaultScope(
-      url1, url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS, std::string(),
-      CONTENT_SETTING_ALLOW);
+      url1, GURL(), CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+      content_settings::ResourceIdentifier(), CONTENT_SETTING_ALLOW);
 
   settings_map->SetContentSettingDefaultScope(
-      url2, url2, CONTENT_SETTINGS_TYPE_NOTIFICATIONS, std::string(),
-      CONTENT_SETTING_BLOCK);
+      url2, GURL(), CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+      content_settings::ResourceIdentifier(), CONTENT_SETTING_BLOCK);
 
   settings_map->SetContentSettingDefaultScope(
-      url3, url3, CONTENT_SETTINGS_TYPE_NOTIFICATIONS, std::string(),
-      CONTENT_SETTING_ASK);
+      url3, GURL(), CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+      content_settings::ResourceIdentifier(), CONTENT_SETTING_ASK);
 
   EXPECT_EQ(5, service_->GetScore(url1));
   EXPECT_EQ(0, service_->GetScore(url2));
@@ -569,8 +569,8 @@
   EXPECT_EQ(3, service_->GetScore(url2));
 
   settings_map->SetContentSettingDefaultScope(
-      url1, url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS, std::string(),
-      CONTENT_SETTING_BLOCK);
+      url1, GURL(), CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+      content_settings::ResourceIdentifier(), CONTENT_SETTING_BLOCK);
 
   EXPECT_EQ(1, service_->GetScore(url1));
 }
@@ -1835,3 +1835,37 @@
   EXPECT_EQ(3, CheckScoreFromSettingsOnThread(content::BrowserThread::IO,
                                               incognito_settings_map, url2));
 }
+
+TEST_F(SiteEngagementServiceTest, GetAllDetailsIncludesBonusOnlyScores) {
+  GURL url1("http://www.google.com/");
+  GURL url2("https://www.google.com/");
+  GURL url3("https://drive.google.com/");
+  GURL url4("https://nothing.google.com/");
+
+  std::vector<mojom::SiteEngagementDetails> details = service_->GetAllDetails();
+  EXPECT_EQ(0u, details.size());
+
+  // Add a single site score via explicitly resetting the engagement score.
+  service_->ResetBaseScoreForURL(url1, 5);
+
+  // Add a second site indirectly, via notifications permissions.
+  HostContentSettingsMap* settings_map =
+      HostContentSettingsMapFactory::GetForProfile(profile());
+  settings_map->SetContentSettingDefaultScope(
+      url2, GURL(), CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+      content_settings::ResourceIdentifier(), CONTENT_SETTING_ALLOW);
+
+  // Add third and fourth sites with notifications permission explicitly denied,
+  // to verify that they are not included.
+  settings_map->SetContentSettingDefaultScope(
+      url3, GURL(), CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+      content_settings::ResourceIdentifier(), CONTENT_SETTING_BLOCK);
+  settings_map->SetContentSettingDefaultScope(
+      url4, GURL(), CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+      content_settings::ResourceIdentifier(), CONTENT_SETTING_BLOCK);
+
+  // Verify that the URLs with engagement, and with notifications permission
+  // boosted engagement total, are included.
+  details = service_->GetAllDetails();
+  EXPECT_EQ(2u, details.size());
+}
diff --git a/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc b/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
index 1c05df7..00c1c9e 100644
--- a/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
+++ b/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
@@ -681,9 +681,7 @@
 }
 
 // TODO(stevenjb): Find a better way to set this up on Chrome OS.
-// TODO(stevenjb): https://crbug.com/710241
-IN_PROC_BROWSER_TEST_F(NetworkingPrivateChromeOSApiTest,
-                       DISABLED_GetManagedProperties) {
+IN_PROC_BROWSER_TEST_F(NetworkingPrivateChromeOSApiTest, GetManagedProperties) {
   const std::string uidata_blob =
       "{ \"user_settings\": {"
       "      \"WiFi\": {"
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 9080413e..680501e 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -275,6 +275,17 @@
   return extension;
 }
 
+std::unique_ptr<ExternalInstallInfoFile> CreateExternalExtension(
+    const extensions::ExtensionId& extension_id,
+    const std::string& version_str,
+    const base::FilePath& path,
+    Manifest::Location location,
+    Extension::InitFromValueFlags flags) {
+  return base::MakeUnique<ExternalInstallInfoFile>(
+      extension_id, base::MakeUnique<base::Version>(version_str), path,
+      location, flags, false, false);
+}
+
 }  // namespace
 
 class MockProviderVisitor
@@ -524,9 +535,11 @@
     AfterStartupTaskUtils::SetBrowserStartupIsCompleteForTesting();
   }
 
-  void AddMockExternalProvider(
-      extensions::ExternalProviderInterface* provider) {
-    service()->AddProviderForTesting(base::WrapUnique(provider));
+  MockExternalProvider* AddMockExternalProvider(Manifest::Location location) {
+    auto provider = base::MakeUnique<MockExternalProvider>(service(), location);
+    MockExternalProvider* provider_ptr = provider.get();
+    service()->AddProviderForTesting(std::move(provider));
+    return provider_ptr;
   }
 
   // Checks for external extensions and waits for one to complete installing.
@@ -1083,16 +1096,14 @@
   base::FilePath path = data_dir().AppendASCII("good.crx");
 
   // Register and install an external extension.
-  std::unique_ptr<base::Version> version(new base::Version("1.0.0.0"));
-  content::WindowedNotificationObserver observer(
-      extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-      content::NotificationService::AllSources());
-  std::unique_ptr<ExternalInstallInfoFile> info(new ExternalInstallInfoFile(
-      good_crx, std::move(version), path, Manifest::EXTERNAL_PREF,
-      Extension::FROM_BOOKMARK, false /* mark_acknowledged */,
-      false /* install_immediately */));
-  if (service()->OnExternalExtensionFileFound(*info))
-    observer.Wait();
+  std::string version_str = "1.0.0.0";
+  std::unique_ptr<ExternalInstallInfoFile> info = CreateExternalExtension(
+      good_crx, version_str, path, Manifest::EXTERNAL_PREF,
+      Extension::FROM_BOOKMARK);
+  MockExternalProvider* provider =
+      AddMockExternalProvider(Manifest::EXTERNAL_POLICY_DOWNLOAD);
+  provider->UpdateOrAddExtension(std::move(info));
+  WaitForExternalExtensionInstalled();
 
   const Extension* extension = service()->GetExtensionById(good_crx, false);
   ASSERT_TRUE(extension);
@@ -1114,16 +1125,15 @@
 
   base::FilePath path = data_dir().AppendASCII("good.crx");
 
+  std::string version_str = "1.0.0.0";
   // Install an external extension.
-  content::WindowedNotificationObserver observer(
-      extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-      content::NotificationService::AllSources());
-  std::unique_ptr<base::Version> version(new base::Version("1.0.0.0"));
-  std::unique_ptr<ExternalInstallInfoFile> info(new ExternalInstallInfoFile(
-      good_crx, std::move(version), path, Manifest::EXTERNAL_PREF,
-      Extension::NO_FLAGS, false, false));
-  if (service()->OnExternalExtensionFileFound(*info))
-    observer.Wait();
+  std::unique_ptr<ExternalInstallInfoFile> info =
+      CreateExternalExtension(good_crx, version_str, path,
+                              Manifest::EXTERNAL_PREF, Extension::NO_FLAGS);
+  MockExternalProvider* provider =
+      AddMockExternalProvider(Manifest::EXTERNAL_PREF);
+  provider->UpdateOrAddExtension(std::move(info));
+  WaitForExternalExtensionInstalled();
 
   ASSERT_TRUE(service()->GetExtensionById(good_crx, false));
 
@@ -1133,19 +1143,20 @@
                       Extension::EXTERNAL_EXTENSION_UNINSTALLED);
 
   // Try to re-install it externally. This should fail because of the killbit.
-  service()->OnExternalExtensionFileFound(*info);
+  info = CreateExternalExtension(good_crx, version_str, path,
+                                 Manifest::EXTERNAL_PREF, Extension::NO_FLAGS);
+  provider->UpdateOrAddExtension(std::move(info));
   base::RunLoop().RunUntilIdle();
   ASSERT_TRUE(NULL == service()->GetExtensionById(good_crx, false));
   ValidateIntegerPref(good_crx, "state",
                       Extension::EXTERNAL_EXTENSION_UNINSTALLED);
 
-  version.reset(new base::Version("1.0.0.1"));
+  std::string newer_version = "1.0.0.1";
   // Repeat the same thing with a newer version of the extension.
   path = data_dir().AppendASCII("good2.crx");
-  info.reset(new ExternalInstallInfoFile(good_crx, std::move(version), path,
-                                         Manifest::EXTERNAL_PREF,
-                                         Extension::NO_FLAGS, false, false));
-  service()->OnExternalExtensionFileFound(*info);
+  info = CreateExternalExtension(good_crx, newer_version, path,
+                                 Manifest::EXTERNAL_PREF, Extension::NO_FLAGS);
+  provider->UpdateOrAddExtension(std::move(info));
   base::RunLoop().RunUntilIdle();
   ASSERT_TRUE(NULL == service()->GetExtensionById(good_crx, false));
   ValidateIntegerPref(good_crx, "state",
@@ -1206,32 +1217,29 @@
   InitializeEmptyExtensionService();
   base::FilePath path = data_dir().AppendASCII("good.crx");
 
-  std::unique_ptr<base::Version> version(new base::Version("1.0.0.0"));
+  std::string version_str = "1.0.0.0";
 
   const std::string wrong_id = all_zero;
   const std::string correct_id = good_crx;
   ASSERT_NE(correct_id, wrong_id);
 
+  MockExternalProvider* provider =
+      AddMockExternalProvider(Manifest::EXTERNAL_PREF);
+
   // Install an external extension with an ID from the external
   // source that is not equal to the ID in the extension manifest.
-  content::WindowedNotificationObserver observer(
-      extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-      content::NotificationService::AllSources());
-  std::unique_ptr<ExternalInstallInfoFile> info(new ExternalInstallInfoFile(
-      wrong_id, std::move(version), path, Manifest::EXTERNAL_PREF,
-      Extension::NO_FLAGS, false, false));
-  service()->OnExternalExtensionFileFound(*info);
-
-  observer.Wait();
+  std::unique_ptr<ExternalInstallInfoFile> info =
+      CreateExternalExtension(wrong_id, version_str, path,
+                              Manifest::EXTERNAL_PREF, Extension::NO_FLAGS);
+  provider->UpdateOrAddExtension(std::move(info));
+  WaitForExternalExtensionInstalled();
   ASSERT_FALSE(service()->GetExtensionById(good_crx, false));
 
   // Try again with the right ID. Expect success.
-  content::WindowedNotificationObserver observer2(
-      extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-      content::NotificationService::AllSources());
-  info->extension_id = correct_id;
-  if (service()->OnExternalExtensionFileFound(*info))
-    observer2.Wait();
+  info = CreateExternalExtension(correct_id, version_str, path,
+                                 Manifest::EXTERNAL_PREF, Extension::NO_FLAGS);
+  provider->UpdateOrAddExtension(std::move(info));
+  WaitForExternalExtensionInstalled();
   ASSERT_TRUE(service()->GetExtensionById(good_crx, false));
 }
 
@@ -1239,30 +1247,26 @@
 TEST_F(ExtensionServiceTest, FailOnWrongVersion) {
   InitializeEmptyExtensionService();
   base::FilePath path = data_dir().AppendASCII("good.crx");
+  MockExternalProvider* provider =
+      AddMockExternalProvider(Manifest::EXTERNAL_PREF);
 
   // Install an external extension with a version from the external
   // source that is not equal to the version in the extension manifest.
-  content::WindowedNotificationObserver observer(
-      extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-      content::NotificationService::AllSources());
-  std::unique_ptr<base::Version> wrong_version(new base::Version("1.2.3.4"));
-  std::unique_ptr<ExternalInstallInfoFile> info(new ExternalInstallInfoFile(
-      good_crx, std::move(wrong_version), path, Manifest::EXTERNAL_PREF,
-      Extension::NO_FLAGS, false, false));
-  service()->OnExternalExtensionFileFound(*info);
-
-  observer.Wait();
+  std::string wrong_version_str = "1.2.3.4";
+  std::unique_ptr<ExternalInstallInfoFile> wrong_info =
+      CreateExternalExtension(good_crx, wrong_version_str, path,
+                              Manifest::EXTERNAL_PREF, Extension::NO_FLAGS);
+  provider->UpdateOrAddExtension(std::move(wrong_info));
+  WaitForExternalExtensionInstalled();
   ASSERT_FALSE(service()->GetExtensionById(good_crx, false));
 
   // Try again with the right version. Expect success.
   service()->pending_extension_manager()->Remove(good_crx);
-  std::unique_ptr<base::Version> correct_version(new base::Version("1.0.0.0"));
-  info->version = std::move(correct_version);
-  content::WindowedNotificationObserver observer2(
-      extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-      content::NotificationService::AllSources());
-  if (service()->OnExternalExtensionFileFound(*info))
-    observer2.Wait();
+  std::unique_ptr<ExternalInstallInfoFile> correct_info =
+      CreateExternalExtension(good_crx, "1.0.0.0", path,
+                              Manifest::EXTERNAL_PREF, Extension::NO_FLAGS);
+  provider->UpdateOrAddExtension(std::move(correct_info));
+  WaitForExternalExtensionInstalled();
   ASSERT_TRUE(service()->GetExtensionById(good_crx, false));
 }
 
@@ -3451,18 +3455,13 @@
 
   // Have policy force-install an extension.
   MockExternalProvider* provider =
-      new MockExternalProvider(service(), Manifest::EXTERNAL_POLICY_DOWNLOAD);
-  AddMockExternalProvider(provider);
+      AddMockExternalProvider(Manifest::EXTERNAL_POLICY_DOWNLOAD);
   provider->UpdateOrAddExtension(
       good_crx, "1.0.0.0", data_dir().AppendASCII("good_crx"));
 
   // Reloading extensions should find our externally registered extension
   // and install it.
-  content::WindowedNotificationObserver observer(
-      extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-      content::NotificationService::AllSources());
-  service()->CheckForExternalUpdates();
-  observer.Wait();
+  WaitForExternalExtensionInstalled();
 
   AssertExtensionBlocksAndUnblocks(false, good_crx);
 }
@@ -3666,18 +3665,13 @@
 
   // Have policy force-install an extension.
   MockExternalProvider* provider =
-      new MockExternalProvider(service(), Manifest::EXTERNAL_POLICY_DOWNLOAD);
-  AddMockExternalProvider(provider);
+      AddMockExternalProvider(Manifest::EXTERNAL_POLICY_DOWNLOAD);
   provider->UpdateOrAddExtension(
       good_crx, "1.0.0.0", data_dir().AppendASCII("good.crx"));
 
   // Reloading extensions should find our externally registered extension
   // and install it.
-  content::WindowedNotificationObserver observer(
-      extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-      content::NotificationService::AllSources());
-  service()->CheckForExternalUpdates();
-  observer.Wait();
+  WaitForExternalExtensionInstalled();
 
   // Extension should be installed despite blacklist.
   ASSERT_EQ(1u, registry()->enabled_extensions().size());
@@ -3923,18 +3917,11 @@
 
   // Use MockExternalProvider to simulate force installing extension.
   MockExternalProvider* provider =
-      new MockExternalProvider(service(), Manifest::EXTERNAL_POLICY_DOWNLOAD);
-  AddMockExternalProvider(provider);
+      AddMockExternalProvider(Manifest::EXTERNAL_POLICY_DOWNLOAD);
   provider->UpdateOrAddExtension(permissions_blocklist, "1.0", crx_path);
 
-  {
-    // Attempts to force install this extension.
-    content::WindowedNotificationObserver observer(
-        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-        content::NotificationService::AllSources());
-    service()->CheckForExternalUpdates();
-    observer.Wait();
-  }
+  // Attempts to force install this extension.
+  WaitForExternalExtensionInstalled();
 
   // The extension should not be installed.
   ASSERT_FALSE(service()->GetInstalledExtension(permissions_blocklist));
@@ -3949,14 +3936,8 @@
     pref.ClearBlockedPermissions("*");
   }
 
-  {
-    // Attempts to force install this extension again.
-    content::WindowedNotificationObserver observer(
-        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-        content::NotificationService::AllSources());
-    service()->CheckForExternalUpdates();
-    observer.Wait();
-  }
+  // Attempts to force install this extension again.
+  WaitForExternalExtensionInstalled();
 
   const Extension* installed =
       service()->GetInstalledExtension(permissions_blocklist);
@@ -4025,15 +4006,9 @@
   // Force install another extension with known id and same manifest as 'ext2'.
   std::string ext2_forced = permissions_blocklist;
   MockExternalProvider* provider =
-      new MockExternalProvider(service(), Manifest::EXTERNAL_POLICY_DOWNLOAD);
-  AddMockExternalProvider(provider);
+      AddMockExternalProvider(Manifest::EXTERNAL_POLICY_DOWNLOAD);
   provider->UpdateOrAddExtension(ext2_forced, "2.0", crx_path);
-
-  content::WindowedNotificationObserver observer(
-      extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-      content::NotificationService::AllSources());
-  service()->CheckForExternalUpdates();
-  observer.Wait();
+  WaitForExternalExtensionInstalled();
 
   extensions::ExtensionRegistry* registry =
       extensions::ExtensionRegistry::Get(profile());
@@ -4088,16 +4063,14 @@
   {
     // Register and install an external extension.
     MockExternalProvider* provider =
-        new MockExternalProvider(service(), Manifest::EXTERNAL_PREF);
-    AddMockExternalProvider(provider);
+        AddMockExternalProvider(Manifest::EXTERNAL_PREF);
     provider->UpdateOrAddExtension(
         good_crx, "1.0.0.0", data_dir().AppendASCII("good.crx"));
   }
   {
     // Have policy force-install an extension.
     MockExternalProvider* provider =
-        new MockExternalProvider(service(), Manifest::EXTERNAL_POLICY_DOWNLOAD);
-    AddMockExternalProvider(provider);
+        AddMockExternalProvider(Manifest::EXTERNAL_POLICY_DOWNLOAD);
     provider->UpdateOrAddExtension(
         page_action, "1.0.0.0", data_dir().AppendASCII("page_action.crx"));
   }
@@ -4128,8 +4101,7 @@
 
   // Register and install an external extension.
   MockExternalProvider* provider =
-      new MockExternalProvider(service(), Manifest::EXTERNAL_PREF);
-  AddMockExternalProvider(provider);  // Takes ownership.
+      AddMockExternalProvider(Manifest::EXTERNAL_PREF);  // Takes ownership.
   provider->UpdateOrAddExtension(good_crx, "1.0.0.0",
                                  data_dir().AppendASCII("good.crx"));
 
@@ -4168,8 +4140,7 @@
 
   // Register and install an external extension.
   MockExternalProvider* provider =
-      new MockExternalProvider(service(), Manifest::EXTERNAL_PREF);
-  AddMockExternalProvider(provider);
+      AddMockExternalProvider(Manifest::EXTERNAL_PREF);
   provider->UpdateOrAddExtension(good_crx, "1.0.0.0",
                                  data_dir().AppendASCII("good.crx"));
 
@@ -4226,15 +4197,11 @@
         Manifest::INVALID_LOCATION,
         Extension::FROM_WEBSTORE | Extension::WAS_INSTALLED_BY_DEFAULT);
 
-    AddMockExternalProvider(provider);
+    service()->AddProviderForTesting(base::WrapUnique(provider));
   }
 
   ASSERT_EQ(0u, registry()->enabled_extensions().size());
-  content::WindowedNotificationObserver observer(
-      extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-      content::NotificationService::AllSources());
-  service()->CheckForExternalUpdates();
-  observer.Wait();
+  WaitForExternalExtensionInstalled();
 
   ASSERT_EQ(1u, registry()->enabled_extensions().size());
   EXPECT_TRUE(service()->GetExtensionById(good_crx, false));
@@ -4939,11 +4906,7 @@
 
   // Reloading extensions should find our externally registered extension
   // and install it.
-  content::WindowedNotificationObserver observer(
-      extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-      content::NotificationService::AllSources());
-  service()->CheckForExternalUpdates();
-  observer.Wait();
+  WaitForExternalExtensionInstalled();
 
   ASSERT_EQ(0u, GetErrors().size());
   ASSERT_EQ(1u, loaded_.size());
@@ -4969,11 +4932,7 @@
   provider->UpdateOrAddExtension(good_crx, "1.0.0.1", source_path);
 
   loaded_.clear();
-  content::WindowedNotificationObserver observer_2(
-      extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-      content::NotificationService::AllSources());
-  service()->CheckForExternalUpdates();
-  observer_2.Wait();
+  WaitForExternalExtensionInstalled();
   ASSERT_EQ(0u, GetErrors().size());
   ASSERT_EQ(1u, loaded_.size());
   ASSERT_EQ("1.0.0.1", loaded_[0]->version()->GetString());
@@ -5012,11 +4971,7 @@
     SetPrefInteg(good_crx, "state", Extension::ENABLED);
 
     loaded_.clear();
-    content::WindowedNotificationObserver observer(
-        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-        content::NotificationService::AllSources());
-    service()->CheckForExternalUpdates();
-    observer.Wait();
+    WaitForExternalExtensionInstalled();
     ASSERT_EQ(1u, loaded_.size());
   }
   ValidatePrefKeyCount(1);
@@ -5041,12 +4996,8 @@
 
     // Now test the case where user uninstalls and then the extension is removed
     // from the external provider.
-    content::WindowedNotificationObserver observer(
-        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-        content::NotificationService::AllSources());
     provider->UpdateOrAddExtension(good_crx, "1.0.0.1", source_path);
-    service()->CheckForExternalUpdates();
-    observer.Wait();
+    WaitForExternalExtensionInstalled();
 
     ASSERT_EQ(1u, loaded_.size());
     ASSERT_EQ(0u, GetErrors().size());
@@ -5082,8 +5033,7 @@
 
   // Now add providers. Extension system takes ownership of the objects.
   MockExternalProvider* reg_provider =
-      new MockExternalProvider(service(), Manifest::EXTERNAL_REGISTRY);
-  AddMockExternalProvider(reg_provider);
+      AddMockExternalProvider(Manifest::EXTERNAL_REGISTRY);
   TestExternalProvider(reg_provider, Manifest::EXTERNAL_REGISTRY);
 }
 #endif
@@ -5093,9 +5043,8 @@
 
   // Now add providers. Extension system takes ownership of the objects.
   MockExternalProvider* pref_provider =
-      new MockExternalProvider(service(), Manifest::EXTERNAL_PREF);
+      AddMockExternalProvider(Manifest::EXTERNAL_PREF);
 
-  AddMockExternalProvider(pref_provider);
   TestExternalProvider(pref_provider, Manifest::EXTERNAL_PREF);
 }
 
@@ -5111,8 +5060,7 @@
   // what the visitor does results in an extension being downloaded and
   // installed.
   MockExternalProvider* pref_provider =
-      new MockExternalProvider(service(), Manifest::EXTERNAL_PREF_DOWNLOAD);
-  AddMockExternalProvider(pref_provider);
+      AddMockExternalProvider(Manifest::EXTERNAL_PREF_DOWNLOAD);
   TestExternalProvider(pref_provider, Manifest::EXTERNAL_PREF_DOWNLOAD);
 }
 
@@ -5128,8 +5076,7 @@
   // what the visitor does results in an extension being downloaded and
   // installed.
   MockExternalProvider* pref_provider =
-      new MockExternalProvider(service(), Manifest::EXTERNAL_POLICY_DOWNLOAD);
-  AddMockExternalProvider(pref_provider);
+      AddMockExternalProvider(Manifest::EXTERNAL_POLICY_DOWNLOAD);
   TestExternalProvider(pref_provider, Manifest::EXTERNAL_POLICY_DOWNLOAD);
 }
 
@@ -5156,8 +5103,7 @@
   InitializeEmptyExtensionService();
 
   MockExternalProvider* provider =
-      new MockExternalProvider(service(), Manifest::EXTERNAL_PREF);
-  AddMockExternalProvider(provider);
+      AddMockExternalProvider(Manifest::EXTERNAL_PREF);
 
   // Verify that starting with no providers loads no extensions.
   service()->Init();
@@ -6221,8 +6167,7 @@
 
   InitializeEmptyExtensionService();
   MockExternalProvider* provider =
-      new MockExternalProvider(service(), Manifest::EXTERNAL_PREF);
-  AddMockExternalProvider(provider);
+      AddMockExternalProvider(Manifest::EXTERNAL_PREF);
 
   service()->external_install_manager()->UpdateExternalExtensionAlert();
   // Should return false, meaning there aren't any extensions that the user
@@ -6243,11 +6188,7 @@
   provider->UpdateOrAddExtension(
       hosted_app, "1.0.0.0", data_dir().AppendASCII("hosted_app.crx"));
 
-  content::WindowedNotificationObserver observer(
-      extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-      content::NotificationService::AllSources());
-  service()->CheckForExternalUpdates();
-  observer.Wait();
+  WaitForExternalExtensionInstalled();
   EXPECT_FALSE(HasExternalInstallErrors(service()));
 
   // Another normal extension, but installed externally.
@@ -6255,11 +6196,7 @@
   provider->UpdateOrAddExtension(
       page_action, "1.0.0.0", data_dir().AppendASCII("page_action.crx"));
 
-  content::WindowedNotificationObserver observer2(
-      extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-      content::NotificationService::AllSources());
-  service()->CheckForExternalUpdates();
-  observer2.Wait();
+  WaitForExternalExtensionInstalled();
   EXPECT_TRUE(HasExternalInstallErrors(service()));
 }
 
@@ -6271,17 +6208,12 @@
 
   InitializeEmptyExtensionService();
   MockExternalProvider* provider =
-      new MockExternalProvider(service(), Manifest::EXTERNAL_PREF);
-  AddMockExternalProvider(provider);
+      AddMockExternalProvider(Manifest::EXTERNAL_PREF);
 
   provider->UpdateOrAddExtension(
       page_action, "1.0.0.0", data_dir().AppendASCII("page_action.crx"));
+  WaitForExternalExtensionInstalled();
 
-  content::WindowedNotificationObserver observer(
-      extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-      content::NotificationService::AllSources());
-  service()->CheckForExternalUpdates();
-  observer.Wait();
   EXPECT_TRUE(HasExternalInstallErrors(service()));
   EXPECT_FALSE(service()->IsExtensionEnabled(page_action));
 
@@ -6340,8 +6272,7 @@
 
   InitializeEmptyExtensionService();
   MockExternalProvider* provider =
-      new MockExternalProvider(service(), Manifest::EXTERNAL_PREF);
-  AddMockExternalProvider(provider);
+      AddMockExternalProvider(Manifest::EXTERNAL_PREF);
 
   provider->UpdateOrAddExtension(
       page_action, "1.0.0.0", data_dir().AppendASCII("page_action.crx"));
@@ -6389,8 +6320,7 @@
   InitializeEmptyExtensionService();
 
   MockExternalProvider* reg_provider =
-      new MockExternalProvider(service(), Manifest::EXTERNAL_REGISTRY);
-  AddMockExternalProvider(reg_provider);
+      AddMockExternalProvider(Manifest::EXTERNAL_REGISTRY);
 
   std::string extension_info[][3] = {
       // {id, path, version}
@@ -6402,11 +6332,7 @@
     reg_provider->UpdateOrAddExtension(
         extension_info[i][0], extension_info[i][1],
         data_dir().AppendASCII(extension_info[i][2]));
-    content::WindowedNotificationObserver observer(
-        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-        content::NotificationService::AllSources());
-    service()->CheckForExternalUpdates();
-    observer.Wait();
+    WaitForExternalExtensionInstalled();
     const size_t expected_error_count = i + 1u;
     EXPECT_EQ(
         expected_error_count,
@@ -6457,8 +6383,7 @@
   InitializeExtensionService(params);
 
   MockExternalProvider* provider =
-      new MockExternalProvider(service(), Manifest::EXTERNAL_PREF);
-  AddMockExternalProvider(provider);
+      AddMockExternalProvider(Manifest::EXTERNAL_PREF);
 
   std::vector<BubbleErrorsTestData> data;
   data.push_back(BubbleErrorsTestData(
@@ -6485,13 +6410,9 @@
     content::WindowedNotificationObserver global_error_observer(
         chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED,
         content::NotificationService::AllSources());
-    content::WindowedNotificationObserver observer(
-        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-        content::NotificationService::AllSources());
     provider->UpdateOrAddExtension(data[i].id, data[i].version,
                                    data[i].crx_path);
-    service()->CheckForExternalUpdates();
-    observer.Wait();
+    WaitForExternalExtensionInstalled();
     // Make sure ExternalInstallError::OnDialogReady() fires.
     global_error_observer.Wait();
 
@@ -6538,14 +6459,10 @@
     content::WindowedNotificationObserver global_error_observer(
         chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED,
         content::NotificationService::AllSources());
-    content::WindowedNotificationObserver observer(
-        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-        content::NotificationService::AllSources());
     provider->UpdateOrAddExtension(
         updates_from_webstore3, "1",
         temp_dir().GetPath().AppendASCII("webstore3.crx"));
-    service()->CheckForExternalUpdates();
-    observer.Wait();
+    WaitForExternalExtensionInstalled();
     // Make sure ExternalInstallError::OnDialogReady() fires.
     global_error_observer.Wait();
 
@@ -6576,8 +6493,7 @@
   InitializeExtensionService(params);
 
   MockExternalProvider* provider =
-      new MockExternalProvider(service(), Manifest::EXTERNAL_PREF);
-  AddMockExternalProvider(provider);
+      AddMockExternalProvider(Manifest::EXTERNAL_PREF);
 
   std::vector<BubbleErrorsTestData> data;
   data.push_back(BubbleErrorsTestData(
@@ -6596,13 +6512,9 @@
     content::WindowedNotificationObserver global_error_observer(
         chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED,
         content::NotificationService::AllSources());
-    content::WindowedNotificationObserver observer(
-        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-        content::NotificationService::AllSources());
     provider->UpdateOrAddExtension(data[0].id, data[0].version,
                                    data[0].crx_path);
-    service()->CheckForExternalUpdates();
-    observer.Wait();
+    WaitForExternalExtensionInstalled();
     // Make sure ExternalInstallError::OnDialogReady() fires.
     global_error_observer.Wait();
 
@@ -6637,13 +6549,9 @@
     content::WindowedNotificationObserver global_error_observer(
         chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED,
         content::NotificationService::AllSources());
-    content::WindowedNotificationObserver observer(
-        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-        content::NotificationService::AllSources());
     provider->UpdateOrAddExtension(data[1].id, data[1].version,
                                    data[1].crx_path);
-    service()->CheckForExternalUpdates();
-    observer.Wait();
+    WaitForExternalExtensionInstalled();
     // Make sure ExternalInstallError::OnDialogReady() fires.
     global_error_observer.Wait();
 
@@ -6680,15 +6588,10 @@
           crx_path);
 
   MockExternalProvider* provider =
-      new MockExternalProvider(service(), Manifest::EXTERNAL_PREF);
-  AddMockExternalProvider(provider);
+      AddMockExternalProvider(Manifest::EXTERNAL_PREF);
   provider->UpdateOrAddExtension(updates_from_webstore, "1", crx_path);
+  WaitForExternalExtensionInstalled();
 
-  content::WindowedNotificationObserver observer(
-      extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-      content::NotificationService::AllSources());
-  service()->CheckForExternalUpdates();
-  observer.Wait();
   EXPECT_TRUE(HasExternalInstallErrors(service()));
   ASSERT_TRUE(GetError(updates_from_webstore));
   EXPECT_EQ(ExternalInstallError::BUBBLE_ALERT,
@@ -6709,15 +6612,10 @@
           crx_path);
 
   MockExternalProvider* provider =
-      new MockExternalProvider(service(), Manifest::EXTERNAL_PREF);
-  AddMockExternalProvider(provider);
+      AddMockExternalProvider(Manifest::EXTERNAL_PREF);
   provider->UpdateOrAddExtension(updates_from_webstore, "1", crx_path);
+  WaitForExternalExtensionInstalled();
 
-  content::WindowedNotificationObserver observer(
-      extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-      content::NotificationService::AllSources());
-  service()->CheckForExternalUpdates();
-  observer.Wait();
   EXPECT_TRUE(HasExternalInstallErrors(service()));
   ASSERT_TRUE(GetError(updates_from_webstore));
   EXPECT_NE(ExternalInstallError::BUBBLE_ALERT,
@@ -6741,15 +6639,10 @@
           crx_path);
 
   MockExternalProvider* provider =
-      new MockExternalProvider(service_, Manifest::EXTERNAL_PREF);
-  AddMockExternalProvider(provider);
+      AddMockExternalProvider(Manifest::EXTERNAL_PREF);
   provider->UpdateOrAddExtension(updates_from_webstore, "1", crx_path);
+  WaitForExternalExtensionInstalled();
 
-  content::WindowedNotificationObserver observer(
-      extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-      content::NotificationService::AllSources());
-  service_->CheckForExternalUpdates();
-  observer.Wait();
   EXPECT_TRUE(HasExternalInstallErrors(service_));
 
   // We check both enabled and disabled, since these are "eventually exclusive"
@@ -6784,15 +6677,10 @@
           crx_path);
 
   MockExternalProvider* provider =
-      new MockExternalProvider(service_, Manifest::EXTERNAL_PREF);
-  AddMockExternalProvider(provider);
+      AddMockExternalProvider(Manifest::EXTERNAL_PREF);
   provider->UpdateOrAddExtension(updates_from_webstore, "1", crx_path);
+  WaitForExternalExtensionInstalled();
 
-  content::WindowedNotificationObserver observer(
-      extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-      content::NotificationService::AllSources());
-  service_->CheckForExternalUpdates();
-  observer.Wait();
   EXPECT_TRUE(HasExternalInstallErrors(service_));
 
   // We check both enabled and disabled, since these are "eventually exclusive"
@@ -6826,8 +6714,7 @@
 
   // Register and install an external extension.
   MockExternalProvider* provider =
-      new MockExternalProvider(service(), Manifest::EXTERNAL_PREF);
-  AddMockExternalProvider(provider);
+      AddMockExternalProvider(Manifest::EXTERNAL_PREF);
   provider->UpdateOrAddExtension(good_crx, "1.0.0.0",
                                  data_dir().AppendASCII("good.crx"));
 
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index e0ce219..0421950 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1951,6 +1951,17 @@
 
 #endif  // defined(OS_ANDROID)
 
+//  In-Product Help flags
+
+#if defined(OS_ANDROID)
+
+const char kEnableIphDemoMode[] = "In-Product Help Demo Mode";
+
+const char kEnableIphDemoModeDescription[] =
+    "Enables In-Product Help demo mode on Android.";
+
+#endif  // defined(OS_ANDROID)
+
 //  Settings window flags
 
 const char kSettingsWindowName[] = "Show settings in a window";
@@ -2759,6 +2770,10 @@
 const char kOmniboxEntitySuggestionsDescription[] =
     "Enable receiving entity suggestions in Omnibox.";
 
+const char kPauseBackgroundTabsName[] = "Pause background tabs";
+const char kPauseBackgroundTabsDescription[] =
+    "Pause timers in background tabs after 5 minutes on desktop.";
+
 #endif  // defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) ||
         // defined(OS_WIN)
 
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index b0bae0c..09f2ab69 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2149,6 +2149,18 @@
 
 #endif  // defined(OS_ANDROID)
 
+//  In-Product Help flags
+
+#if defined(OS_ANDROID)
+
+// The name of the In-Product Help demo mode in about:flags.
+extern const char kEnableIphDemoMode[];
+
+// Description of the In-Product Help demo mode in about:flags.
+extern const char kEnableIphDemoModeDescription[];
+
+#endif  // defined(OS_ANDROID)
+
 //  Settings window flags
 
 // An about::flags experiment title to show settings in a separate window
@@ -2980,6 +2992,9 @@
 // Description of the flag that enables entity suggestions.
 extern const char kOmniboxEntitySuggestionsDescription[];
 
+extern const char kPauseBackgroundTabsName[];
+extern const char kPauseBackgroundTabsDescription[];
+
 #endif  // defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) ||
         // defined(OS_WIN)
 
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.cc
index d6370f98..c893493 100644
--- a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.cc
+++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.cc
@@ -6,16 +6,23 @@
 
 #include <stdint.h>
 
+#include <map>
+#include <string>
+
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
+#include "base/strings/string_piece.h"
 #include "base/values.h"
 #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h"
 #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/pref_names.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
+#include "components/data_reduction_proxy/core/browser/data_usage_store.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
@@ -29,7 +36,28 @@
 using base::android::ScopedJavaLocalRef;
 using data_reduction_proxy::DataReductionProxySettings;
 
-DataReductionProxySettingsAndroid::DataReductionProxySettingsAndroid() {
+namespace {
+
+constexpr size_t kBucketsPerDay =
+    24 * 60 / data_reduction_proxy::kDataUsageBucketLengthInMinutes;
+
+struct DataUsageForHost {
+  DataUsageForHost() : data_used(0), original_size(0) {}
+
+  int64_t data_used;
+  int64_t original_size;
+};
+
+}  // namespace
+
+DataReductionProxySettingsAndroid::DataReductionProxySettingsAndroid()
+    : weak_factory_(this) {}
+
+DataReductionProxySettingsAndroid::DataReductionProxySettingsAndroid(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj)
+    : weak_factory_(this) {
+  j_settings_obj_ = JavaObjectWeakGlobalRef(env, obj);
 }
 
 DataReductionProxySettingsAndroid::~DataReductionProxySettingsAndroid() {
@@ -66,6 +94,12 @@
   return Settings()->GetDataReductionLastUpdateTime();
 }
 
+void DataReductionProxySettingsAndroid::ClearDataSavingStatistics(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj) {
+  Settings()->ClearDataSavingStatistics();
+}
+
 base::android::ScopedJavaLocalRef<jobject>
 DataReductionProxySettingsAndroid::GetContentLengths(
     JNIEnv* env,
@@ -174,7 +208,63 @@
   return ConvertUTF8ToJavaString(env, event_store->SanitizedLastBypassEvent());
 }
 
+void DataReductionProxySettingsAndroid::QueryDataUsage(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& obj,
+    const JavaParamRef<jobject>& j_result_obj,
+    jint num_days) {
+  DCHECK(num_days <= data_reduction_proxy::kDataUsageHistoryNumDays);
+  j_query_result_obj_.Reset(env, j_result_obj);
+  num_day_for_query_ = num_days;
+  Settings()
+      ->data_reduction_proxy_service()
+      ->compression_stats()
+      ->GetHistoricalDataUsage(base::Bind(
+          &DataReductionProxySettingsAndroid::OnQueryDataUsageComplete,
+          weak_factory_.GetWeakPtr()));
+}
+
+void DataReductionProxySettingsAndroid::OnQueryDataUsageComplete(
+    std::unique_ptr<std::vector<data_reduction_proxy::DataUsageBucket>>
+        data_usage) {
+  if (j_query_result_obj_.is_null())
+    return;
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+
+  std::map<base::StringPiece, DataUsageForHost> per_site_usage_map;
+
+  // Data usage is sorted chronologically with the last entry corresponding to
+  // |base::Time::Now()|.
+  const size_t num_buckets_to_display = num_day_for_query_ * kBucketsPerDay;
+  for (auto data_usage_it = data_usage->size() > num_buckets_to_display
+                                ? data_usage->end() - num_buckets_to_display
+                                : data_usage->begin();
+       data_usage_it != data_usage->end(); ++data_usage_it) {
+    for (const auto& connection_usage : data_usage_it->connection_usage()) {
+      for (const auto& site_usage : connection_usage.site_usage()) {
+        DataUsageForHost& usage = per_site_usage_map[site_usage.hostname()];
+        usage.data_used += site_usage.data_used();
+        usage.original_size += site_usage.original_size();
+      }
+    }
+  }
+
+  for (const auto& site_bucket : per_site_usage_map) {
+    Java_DataReductionProxySettings_createDataUseItemAndAddToList(
+        env, j_query_result_obj_.obj(),
+        ConvertUTF8ToJavaString(env, site_bucket.first),
+        site_bucket.second.data_used, site_bucket.second.original_size);
+  }
+
+  Java_DataReductionProxySettings_onQueryDataUsageComplete(
+      env, j_settings_obj_.get(env), j_query_result_obj_.obj());
+
+  j_query_result_obj_.Release();
+}
+
 // Used by generated jni code.
 static jlong Init(JNIEnv* env, const JavaParamRef<jobject>& obj) {
-  return reinterpret_cast<intptr_t>(new DataReductionProxySettingsAndroid());
+  return reinterpret_cast<intptr_t>(
+      new DataReductionProxySettingsAndroid(env, obj));
 }
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.h b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.h
index 1d29614..835a5b7 100644
--- a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.h
+++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.h
@@ -5,14 +5,20 @@
 #ifndef CHROME_BROWSER_NET_SPDYPROXY_DATA_REDUCTION_PROXY_SETTINGS_ANDROID_H_
 #define CHROME_BROWSER_NET_SPDYPROXY_DATA_REDUCTION_PROXY_SETTINGS_ANDROID_H_
 
-#include <memory>
+#include <jni.h>
 
+#include <memory>
+#include <vector>
+
+#include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "base/android/jni_weak_ref.h"
 #include "base/android/scoped_java_ref.h"
 #include "base/compiler_specific.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/data_reduction_proxy/proto/data_store.pb.h"
 #include "components/prefs/pref_member.h"
 
 using base::android::ScopedJavaLocalRef;
@@ -28,7 +34,9 @@
 // be called from there.
 class DataReductionProxySettingsAndroid {
  public:
-  DataReductionProxySettingsAndroid();
+  DataReductionProxySettingsAndroid(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj);
 
   virtual ~DataReductionProxySettingsAndroid();
 
@@ -51,6 +59,9 @@
   jlong GetDataReductionLastUpdateTime(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
+  void ClearDataSavingStatistics(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj);
   jlong GetTotalHttpContentLengthSaved(
         JNIEnv* env,
         const base::android::JavaParamRef<jobject>& obj);
@@ -96,19 +107,39 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
 
+  // Gets the historical data usage for |numDays| and adds them to a list that
+  // groups data use by hostname.
+  void QueryDataUsage(JNIEnv* env,
+                      const base::android::JavaParamRef<jobject>& obj,
+                      const base::android::JavaParamRef<jobject>& j_result_obj,
+                      jint num_days);
+  void OnQueryDataUsageComplete(
+      std::unique_ptr<std::vector<data_reduction_proxy::DataUsageBucket>>
+          data_usage);
+
   // Registers the native methods to be call from Java.
   static bool Register(JNIEnv* env);
 
+  JavaObjectWeakGlobalRef j_settings_obj_;
+  base::android::ScopedJavaGlobalRef<jobject> j_query_result_obj_;
+  int num_day_for_query_;
+
  private:
   friend class DataReductionProxySettingsAndroidTest;
+  friend class TestDataReductionProxySettingsAndroid;
   FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsAndroidTest,
                            TestGetDailyContentLengths);
 
+  // For testing purposes.
+  DataReductionProxySettingsAndroid();
+
   ScopedJavaLocalRef<jlongArray> GetDailyContentLengths(JNIEnv* env,
                                                         const char* pref_name);
 
   virtual data_reduction_proxy::DataReductionProxySettings* Settings();
 
+  base::WeakPtrFactory<DataReductionProxySettingsAndroid> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(DataReductionProxySettingsAndroid);
 };
 
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/command_handler.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/command_handler.js
index 9cc9cd7..fd27489 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/command_handler.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/command_handler.js
@@ -860,4 +860,36 @@
   }
 };
 
+/**
+ * Performs global initialization.
+ * @private
+ */
+CommandHandler.init_ = function() {
+  var firstRunId = 'jdgcneonijmofocbhmijhacgchbihela';
+  chrome.runtime.onMessageExternal.addListener(
+      function(request, sender, sendResponse) {
+        if (sender.id != firstRunId)
+          return;
+
+        if (request.openTutorial) {
+          var launchTutorial = function(desktop, evt) {
+            desktop.removeEventListener(
+                chrome.automation.EventType.FOCUS, launchTutorial, true);
+            CommandHandler.onCommand('help');
+          };
+
+          // Since we get this command early on ChromeVox launch, the first run
+          // UI is not yet shown. Monitor for when first run gets focused, and
+          // show our tutorial.
+          chrome.automation.getDesktop(function(desktop) {
+            launchTutorial = launchTutorial.bind(this, desktop);
+            desktop.addEventListener(
+                chrome.automation.EventType.FOCUS, launchTutorial, true);
+          });
+        }
+      });
+};
+
+CommandHandler.init_();
+
 }); //  goog.scope
diff --git a/chrome/browser/resources/chromeos/first_run/app/main.html b/chrome/browser/resources/chromeos/first_run/app/main.html
index 32e52a3..8ce5527 100644
--- a/chrome/browser/resources/chromeos/first_run/app/main.html
+++ b/chrome/browser/resources/chromeos/first_run/app/main.html
@@ -2,7 +2,7 @@
 <html i18n-values="dir:textdirection;.style.fontFamily:fontfamily;lang:language">
   <head>
     <meta charset=utf-8>
-    <title></title>
+    <title i18n-content="accessibleTitle"></title>
     <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
     <link rel="stylesheet" href="chrome://resources/css/widgets.css">
     <link rel="stylesheet" href="chrome://resources/css/apps/common.css">
diff --git a/chrome/browser/resources/chromeos/first_run/app/main.js b/chrome/browser/resources/chromeos/first_run/app/main.js
index e1afc5b..893b7a0 100644
--- a/chrome/browser/resources/chromeos/first_run/app/main.js
+++ b/chrome/browser/resources/chromeos/first_run/app/main.js
@@ -31,6 +31,14 @@
     appWindow.close();
     e.stopPropagation();
   });
+
+  // If spoken feedback is enabled, also show its tutorial.
+  chrome.accessibilityFeatures.spokenFeedback.get({}, function(details) {
+    if (details.value) {
+      var chromeVoxId = 'mndnfokpggljbaajbnioimlmbfngpief';
+      chrome.runtime.sendMessage(chromeVoxId, {openTutorial: true});
+    }
+  });
 }
 
 document.addEventListener('DOMContentLoaded', init);
diff --git a/chrome/browser/resources/chromeos/first_run/app/manifest.json b/chrome/browser/resources/chromeos/first_run/app/manifest.json
index de6564b..b307748 100644
--- a/chrome/browser/resources/chromeos/first_run/app/manifest.json
+++ b/chrome/browser/resources/chromeos/first_run/app/manifest.json
@@ -20,6 +20,7 @@
     "content_security_policy": "default-src 'none'; script-src 'self' blob: filesystem: chrome://resources; style-src 'self' blob: filesystem: 'unsafe-inline' chrome://resources; img-src 'self' blob: filesystem: chrome://theme chrome://resources"
   },
   "permissions": [
+    "accessibilityFeatures.read",
     "firstRunPrivate",
     "chrome://theme/",
     "chrome://resources/"
diff --git a/chrome/browser/resources/chromeos/first_run/first_run.html b/chrome/browser/resources/chromeos/first_run/first_run.html
index 29fe952b..184c631f 100644
--- a/chrome/browser/resources/chromeos/first_run/first_run.html
+++ b/chrome/browser/resources/chromeos/first_run/first_run.html
@@ -2,7 +2,7 @@
 <html i18n-values="dir:textdirection;shelf:shelfAlignment;.style.fontFamily:fontfamily;">
   <head>
     <meta charset=utf-8>
-    <title></title>
+    <title i18n-content="accessibleTitle"></title>
     <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
     <link rel="stylesheet" href="chrome://resources/css/widgets.css">
     <link rel="stylesheet" href="chrome://resources/css/apps/common.css">
@@ -28,4 +28,3 @@
     <script src="chrome://resources/js/i18n_template.js"></script>
   </body>
 </html>
-
diff --git a/chrome/browser/resources/engagement/site_engagement.html b/chrome/browser/resources/engagement/site_engagement.html
index 4a519fa..9a7f960 100644
--- a/chrome/browser/resources/engagement/site_engagement.html
+++ b/chrome/browser/resources/engagement/site_engagement.html
@@ -61,7 +61,7 @@
       box-shadow: 0 0 2px rgb(113, 158, 206);
       outline: none;
     }
-    
+
     table tr:hover {
       background: rgb(255, 255, 187);
     }
@@ -83,7 +83,7 @@
         <th sort-key="origin">
           Origin
         </th>
-        <th sort-key="score" class="sort-column" sort-reverse>
+        <th sort-key="total_score" class="sort-column" sort-reverse>
           Points
         </th>
       </tr>
diff --git a/chrome/browser/resources/engagement/site_engagement.js b/chrome/browser/resources/engagement/site_engagement.js
index 8dab46a..0aaaf60 100644
--- a/chrome/browser/resources/engagement/site_engagement.js
+++ b/chrome/browser/resources/engagement/site_engagement.js
@@ -16,18 +16,18 @@
 }
 
 define('main', [
-    'chrome/browser/engagement/site_engagement.mojom',
+    'chrome/browser/engagement/site_engagement_details.mojom',
     'content/public/renderer/frame_interfaces',
 ], (siteEngagementMojom, frameInterfaces) => {
   return () => {
-    var uiHandler = new siteEngagementMojom.SiteEngagementUIHandlerPtr(
+    var uiHandler = new siteEngagementMojom.SiteEngagementDetailsProviderPtr(
         frameInterfaces.getInterface(
-            siteEngagementMojom.SiteEngagementUIHandler.name));
+            siteEngagementMojom.SiteEngagementDetailsProvider.name));
 
     var engagementTableBody = $('engagement-table-body');
     var updateInterval = null;
     var info = null;
-    var sortKey = 'score';
+    var sortKey = 'total_score';
     var sortReverse = true;
 
     // Set table header sort handlers.
@@ -55,7 +55,7 @@
 
     /**
      * Creates a single row in the engagement table.
-     * @param {SiteEngagementInfo} info The info to create the row from.
+     * @param {SiteEngagementDetails} info The info to create the row from.
      * @return {HTMLElement}
      */
     function createRow(info) {
@@ -67,13 +67,13 @@
           'change', handleScoreChange.bind(undefined, info.origin));
       scoreInput.addEventListener('focus', disableAutoupdate);
       scoreInput.addEventListener('blur', enableAutoupdate);
-      scoreInput.value = info.score;
+      scoreInput.value = info.total_score;
 
       var scoreCell = createElementWithClassName('td', 'score-cell');
       scoreCell.appendChild(scoreInput);
 
       var engagementBar = createElementWithClassName('div', 'engagement-bar');
-      engagementBar.style.width = (info.score * 4) + 'px';
+      engagementBar.style.width = (info.total_score * 4) + 'px';
 
       var engagementBarCell =
           createElementWithClassName('td', 'engagement-bar-cell');
@@ -111,7 +111,7 @@
      */
     function handleScoreChange(origin, e) {
       var scoreInput = e.target;
-      uiHandler.setSiteEngagementScoreForOrigin(origin, scoreInput.value);
+      uiHandler.setSiteEngagementScoreForUrl(origin, scoreInput.value);
       scoreInput.barCellRef.style.width = (scoreInput.value * 4) + 'px';
       scoreInput.blur();
       enableAutoupdate();
@@ -135,7 +135,7 @@
     }
 
     /**
-     * Compares two SiteEngagementInfo objects based on |sortKey|.
+     * Compares two SiteEngagementDetails objects based on |sortKey|.
      * @param {string} sortKey The name of the property to sort by.
      * @return {number} A negative number if |a| should be ordered before |b|, a
      * positive number otherwise.
@@ -148,7 +148,7 @@
       if (sortKey == 'origin')
         return new URL(val1.url).host > new URL(val2.url).host ? 1 : -1;
 
-      if (sortKey == 'score')
+      if (sortKey == 'total_score')
         return val1 - val2;
 
       assertNotReached('Unsupported sort key: ' + sortKey);
@@ -163,7 +163,7 @@
       sortInfo();
       // Round each score to 2 decimal places.
       info.forEach((info) => {
-        info.score = Number(Math.round(info.score * 100) / 100);
+        info.total_score = Number(Math.round(info.total_score * 100) / 100);
         engagementTableBody.appendChild(createRow(info));
       });
 
@@ -175,7 +175,7 @@
      */
     function updateEngagementTable() {
       // Populate engagement table.
-      uiHandler.getSiteEngagementInfo().then((response) => {
+      uiHandler.getSiteEngagementDetails().then((response) => {
         info = response.info;
         renderTable(info);
       });
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.html b/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
index a4b27a2..7dee54ee 100644
--- a/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
+++ b/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
@@ -184,6 +184,26 @@
           aria-label="$i18n{siteSettingsPopups}"
           aria-describedby="popupsSecondary"></button>
     </div>
+    <template is="dom-if" if="[[enableSafeBrowsingSubresourceFilter_]]">
+      <div class="settings-box two-line"
+          category$="[[ContentSettingsTypes.SUBRESOURCE_FILTER]]"
+          data-route="SITE_SETTINGS_SUBRESOURCE_FILTER" on-tap="onTapNavigate_"
+          actionable>
+        <iron-icon icon="cr:open-in-new"></iron-icon>
+        <div class="middle">
+          $i18n{siteSettingsSubresourceFilter}
+          <div class="secondary" id="subresourceFilterSecondary">
+            [[defaultSettingLabel_(
+                default_.subresourceFilter,
+                '$i18nPolymer{siteSettingsSubresourceFilterBlock}',
+                '$i18nPolymer{siteSettingsSubresourceFilterAllow}')]]
+          </div>
+        </div>
+        <button class="subpage-arrow" is="paper-icon-button-light"
+            aria-label="$i18n{siteSettingsSubresourceFilter}"
+            aria-describedby="subresourceFilterSecondary"></button>
+      </div>
+    </template>
     <div class="settings-box two-line"
         category$="[[ContentSettingsTypes.BACKGROUND_SYNC]]"
         data-route="SITE_SETTINGS_BACKGROUND_SYNC" on-tap="onTapNavigate_"
@@ -276,26 +296,6 @@
           aria-label="$i18n{siteSettingsMidiDevices}"
           aria-describedby="midiDevicesSecondary"></button>
     </div>
-    <template is="dom-if" if="[[enableSafeBrowsingSubresourceFilter_]]">
-      <div class="settings-box two-line"
-          category$="[[ContentSettingsTypes.SUBRESOURCE_FILTER]]"
-          data-route="SITE_SETTINGS_SUBRESOURCE_FILTER" on-tap="onTapNavigate_"
-          actionable>
-        <iron-icon icon="cr:open-in-new"></iron-icon>
-        <div class="middle">
-          $i18n{siteSettingsSubresourceFilter}
-          <div class="secondary" id="subresourceFilterSecondary">
-            [[defaultSettingLabel_(
-                default_.subresourceFilter,
-                '$i18nPolymer{siteSettingsSubresourceFilterBlock}',
-                '$i18nPolymer{siteSettingsSubresourceFilterAllow}')]]
-          </div>
-        </div>
-        <button class="subpage-arrow" is="paper-icon-button-light"
-            aria-label="$i18n{siteSettingsSubresourceFilter}"
-            aria-describedby="subresourceFilterSecondary"></button>
-      </div>
-    </template>
     <div class="settings-box" category$="[[ContentSettingsTypes.ZOOM_LEVELS]]"
         data-route="SITE_SETTINGS_ZOOM_LEVELS"
         on-tap="onTapNavigate_" actionable>
diff --git a/chrome/browser/safe_browsing/DEPS b/chrome/browser/safe_browsing/DEPS
new file mode 100644
index 0000000..1a00f99
--- /dev/null
+++ b/chrome/browser/safe_browsing/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+mojo",
+  "+components/chrome_cleaner",
+]
diff --git a/chrome/browser/safe_browsing/srt_chrome_prompt_impl.cc b/chrome/browser/safe_browsing/srt_chrome_prompt_impl.cc
new file mode 100644
index 0000000..4e60db0
--- /dev/null
+++ b/chrome/browser/safe_browsing/srt_chrome_prompt_impl.cc
@@ -0,0 +1,30 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/safe_browsing/srt_chrome_prompt_impl.h"
+
+#include "base/logging.h"
+
+namespace safe_browsing {
+
+using chrome_cleaner::mojom::ChromePrompt;
+using chrome_cleaner::mojom::ChromePromptRequest;
+using chrome_cleaner::mojom::PromptAcceptance;
+using chrome_cleaner::mojom::UwSPtr;
+
+ChromePromptImpl::ChromePromptImpl(ChromePromptRequest request)
+    : binding_(this, std::move(request)) {}
+
+ChromePromptImpl::~ChromePromptImpl() {}
+
+void ChromePromptImpl::PromptUser(
+    std::vector<UwSPtr> removable_uws_found,
+    bool elevation_required,
+    const ChromePrompt::PromptUserCallback& callback) {
+  // Placeholder. The actual implementation will show the prompt dialog to the
+  // user and invoke this callback depending on the user's response.
+  callback.Run(PromptAcceptance::DENIED);
+}
+
+}  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/srt_chrome_prompt_impl.h b/chrome/browser/safe_browsing/srt_chrome_prompt_impl.h
new file mode 100644
index 0000000..fbba20c
--- /dev/null
+++ b/chrome/browser/safe_browsing/srt_chrome_prompt_impl.h
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SAFE_BROWSING_SRT_CHROME_PROMPT_IMPL_H_
+#define CHROME_BROWSER_SAFE_BROWSING_SRT_CHROME_PROMPT_IMPL_H_
+
+#include "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace safe_browsing {
+
+// Implementation of the ChromePrompt Mojo interface.
+class ChromePromptImpl : public chrome_cleaner::mojom::ChromePrompt {
+ public:
+  explicit ChromePromptImpl(chrome_cleaner::mojom::ChromePromptRequest request);
+  ~ChromePromptImpl() override;
+
+  void PromptUser(
+      std::vector<chrome_cleaner::mojom::UwSPtr> removable_uws_found,
+      bool elevation_required,
+      const chrome_cleaner::mojom::ChromePrompt::PromptUserCallback& callback)
+      override;
+
+ private:
+  mojo::Binding<chrome_cleaner::mojom::ChromePrompt> binding_;
+};
+
+}  // namespace safe_browsing
+
+#endif  // CHROME_BROWSER_SAFE_BROWSING_SRT_CHROME_PROMPT_IMPL_H_
diff --git a/chrome/browser/safe_browsing/srt_fetcher_browsertest_win.cc b/chrome/browser/safe_browsing/srt_fetcher_browsertest_win.cc
index ad157be..60053da 100644
--- a/chrome/browser/safe_browsing/srt_fetcher_browsertest_win.cc
+++ b/chrome/browser/safe_browsing/srt_fetcher_browsertest_win.cc
@@ -5,18 +5,24 @@
 #include "chrome/browser/safe_browsing/srt_fetcher_win.h"
 
 #include <initializer_list>
-#include <iterator>
 #include <set>
+#include <tuple>
+#include <utility>
 #include <vector>
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/callback.h"
+#include "base/callback_helpers.h"
 #include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
 #include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/multiprocess_test.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_mock_time_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/version.h"
@@ -30,23 +36,197 @@
 #include "chrome/browser/ui/test/test_browser_dialog.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom.h"
 #include "components/component_updater/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing_db/safe_browsing_prefs.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/scoped_ipc_support.h"
+#include "mojo/edk/system/core.h"
+#include "testing/multiprocess_func_list.h"
 
 namespace safe_browsing {
 
 namespace {
 
-const char* const kExpectedSwitches[] = {kExtendedSafeBrowsingEnabledSwitch,
-                                         kChromeVersionSwitch,
-                                         kChromeChannelSwitch};
+using chrome_cleaner::mojom::PromptAcceptance;
 
-class SRTFetcherTest : public InProcessBrowserTest,
-                       public SwReporterTestingDelegate {
+// Special switches passed by the parent process (test case) to the reporter
+// child process to indicate the behavior that should be mocked.
+constexpr char kExitCodeToReturnSwitch[] = "exit-code-to-return";
+constexpr char kReportUwSFoundSwitch[] = "report-uws-found";
+constexpr char kReportElevationRequiredSwitch[] = "report-elevation-required";
+constexpr char kExpectedPromptAcceptanceSwitch[] = "expected-prompt-acceptance";
+
+// The exit code to be returned in case of failure in the child process.
+// This should never be passed as the expected exit code to be reported by
+// tests.
+constexpr int kFailureExitCode = -1;
+
+// Pass the |prompt_acceptance| to the mock child process in command line
+// switch kExpectedPromptAcceptanceSwitch.
+void AddPromptAcceptanceToCommandLine(PromptAcceptance prompt_acceptance,
+                                      base::CommandLine* command_line) {
+  command_line->AppendSwitchASCII(
+      kExpectedPromptAcceptanceSwitch,
+      base::IntToString(static_cast<int>(prompt_acceptance)));
+}
+
+// Parses and returns the prompt acceptance value passed by the parent process
+// in command line switch kExpectedPromptAcceptanceSwitch. Returns
+// PromptAcceptance::UNSPECIFIED if the switch doesn't exist or can't be
+// parsed to a valid PromptAcceptance enumerator.
+PromptAcceptance PromptAcceptanceFromCommandLine(
+    const base::CommandLine& command_line) {
+  const std::string& prompt_acceptance_str =
+      command_line.GetSwitchValueASCII(kExpectedPromptAcceptanceSwitch);
+  int val = -1;
+  if (base::StringToInt(prompt_acceptance_str, &val)) {
+    PromptAcceptance prompt_acceptance = static_cast<PromptAcceptance>(val);
+    if (chrome_cleaner::mojom::IsKnownEnumValue(prompt_acceptance))
+      return prompt_acceptance;
+  }
+  return PromptAcceptance::UNSPECIFIED;
+}
+
+// Pointer to ChromePromptPtr object to be used by the child process. The
+// object must be created, deleted, and accessed on the IPC thread only.
+chrome_cleaner::mojom::ChromePromptPtr* g_chrome_prompt_ptr = nullptr;
+
+// The callback function to be passed to ChromePrompt::PromptUser to check if
+// the prompt accepted returned by the parent process is equal to
+// |expected_prompt_acceptance|. Will set |expected_value_received| with the
+// comparison result, so that the main thread can report failure. Will invoke
+// |done| callback when done.
+void PromptUserCallback(const base::Closure& done,
+                        PromptAcceptance expected_prompt_acceptance,
+                        bool* expected_value_received,
+                        PromptAcceptance prompt_acceptance) {
+  *expected_value_received = prompt_acceptance == expected_prompt_acceptance;
+  // It's safe to delete the ChromePromptPtr object here, since it will not be
+  // used anymore by the child process.
+  delete g_chrome_prompt_ptr;
+  g_chrome_prompt_ptr = nullptr;
+  done.Run();
+}
+
+// Mocks the sending of scan results from the child process to the parent
+// process. Obtains the behavior to be mocked from special switches in
+// |command_line|. Sets |expected_value_received| as true if the parent
+// process replies with the expected prompt acceptance value.
+void SendScanResults(const std::string& chrome_mojo_pipe_token,
+                     const base::CommandLine& command_line,
+                     const base::Closure& done,
+                     bool* expected_value_received) {
+  constexpr int kDefaultUwSId = 10;
+  constexpr char kDefaultUwSName[] = "RemovedUwS";
+
+  mojo::ScopedMessagePipeHandle message_pipe_handle =
+      mojo::edk::CreateChildMessagePipe(chrome_mojo_pipe_token);
+  // This pointer will be deleted by PromptUserCallback.
+  g_chrome_prompt_ptr = new chrome_cleaner::mojom::ChromePromptPtr();
+  g_chrome_prompt_ptr->Bind(chrome_cleaner::mojom::ChromePromptPtrInfo(
+      std::move(message_pipe_handle), 0));
+
+  std::vector<chrome_cleaner::mojom::UwSPtr> removable_uws_found;
+  if (command_line.HasSwitch(kReportUwSFoundSwitch)) {
+    chrome_cleaner::mojom::UwSPtr uws = chrome_cleaner::mojom::UwS::New();
+    uws->id = kDefaultUwSId;
+    uws->name = kDefaultUwSName;
+    uws->observed_behaviours = chrome_cleaner::mojom::ObservedBehaviours::New();
+    removable_uws_found.push_back(std::move(uws));
+  }
+  const bool elevation_required =
+      command_line.HasSwitch(kReportElevationRequiredSwitch);
+  const PromptAcceptance expected_prompt_acceptance =
+      PromptAcceptanceFromCommandLine(command_line);
+
+  (*g_chrome_prompt_ptr)
+      ->PromptUser(
+          std::move(removable_uws_found), elevation_required,
+          base::Bind(&PromptUserCallback, done, expected_prompt_acceptance,
+                     expected_value_received));
+}
+
+// Connects to the parent process and sends mocked scan results. Returns true
+// if connection was successful and the prompt acceptance results sent by the
+// parent process are the same as expected.
+bool ConnectAndSendDataToParentProcess(
+    const std::string& chrome_mojo_pipe_token,
+    const base::CommandLine& command_line) {
+  DCHECK(!chrome_mojo_pipe_token.empty());
+
+  mojo::edk::Init();
+  base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
+  base::Thread io_thread("IPCThread");
+  if (!io_thread.StartWithOptions(options))
+    return false;
+  mojo::edk::ScopedIPCSupport ipc_support(
+      io_thread.task_runner(),
+      mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
+  mojo::edk::SetParentPipeHandleFromCommandLine();
+  base::MessageLoop message_loop;
+  base::RunLoop run_loop;
+  // After the response from the parent process is received, this will post a
+  // task to unblock the child process's main thread.
+  auto done = base::Bind(
+      [](scoped_refptr<base::SequencedTaskRunner> main_runner,
+         base::Closure quit_closure) {
+        main_runner->PostTask(FROM_HERE, std::move(quit_closure));
+      },
+      base::SequencedTaskRunnerHandle::Get(),
+      base::Passed(run_loop.QuitClosure()));
+
+  bool expected_value_received = false;
+  io_thread.task_runner()->PostTask(
+      FROM_HERE, base::Bind(&SendScanResults, chrome_mojo_pipe_token,
+                            command_line, done, &expected_value_received));
+  run_loop.Run();
+
+  return expected_value_received;
+}
+
+// Mocks a Software Reporter process that returns an exit code specified by
+// command line switch kExitCodeToReturnSwitch. If a Mojo IPC is available,
+// this will also connect to the parent process and send mocked scan results
+// to the parent process using data passed as command line switches.
+MULTIPROCESS_TEST_MAIN(MockSwReporterProcess) {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  const std::string& str =
+      command_line->GetSwitchValueASCII(kExitCodeToReturnSwitch);
+  const std::string& chrome_mojo_pipe_token =
+      command_line->GetSwitchValueASCII(kChromeMojoPipeTokenSwitch);
+  int exit_code_to_report = kFailureExitCode;
+  bool success = base::StringToInt(str, &exit_code_to_report) &&
+                 (chrome_mojo_pipe_token.empty() ||
+                  ConnectAndSendDataToParentProcess(chrome_mojo_pipe_token,
+                                                    *command_line));
+  return success ? exit_code_to_report : kFailureExitCode;
+}
+
+// Parameters for this test:
+//  - bool in_browser_cleaner_ui: indicates if InBrowserCleanerUI experiment
+//    is enabled; if so, the parent and the child processes will communicate
+//    via a Mojo IPC;
+//  - bool elevation_required: indicates if the scan results sent by the child
+//    process should consider that elevation will be required for cleanup.
+class SRTFetcherTest
+    : public InProcessBrowserTest,
+      public SwReporterTestingDelegate,
+      public ::testing::WithParamInterface<std::tuple<bool, bool>> {
  public:
   void SetUpInProcessBrowserTestFixture() override {
     SetSwReporterTestingDelegate(this);
+
+    std::tie(in_browser_cleaner_ui_, elevation_required_) = GetParam();
+    // The config should only accept elevation_required_ if InBrowserCleanerUI
+    // feature is enabled.
+    ASSERT_TRUE(!elevation_required_ || in_browser_cleaner_ui_);
+
+    if (in_browser_cleaner_ui_)
+      scoped_feature_list_.InitAndEnableFeature(kInBrowserCleanerUIFeature);
+    else
+      scoped_feature_list_.InitAndDisableFeature(kInBrowserCleanerUIFeature);
   }
 
   void SetUpOnMainThread() override {
@@ -80,14 +260,37 @@
     prompt_trigger_called_ = true;
   }
 
-  // Records that the reporter was launched with the parameters given in
-  // |invocation|.
-  int LaunchReporter(const SwReporterInvocation& invocation) override {
+  // Spawns and returns a subprocess to mock an execution of the reporter with
+  // the parameters given in |invocation| that will return
+  // |exit_code_to_report_|. If IPC communication needs to be mocked, this will
+  // also provide values that define the expected behavior of the child
+  // process.
+  // Records the launch and parameters used for further verification.
+  base::Process LaunchReporter(
+      const SwReporterInvocation& invocation,
+      const base::LaunchOptions& launch_options) override {
     ++reporter_launch_count_;
     reporter_launch_parameters_.push_back(invocation);
     if (first_launch_callback_)
       std::move(first_launch_callback_).Run();
-    return exit_code_to_report_;
+
+    base::CommandLine command_line(
+        base::GetMultiProcessTestChildBaseCommandLine());
+    command_line.AppendArguments(invocation.command_line,
+                                 /*include_program=*/false);
+    command_line.AppendSwitchASCII(kExitCodeToReturnSwitch,
+                                   base::IntToString(exit_code_to_report_));
+    if (in_browser_cleaner_ui_) {
+      AddPromptAcceptanceToCommandLine(PromptAcceptance::DENIED, &command_line);
+      if (exit_code_to_report_ == kSwReporterCleanupNeeded) {
+        command_line.AppendSwitch(kReportUwSFoundSwitch);
+        if (elevation_required_)
+          command_line.AppendSwitch(kReportElevationRequiredSwitch);
+      }
+    }
+    base::SpawnChildResult result = base::SpawnMultiProcessTestChild(
+        "MockSwReporterProcess", command_line, launch_options);
+    return std::move(result.process);
   }
 
   // Returns the test's idea of the current time.
@@ -231,22 +434,20 @@
   }
 
   // Expects |invocation|'s command line to contain all the switches required
-  // for reporter logging.
+  // for reporter logging if and only if |expect_switches| is true.
   void ExpectLoggingSwitches(const SwReporterInvocation& invocation,
                              bool expect_switches) {
+    static const std::set<std::string> logging_switches{
+        kExtendedSafeBrowsingEnabledSwitch, kChromeVersionSwitch,
+        kChromeChannelSwitch};
+
     const base::CommandLine::SwitchMap& invocation_switches =
         invocation.command_line.GetSwitches();
-    std::set<std::string> expected_switches;
-    if (expect_switches)
-      expected_switches = {std::begin(kExpectedSwitches),
-                           std::end(kExpectedSwitches)};
-    EXPECT_EQ(expected_switches.size(), invocation_switches.size());
-    // Checks if all expected switches are in the invocation switches. It's not
-    // necessary to check if all invocation switches are expected, since we
-    // checked if both sets should have the same size.
-    for (const std::string& expected_switch : expected_switches) {
-      EXPECT_NE(invocation_switches.end(),
-                invocation_switches.find(expected_switch));
+    // Checks if switches that enable logging on the reporter are present on
+    // the invocation if and only if logging is allowed.
+    for (const std::string& logging_switch : logging_switches) {
+      EXPECT_EQ(expect_switches, invocation_switches.find(logging_switch) !=
+                                     invocation_switches.end());
     }
   }
 
@@ -257,6 +458,9 @@
   // The task runner that was in use before installing |mock_time_task_runner_|.
   scoped_refptr<base::SingleThreadTaskRunner> saved_task_runner_;
 
+  bool in_browser_cleaner_ui_;
+  bool elevation_required_;
+
   bool prompt_trigger_called_ = false;
   int reporter_launch_count_ = 0;
   std::vector<SwReporterInvocation> reporter_launch_parameters_;
@@ -266,6 +470,8 @@
   // can be used to perform actions in the middle of a queue of reporters which
   // all launch on the same mock clock tick.
   base::OnceClosure first_launch_callback_;
+
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 class SRTFetcherPromptTest : public DialogBrowserTest {
@@ -283,19 +489,19 @@
 
 }  // namespace
 
-IN_PROC_BROWSER_TEST_F(SRTFetcherTest, NothingFound) {
+IN_PROC_BROWSER_TEST_P(SRTFetcherTest, NothingFound) {
   RunReporter(kSwReporterNothingFound);
   ExpectReporterLaunches(0, 1, false);
   ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
 }
 
-IN_PROC_BROWSER_TEST_F(SRTFetcherTest, CleanupNeeded) {
+IN_PROC_BROWSER_TEST_P(SRTFetcherTest, CleanupNeeded) {
   RunReporter(kSwReporterCleanupNeeded);
   ExpectReporterLaunches(0, 1, true);
   ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
 }
 
-IN_PROC_BROWSER_TEST_F(SRTFetcherTest, RanRecently) {
+IN_PROC_BROWSER_TEST_P(SRTFetcherTest, RanRecently) {
   constexpr int kDaysLeft = 1;
   SetDaysSinceLastTriggered(kDaysBetweenSuccessfulSwReporterRuns - kDaysLeft);
   RunReporter(kSwReporterNothingFound);
@@ -306,7 +512,7 @@
 }
 
 // Test is flaky. crbug.com/705608
-IN_PROC_BROWSER_TEST_F(SRTFetcherTest, DISABLED_WaitForBrowser) {
+IN_PROC_BROWSER_TEST_P(SRTFetcherTest, DISABLED_WaitForBrowser) {
   Profile* profile = browser()->profile();
 
   // Ensure that even though we're closing the last browser, we don't enter the
@@ -346,13 +552,13 @@
   ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
 }
 
-IN_PROC_BROWSER_TEST_F(SRTFetcherTest, Failure) {
+IN_PROC_BROWSER_TEST_P(SRTFetcherTest, Failure) {
   RunReporter(kReporterFailureExitCode);
   ExpectReporterLaunches(0, 1, false);
   ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
 }
 
-IN_PROC_BROWSER_TEST_F(SRTFetcherTest, RunDaily) {
+IN_PROC_BROWSER_TEST_P(SRTFetcherTest, RunDaily) {
   PrefService* local_state = g_browser_process->local_state();
   local_state->SetBoolean(prefs::kSwReporterPendingPrompt, true);
   SetDaysSinceLastTriggered(kDaysBetweenSuccessfulSwReporterRuns - 1);
@@ -381,7 +587,7 @@
   ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
 }
 
-IN_PROC_BROWSER_TEST_F(SRTFetcherTest, ParameterChange) {
+IN_PROC_BROWSER_TEST_P(SRTFetcherTest, ParameterChange) {
   // If the reporter is run several times with different parameters, it should
   // only be launched once, with the last parameter set.
   const base::FilePath path1(L"path1");
@@ -438,7 +644,7 @@
   ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
 }
 
-IN_PROC_BROWSER_TEST_F(SRTFetcherTest, MultipleLaunches) {
+IN_PROC_BROWSER_TEST_P(SRTFetcherTest, MultipleLaunches) {
   const base::FilePath path1(L"path1");
   const base::FilePath path2(L"path2");
   const base::FilePath path3(L"path3");
@@ -506,8 +712,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(SRTFetcherTest, ReporterLogging_NoSBExtendedReporting) {
-  base::test::ScopedFeatureList scoped_feature_list;
+IN_PROC_BROWSER_TEST_P(SRTFetcherTest, ReporterLogging_NoSBExtendedReporting) {
   RunReporter(kSwReporterNothingFound);
   ExpectReporterLaunches(0, 1, false);
   ExpectLoggingSwitches(reporter_launch_parameters_.front(), false);
@@ -515,8 +720,7 @@
   ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
 }
 
-IN_PROC_BROWSER_TEST_F(SRTFetcherTest, ReporterLogging_EnabledFirstRun) {
-  base::test::ScopedFeatureList scoped_feature_list;
+IN_PROC_BROWSER_TEST_P(SRTFetcherTest, ReporterLogging_EnabledFirstRun) {
   EnableSBExtendedReporting();
   // Note: don't set last time sent logs in the local state.
   // SBER is enabled and there is no record in the local state of the last time
@@ -528,8 +732,7 @@
   ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
 }
 
-IN_PROC_BROWSER_TEST_F(SRTFetcherTest, ReporterLogging_EnabledNoRecentLogging) {
-  base::test::ScopedFeatureList scoped_feature_list;
+IN_PROC_BROWSER_TEST_P(SRTFetcherTest, ReporterLogging_EnabledNoRecentLogging) {
   // SBER is enabled and last time logs were sent was more than
   // |kDaysBetweenReporterLogsSent| day ago, so we should send logs in this run.
   EnableSBExtendedReporting();
@@ -541,8 +744,7 @@
   ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
 }
 
-IN_PROC_BROWSER_TEST_F(SRTFetcherTest, ReporterLogging_EnabledRecentlyLogged) {
-  base::test::ScopedFeatureList scoped_feature_list;
+IN_PROC_BROWSER_TEST_P(SRTFetcherTest, ReporterLogging_EnabledRecentlyLogged) {
   // SBER is enabled, but logs have been sent less than
   // |kDaysBetweenReporterLogsSent| day ago, so we shouldn't send any logs in
   // this run.
@@ -556,8 +758,7 @@
   ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
 }
 
-IN_PROC_BROWSER_TEST_F(SRTFetcherTest, ReporterLogging_MultipleLaunches) {
-  base::test::ScopedFeatureList scoped_feature_list;
+IN_PROC_BROWSER_TEST_P(SRTFetcherTest, ReporterLogging_MultipleLaunches) {
   EnableSBExtendedReporting();
   SetLastTimeSentReport(kDaysBetweenReporterLogsSent + 3);
 
@@ -593,6 +794,16 @@
   ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
 }
 
+INSTANTIATE_TEST_CASE_P(NoInBrowserCleanerUI,
+                        SRTFetcherTest,
+                        testing::Combine(testing::Values(false),
+                                         testing::Values(false)));
+
+INSTANTIATE_TEST_CASE_P(InBrowserCleanerUI,
+                        SRTFetcherTest,
+                        testing::Combine(testing::Values(true),
+                                         testing::Bool()));
+
 // This provide tests which allows explicit invocation of the SRT Prompt
 // useful for checking dialog layout or any other interactive functionality
 // tests. See docs/testing/test_browser_dialog.md for description of the
diff --git a/chrome/browser/safe_browsing/srt_fetcher_win.cc b/chrome/browser/safe_browsing/srt_fetcher_win.cc
index f2f9835..f3509738 100644
--- a/chrome/browser/safe_browsing/srt_fetcher_win.cc
+++ b/chrome/browser/safe_browsing/srt_fetcher_win.cc
@@ -22,7 +22,6 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/sparse_histogram.h"
-#include "base/process/launch.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -36,6 +35,7 @@
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_io_data.h"
+#include "chrome/browser/safe_browsing/srt_chrome_prompt_impl.h"
 #include "chrome/browser/safe_browsing/srt_client_info_win.h"
 #include "chrome/browser/safe_browsing/srt_global_error_win.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -44,12 +44,17 @@
 #include "chrome/browser/ui/global_error/global_error_service.h"
 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
 #include "chrome/common/pref_names.h"
+#include "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom.h"
 #include "components/component_updater/pref_names.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
 #include "components/prefs/pref_service.h"
 #include "components/variations/net/variations_http_headers.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/browser_thread.h"
+#include "mojo/edk/embedder/connection_params.h"
+#include "mojo/edk/embedder/pending_process_connection.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "mojo/public/cpp/system/message_pipe.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_status_code.h"
 #include "net/url_request/url_fetcher.h"
@@ -70,6 +75,11 @@
 const wchar_t kEndTimeValueName[] = L"EndTime";
 const wchar_t kStartTimeValueName[] = L"StartTime";
 
+const base::Feature kInBrowserCleanerUIFeature{
+    "InBrowserCleanerUI", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const char kChromeMojoPipeTokenSwitch[] = "chrome-mojo-pipe-token";
+
 namespace {
 
 // Used to send UMA information about missing start and end time registry
@@ -553,19 +563,83 @@
     global_error->ShowBubbleView(browser);
 }
 
-// This function is called from a worker thread to launch the SwReporter and
-// wait for termination to collect its exit code. This task could be
-// interrupted by a shutdown at any time, so it shouldn't depend on anything
-// external that could be shut down beforehand.
-int LaunchAndWaitForExit(const SwReporterInvocation& invocation) {
-  if (g_testing_delegate_)
-    return g_testing_delegate_->LaunchReporter(invocation);
+// Class responsible for launching the reporter process and waiting for its
+// completion. If feature InBrowserCleanerUI is enabled, this object will also
+// be responsible for starting the ChromePromptImpl object on the IO thread and
+// controlling its lifetime.
+//
+// Expected lifecycle of a SwReporterProcess:
+//  - created on the UI thread before the reporter process launch is posted
+//    (method ScheduleNextInvocation);
+//  - deleted on the UI thread once ReporterDone() finishes (the method is
+//    called after the reporter process exits).
+//
+// If feature InBrowserCleanerUI feature is enabled, the following tasks will
+// be posted in sequence to the IO Thread and will retain the SwReporterProcess
+// object:
+//  - creation of a ChromePromptImpl object right after the reporter process is
+//    launched (that object will be responsible for handling IPC requests from
+//    the reporter process);
+//  - deletion of the ChromePromptImpl object on ReporterDone().
+// As a consequence, the SwReporterProcess object can outlive ReporterDone()
+// and will only be deleted after the ChromePromptImpl object is released on
+// the IO thread.
+class SwReporterProcess : public base::RefCountedThreadSafe<SwReporterProcess> {
+ public:
+  explicit SwReporterProcess(const SwReporterInvocation& invocation)
+      : invocation_(invocation) {}
+
+  // This function is called from a worker thread to launch the SwReporter and
+  // wait for termination to collect its exit code. This task could be
+  // interrupted by a shutdown at any time, so it shouldn't depend on anything
+  // external that could be shut down beforehand.
+  int LaunchAndWaitForExitOnBackgroundThread();
+
+  // Schedules to release the instance of ChromePromptImpl on the IO thread.
+  void OnReporterDone();
+
+  const SwReporterInvocation& invocation() const { return invocation_; }
+
+ private:
+  friend class base::RefCountedThreadSafe<SwReporterProcess>;
+  ~SwReporterProcess() = default;
+
+  // Starts a new IPC service implementing the ChromePrompt interface and
+  // launches a new reporter process that can connect to the IPC.
+  base::Process LaunchConnectedReporterProcess();
+
+  // Starts a new instance of ChromePromptImpl to receive requests from the
+  // reporter. Must be run on the IO thread.
+  void CreateChromePromptImpl(
+      chrome_cleaner::mojom::ChromePromptRequest chrome_prompt_request);
+
+  // Releases the instance of ChromePromptImpl. Must be run on the IO thread.
+  void ReleaseChromePromptImpl();
+
+  // Launches a new process with the command line in the invocation and
+  // provided launch options. Uses g_testing_delegate_ if not null.
+  base::Process LaunchReporterProcess(
+      const SwReporterInvocation& invocation,
+      const base::LaunchOptions& launch_options);
+
+  // The invocation for the current reporter process.
+  SwReporterInvocation invocation_;
+
+  // Implementation of the ChromePrompt service to be used by the current
+  // reporter process. Can only be accessed on the IO thread.
+  std::unique_ptr<ChromePromptImpl> chrome_prompt_impl_;
+};
+
+int SwReporterProcess::LaunchAndWaitForExitOnBackgroundThread() {
   base::Process reporter_process =
-      base::LaunchProcess(invocation.command_line, base::LaunchOptions());
+      base::FeatureList::IsEnabled(kInBrowserCleanerUIFeature)
+          ? LaunchConnectedReporterProcess()
+          : LaunchReporterProcess(invocation_, base::LaunchOptions());
+
   // This exit code is used to identify that a reporter run didn't happen, so
   // the result should be ignored and a rerun scheduled for the usual delay.
   int exit_code = kReporterFailureExitCode;
-  UMAHistogramReporter uma(invocation.suffix);
+  UMAHistogramReporter uma(invocation_.suffix);
   if (reporter_process.IsValid()) {
     uma.RecordReporterStep(SW_REPORTER_START_EXECUTION);
     bool success = reporter_process.WaitForExit(&exit_code);
@@ -576,6 +650,81 @@
   return exit_code;
 }
 
+void SwReporterProcess::OnReporterDone() {
+  if (base::FeatureList::IsEnabled(kInBrowserCleanerUIFeature)) {
+    BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)
+        ->PostTask(FROM_HERE,
+                   base::Bind(&SwReporterProcess::ReleaseChromePromptImpl,
+                              base::RetainedRef(this)));
+  }
+}
+
+base::Process SwReporterProcess::LaunchConnectedReporterProcess() {
+  DCHECK(base::FeatureList::IsEnabled(kInBrowserCleanerUIFeature));
+
+  mojo::edk::PendingProcessConnection pending_process_connection;
+  std::string mojo_pipe_token;
+  mojo::ScopedMessagePipeHandle mojo_pipe =
+      pending_process_connection.CreateMessagePipe(&mojo_pipe_token);
+  invocation_.command_line.AppendSwitchASCII(kChromeMojoPipeTokenSwitch,
+                                             mojo_pipe_token);
+
+  mojo::edk::PlatformChannelPair channel;
+  base::HandlesToInheritVector handles_to_inherit;
+  channel.PrepareToPassClientHandleToChildProcess(&invocation_.command_line,
+                                                  &handles_to_inherit);
+
+  base::LaunchOptions launch_options;
+  launch_options.handles_to_inherit = &handles_to_inherit;
+  base::Process reporter_process =
+      LaunchReporterProcess(invocation_, launch_options);
+
+  if (!reporter_process.IsValid())
+    return reporter_process;
+
+  pending_process_connection.Connect(
+      reporter_process.Handle(),
+      mojo::edk::ConnectionParams(channel.PassServerHandle()));
+
+  chrome_cleaner::mojom::ChromePromptRequest chrome_prompt_request;
+  chrome_prompt_request.Bind(std::move(mojo_pipe));
+
+  // ChromePromptImpl tasks will need to run on the IO thread. There is no
+  // need to synchronize its creation, since the client end will wait for this
+  // initialization to be done before sending requests.
+  BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)
+      ->PostTask(FROM_HERE,
+                 base::BindOnce(&SwReporterProcess::CreateChromePromptImpl,
+                                base::RetainedRef(this),
+                                std::move(chrome_prompt_request)));
+
+  return reporter_process;
+}
+
+base::Process SwReporterProcess::LaunchReporterProcess(
+    const SwReporterInvocation& invocation,
+    const base::LaunchOptions& launch_options) {
+  return g_testing_delegate_
+             ? g_testing_delegate_->LaunchReporter(invocation, launch_options)
+             : base::LaunchProcess(invocation.command_line, launch_options);
+}
+
+void SwReporterProcess::CreateChromePromptImpl(
+    chrome_cleaner::mojom::ChromePromptRequest chrome_prompt_request) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(base::FeatureList::IsEnabled(kInBrowserCleanerUIFeature));
+
+  chrome_prompt_impl_ =
+      base::MakeUnique<ChromePromptImpl>(std::move(chrome_prompt_request));
+}
+
+void SwReporterProcess::ReleaseChromePromptImpl() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(base::FeatureList::IsEnabled(kInBrowserCleanerUIFeature));
+
+  chrome_prompt_impl_.release();
+}
+
 }  // namespace
 
 void DisplaySRTPromptForTesting(const base::FilePath& download_path) {
@@ -771,11 +920,17 @@
     base::TaskRunner* task_runner =
         g_testing_delegate_ ? g_testing_delegate_->BlockingTaskRunner()
                             : blocking_task_runner_.get();
-    base::PostTaskAndReplyWithResult(
-        task_runner, FROM_HERE,
-        base::Bind(&LaunchAndWaitForExit, next_invocation),
+    auto sw_reporter_process =
+        make_scoped_refptr(new SwReporterProcess(next_invocation));
+    auto launch_and_wait =
+        base::Bind(&SwReporterProcess::LaunchAndWaitForExitOnBackgroundThread,
+                   sw_reporter_process);
+    auto reporter_done =
         base::Bind(&ReporterRunner::ReporterDone, base::Unretained(this), Now(),
-                   version_, next_invocation));
+                   version_, std::move(sw_reporter_process));
+    base::PostTaskAndReplyWithResult(task_runner, FROM_HERE,
+                                     std::move(launch_and_wait),
+                                     std::move(reporter_done));
   }
 
   // This method is called on the UI thread when an invocation of the reporter
@@ -783,10 +938,12 @@
   // thread so should be resilient to unexpected shutdown.
   void ReporterDone(const base::Time& reporter_start_time,
                     const base::Version& version,
-                    const SwReporterInvocation& finished_invocation,
+                    scoped_refptr<SwReporterProcess> sw_reporter_process,
                     int exit_code) {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
+    sw_reporter_process->OnReporterDone();
+
     base::Time now = Now();
     base::TimeDelta reporter_running_time = now - reporter_start_time;
 
@@ -809,10 +966,12 @@
 
     // If the reporter failed to launch, do not process the results. (The exit
     // code itself doesn't need to be logged in this case because
-    // SW_REPORTER_FAILED_TO_START is logged in |LaunchAndWaitForExit|.)
+    // SW_REPORTER_FAILED_TO_START is logged in
+    // |LaunchAndWaitForExitOnBackgroundThread|.)
     if (exit_code == kReporterFailureExitCode)
       return;
 
+    const auto& finished_invocation = sw_reporter_process->invocation();
     UMAHistogramReporter uma(finished_invocation.suffix);
     uma.ReportVersion(version);
     uma.ReportExitCode(exit_code);
@@ -993,8 +1152,8 @@
 
   scoped_refptr<base::TaskRunner> blocking_task_runner_ =
       base::CreateTaskRunnerWithTraits(
-          // LaunchAndWaitForExit() creates (MayBlock()) and joins
-          // (WithBaseSyncPrimitives()) a process.
+          // LaunchAndWaitForExitOnBackgroundThread() creates (MayBlock()) and
+          // joins (WithBaseSyncPrimitives()) a process.
           base::TaskTraits()
               .WithShutdownBehavior(
                   base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)
diff --git a/chrome/browser/safe_browsing/srt_fetcher_win.h b/chrome/browser/safe_browsing/srt_fetcher_win.h
index 32f0ee40..a6e79e1 100644
--- a/chrome/browser/safe_browsing/srt_fetcher_win.h
+++ b/chrome/browser/safe_browsing/srt_fetcher_win.h
@@ -12,6 +12,9 @@
 #include <string>
 
 #include "base/command_line.h"
+#include "base/feature_list.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
 #include "base/time/time.h"
 
 namespace base {
@@ -44,6 +47,15 @@
 // The number of days to wait before sending out reporter logs.
 const int kDaysBetweenReporterLogsSent = 7;
 
+// When enabled, moves all user interaction with the Software Reporter and the
+// Chrome Cleanup tool to Chrome.
+extern const base::Feature kInBrowserCleanerUIFeature;
+
+// The switch to be passed to the Software Reporter process with the Mojo pipe
+// token for the IPC communication with Chrome.
+// TODO(crbug/709035) Move this to //components/chrome_cleaner.
+extern const char kChromeMojoPipeTokenSwitch[];
+
 // Parameters used to invoke the sw_reporter component.
 struct SwReporterInvocation {
   base::CommandLine command_line;
@@ -113,7 +125,9 @@
   virtual ~SwReporterTestingDelegate() {}
 
   // Test mock for launching the reporter.
-  virtual int LaunchReporter(const SwReporterInvocation& invocation) = 0;
+  virtual base::Process LaunchReporter(
+      const SwReporterInvocation& invocation,
+      const base::LaunchOptions& launch_options) = 0;
 
   // Test mock for showing the prompt.
   virtual void TriggerPrompt(Browser* browser,
diff --git a/chrome/browser/translate/translate_ranker_factory.cc b/chrome/browser/translate/translate_ranker_factory.cc
index e86c3d2..fe44939 100644
--- a/chrome/browser/translate/translate_ranker_factory.cc
+++ b/chrome/browser/translate/translate_ranker_factory.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/translate/translate_ranker_factory.h"
 
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/keyed_service/core/keyed_service.h"
@@ -35,7 +36,7 @@
     content::BrowserContext* browser_context) const {
   return new TranslateRankerImpl(
       TranslateRankerImpl::GetModelPath(browser_context->GetPath()),
-      TranslateRankerImpl::GetModelURL());
+      TranslateRankerImpl::GetModelURL(), g_browser_process->ukm_service());
 }
 
 content::BrowserContext* TranslateRankerFactory::GetBrowserContextToUse(
diff --git a/chrome/browser/ui/cocoa/OWNERS b/chrome/browser/ui/cocoa/OWNERS
index 2b2fecee..ca6b82b 100644
--- a/chrome/browser/ui/cocoa/OWNERS
+++ b/chrome/browser/ui/cocoa/OWNERS
@@ -6,7 +6,6 @@
 mark@chromium.org
 rohitrao@chromium.org
 rsesek@chromium.org
-shess@chromium.org
 tapted@chromium.org
 thakis@chromium.org
 
diff --git a/chrome/browser/ui/cocoa/toolbar/OWNERS b/chrome/browser/ui/cocoa/toolbar/OWNERS
index ae0df7c..fdbfdb1 100644
--- a/chrome/browser/ui/cocoa/toolbar/OWNERS
+++ b/chrome/browser/ui/cocoa/toolbar/OWNERS
@@ -1,3 +1,2 @@
 avi@chromium.org
 rohitrao@chromium.org
-shess@chromium.org
diff --git a/chrome/browser/ui/webui/chromeos/first_run/first_run_ui.cc b/chrome/browser/ui/webui/chromeos/first_run/first_run_ui.cc
index 85a73c1..9601a74 100644
--- a/chrome/browser/ui/webui/chromeos/first_run/first_run_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/first_run/first_run_ui.cc
@@ -63,6 +63,9 @@
       "transitionsEnabled",
       base::CommandLine::ForCurrentProcess()->HasSwitch(
           chromeos::switches::kEnableFirstRunUITransitions));
+  localized_strings->SetString(
+      "accessibleTitle",
+      l10n_util::GetStringUTF16(IDS_FIRST_RUN_STEP_ACCESSIBLE_TITLE));
   ash::WmShelf* shelf =
       ash::WmShelf::ForWindow(ash::ShellPort::Get()->GetPrimaryRootWindow());
   std::string shelf_alignment;
@@ -109,4 +112,3 @@
 }
 
 }  // namespace chromeos
-
diff --git a/chrome/browser/ui/webui/engagement/site_engagement_ui.cc b/chrome/browser/ui/webui/engagement/site_engagement_ui.cc
index 2321fc1..d840187 100644
--- a/chrome/browser/ui/webui/engagement/site_engagement_ui.cc
+++ b/chrome/browser/ui/webui/engagement/site_engagement_ui.cc
@@ -20,42 +20,41 @@
 
 namespace {
 
-// Implementation of mojom::SiteEngagementUIHandler that gets information from
-// the
-// SiteEngagementService to provide data for the WebUI.
-class SiteEngagementUIHandlerImpl : public mojom::SiteEngagementUIHandler {
+// Implementation of mojom::SiteEngagementDetailsProvider that gets information
+// from the SiteEngagementService to provide data for the WebUI.
+class SiteEngagementDetailsProviderImpl
+    : public mojom::SiteEngagementDetailsProvider {
  public:
-  // SiteEngagementUIHandlerImpl is deleted when the supplied pipe is destroyed.
-  SiteEngagementUIHandlerImpl(
+  // Instance is deleted when the supplied pipe is destroyed.
+  SiteEngagementDetailsProviderImpl(
       Profile* profile,
-      mojo::InterfaceRequest<mojom::SiteEngagementUIHandler> request)
+      mojo::InterfaceRequest<mojom::SiteEngagementDetailsProvider> request)
       : profile_(profile), binding_(this, std::move(request)) {
     DCHECK(profile_);
   }
 
-  ~SiteEngagementUIHandlerImpl() override {}
+  ~SiteEngagementDetailsProviderImpl() override {}
 
-  // mojom::SiteEngagementUIHandler overrides:
-  void GetSiteEngagementInfo(
-      const GetSiteEngagementInfoCallback& callback) override {
+  // mojom::SiteEngagementDetailsProvider overrides:
+  void GetSiteEngagementDetails(
+      const GetSiteEngagementDetailsCallback& callback) override {
     SiteEngagementService* service = SiteEngagementService::Get(profile_);
-    std::map<GURL, double> score_map = service->GetScoreMap();
+    std::vector<mojom::SiteEngagementDetails> scores = service->GetAllDetails();
 
-    std::vector<mojom::SiteEngagementInfoPtr> engagement_info;
-    engagement_info.reserve(score_map.size());
-    for (const auto& info : score_map) {
-      mojom::SiteEngagementInfoPtr origin_info(
-          mojom::SiteEngagementInfo::New());
-      origin_info->origin = info.first;
-      origin_info->score = info.second;
+    std::vector<mojom::SiteEngagementDetailsPtr> engagement_info;
+    engagement_info.reserve(scores.size());
+    for (const auto& info : scores) {
+      mojom::SiteEngagementDetailsPtr origin_info(
+          mojom::SiteEngagementDetails::New());
+      *origin_info = std::move(info);
       engagement_info.push_back(std::move(origin_info));
     }
 
     callback.Run(std::move(engagement_info));
   }
 
-  void SetSiteEngagementScoreForOrigin(const GURL& origin,
-                                       double score) override {
+  void SetSiteEngagementBaseScoreForUrl(const GURL& origin,
+                                        double score) override {
     if (!origin.is_valid() || score < 0 ||
         score > SiteEngagementService::GetMaxPoints() || std::isnan(score)) {
       return;
@@ -69,21 +68,22 @@
   // The Profile* handed to us in our constructor.
   Profile* profile_;
 
-  mojo::Binding<mojom::SiteEngagementUIHandler> binding_;
+  mojo::Binding<mojom::SiteEngagementDetailsProvider> binding_;
 
-  DISALLOW_COPY_AND_ASSIGN(SiteEngagementUIHandlerImpl);
+  DISALLOW_COPY_AND_ASSIGN(SiteEngagementDetailsProviderImpl);
 };
 
 }  // namespace
 
 SiteEngagementUI::SiteEngagementUI(content::WebUI* web_ui)
-    : MojoWebUIController<mojom::SiteEngagementUIHandler>(web_ui) {
+    : MojoWebUIController<mojom::SiteEngagementDetailsProvider>(web_ui) {
   // Set up the chrome://site-engagement/ source.
   std::unique_ptr<content::WebUIDataSource> source(
       content::WebUIDataSource::Create(chrome::kChromeUISiteEngagementHost));
   source->AddResourcePath("site_engagement.js", IDR_SITE_ENGAGEMENT_JS);
-  source->AddResourcePath("chrome/browser/engagement/site_engagement.mojom",
-                          IDR_SITE_ENGAGEMENT_MOJO_JS);
+  source->AddResourcePath(
+      "chrome/browser/engagement/site_engagement_details.mojom",
+      IDR_SITE_ENGAGEMENT_MOJO_JS);
   source->AddResourcePath("url/mojo/url.mojom", IDR_URL_MOJO_JS);
   source->SetDefaultResource(IDR_SITE_ENGAGEMENT_HTML);
   source->UseGzip(std::unordered_set<std::string>());
@@ -93,7 +93,7 @@
 SiteEngagementUI::~SiteEngagementUI() {}
 
 void SiteEngagementUI::BindUIHandler(
-    mojo::InterfaceRequest<mojom::SiteEngagementUIHandler> request) {
-  ui_handler_.reset(new SiteEngagementUIHandlerImpl(
+    mojo::InterfaceRequest<mojom::SiteEngagementDetailsProvider> request) {
+  ui_handler_.reset(new SiteEngagementDetailsProviderImpl(
       Profile::FromWebUI(web_ui()), std::move(request)));
 }
diff --git a/chrome/browser/ui/webui/engagement/site_engagement_ui.h b/chrome/browser/ui/webui/engagement/site_engagement_ui.h
index 2580c89..98aa2c9 100644
--- a/chrome/browser/ui/webui/engagement/site_engagement_ui.h
+++ b/chrome/browser/ui/webui/engagement/site_engagement_ui.h
@@ -6,12 +6,12 @@
 #define CHROME_BROWSER_UI_WEBUI_ENGAGEMENT_SITE_ENGAGEMENT_UI_H_
 
 #include "base/macros.h"
-#include "chrome/browser/engagement/site_engagement.mojom.h"
+#include "chrome/browser/engagement/site_engagement_details.mojom.h"
 #include "chrome/browser/ui/webui/mojo_web_ui_controller.h"
 
 // The UI for chrome://site-engagement/.
 class SiteEngagementUI
-    : public MojoWebUIController<mojom::SiteEngagementUIHandler> {
+    : public MojoWebUIController<mojom::SiteEngagementDetailsProvider> {
  public:
   explicit SiteEngagementUI(content::WebUI* web_ui);
   ~SiteEngagementUI() override;
@@ -19,9 +19,10 @@
  private:
   // MojoWebUIController overrides:
   void BindUIHandler(
-      mojo::InterfaceRequest<mojom::SiteEngagementUIHandler> request) override;
+      mojo::InterfaceRequest<mojom::SiteEngagementDetailsProvider> request)
+      override;
 
-  std::unique_ptr<mojom::SiteEngagementUIHandler> ui_handler_;
+  std::unique_ptr<mojom::SiteEngagementDetailsProvider> ui_handler_;
 
   DISALLOW_COPY_AND_ASSIGN(SiteEngagementUI);
 };
diff --git a/chrome/browser/ui/webui/engagement/site_engagement_ui_browsertest.cc b/chrome/browser/ui/webui/engagement/site_engagement_ui_browsertest.cc
index b1d442e..7a437bd 100644
--- a/chrome/browser/ui/webui/engagement/site_engagement_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/engagement/site_engagement_ui_browsertest.cc
@@ -66,25 +66,50 @@
     ASSERT_TRUE(page_is_populated_);
   }
 
-  // Verifies that a row exists for the specified site URL.
-  void ExpectPageContainsUrl(const GURL& url) {
-    ASSERT_TRUE(page_is_populated_);
+  // Expects that there will be the specified number of rows.
+  int NumberOfRows() {
+    EXPECT_TRUE(page_is_populated_);
 
-    bool found_url = false;
-    ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+    int number_of_rows = -1;
+    EXPECT_TRUE(content::ExecuteScriptAndExtractInt(
         browser()->tab_strip_model()->GetActiveWebContents(),
-        base::JoinString(
-            {"var origin_cells = "
-             "    Array.from(document.getElementsByClassName('origin-cell'));"
-             "window.domAutomationController.send(origin_cells.reduce("
-             "    (found, element) => {"
-             "      return found || (element.innerHTML == '",
-             url.spec(),
-             "');"
-             "    }, false));"},
-            ""),
-        &found_url));
-    EXPECT_TRUE(found_url);
+        "window.domAutomationController.send("
+        "    document.getElementsByClassName('origin-cell').length);",
+        &number_of_rows));
+
+    return number_of_rows;
+  }
+
+  // Returns the origin URL at the specified zero-based row index.
+  std::string OriginUrlAtRow(int index) {
+    EXPECT_TRUE(page_is_populated_);
+
+    std::string origin_url;
+    EXPECT_TRUE(content::ExecuteScriptAndExtractString(
+        browser()->tab_strip_model()->GetActiveWebContents(),
+        base::JoinString({"window.domAutomationController.send("
+                          "    document.getElementsByClassName('origin-cell')[",
+                          base::IntToString(index), "].innerHTML);"},
+                         ""),
+        &origin_url));
+
+    return origin_url;
+  }
+
+  // Returns the stringified score at the specified zero-based row index.
+  std::string ScoreAtRow(int index) {
+    EXPECT_TRUE(page_is_populated_);
+
+    std::string score_string;
+    EXPECT_TRUE(content::ExecuteScriptAndExtractString(
+        browser()->tab_strip_model()->GetActiveWebContents(),
+        base::JoinString({"window.domAutomationController.send("
+                          "    document.getElementsByClassName('score-input')[",
+                          base::IntToString(index), "].value);"},
+                         ""),
+        &score_string));
+
+    return score_string;
   }
 
  private:
@@ -98,5 +123,17 @@
   NavigateToWebUi();
   WaitUntilPagePopulated();
 
-  ExpectPageContainsUrl(kExampleUrl);
+  EXPECT_EQ(1, NumberOfRows());
+  EXPECT_EQ(kExampleUrl, OriginUrlAtRow(0));
+}
+
+IN_PROC_BROWSER_TEST_F(SiteEngagementUiBrowserTest,
+                       ScoresHaveTwoDecimalPlaces) {
+  ResetBaseScore(kExampleUrl, 3.14159);
+
+  NavigateToWebUi();
+  WaitUntilPagePopulated();
+
+  EXPECT_EQ(1, NumberOfRows());
+  EXPECT_EQ("3.14", ScoreAtRow(0));
 }
diff --git a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
index a08eb77..8a8b009 100644
--- a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
@@ -16,9 +16,9 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chromeos/printing/fake_printer_discoverer.h"
 #include "chrome/browser/chromeos/printing/ppd_provider_factory.h"
 #include "chrome/browser/chromeos/printing/printer_configurer.h"
+#include "chrome/browser/chromeos/printing/printer_discoverer.h"
 #include "chrome/browser/chromeos/printing/printers_manager_factory.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/profiles/profile.h"
@@ -378,23 +378,16 @@
 }
 
 void CupsPrintersHandler::HandleStartDiscovery(const base::ListValue* args) {
-  if (!printer_discoverer_.get())
-    printer_discoverer_ = chromeos::PrinterDiscoverer::Create();
+  if (!printer_discoverer_.get()) {
+    printer_discoverer_ =
+        chromeos::PrinterDiscoverer::CreateForProfile(profile_);
+  }
 
   printer_discoverer_->AddObserver(this);
-  if (!printer_discoverer_->StartDiscovery()) {
-    CallJavascriptFunction("cr.webUIListenerCallback",
-                           base::Value("on-printer-discovery-failed"));
-    printer_discoverer_->RemoveObserver(this);
-  }
 }
 
 void CupsPrintersHandler::HandleStopDiscovery(const base::ListValue* args) {
-  if (printer_discoverer_.get()) {
-    printer_discoverer_->RemoveObserver(this);
-    printer_discoverer_->StopDiscovery();
-    printer_discoverer_.reset();
-  }
+  printer_discoverer_.reset();
 }
 
 void CupsPrintersHandler::OnPrintersFound(
@@ -402,16 +395,14 @@
   std::unique_ptr<base::ListValue> printers_list =
       base::MakeUnique<base::ListValue>();
   for (const auto& printer : printers) {
-    std::unique_ptr<base::DictionaryValue> printer_info =
-        GetPrinterInfo(printer);
-    printers_list->Append(std::move(printer_info));
+    printers_list->Append(GetPrinterInfo(printer));
   }
 
   CallJavascriptFunction("cr.webUIListenerCallback",
                          base::Value("on-printer-discovered"), *printers_list);
 }
 
-void CupsPrintersHandler::OnDiscoveryDone() {
+void CupsPrintersHandler::OnDiscoveryInitialScanDone() {
   CallJavascriptFunction("cr.webUIListenerCallback",
                          base::Value("on-printer-discovery-done"));
 }
diff --git a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
index 68f2cc1..847e001 100644
--- a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
@@ -88,7 +88,7 @@
 
   // chromeos::PrinterDiscoverer::Observer override:
   void OnPrintersFound(const std::vector<Printer>& printers) override;
-  void OnDiscoveryDone() override;
+  void OnDiscoveryInitialScanDone() override;
 
   // Invokes debugd to add the printer to CUPS.  If |ipp_everywhere| is true,
   // automatic configuration will be attempted  and |ppd_path| is ignored.
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 79e8128d..bbf02bf 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -115,6 +115,7 @@
     "page_load_metrics/page_track_decider.h",
     "partial_circular_buffer.cc",
     "partial_circular_buffer.h",
+    "pause_tabs_field_trial.h",
     "pref_names_util.cc",
     "pref_names_util.h",
     "prerender_types.h",
diff --git a/chrome/common/pause_tabs_field_trial.h b/chrome/common/pause_tabs_field_trial.h
new file mode 100644
index 0000000..bbc8f69
--- /dev/null
+++ b/chrome/common/pause_tabs_field_trial.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_PAUSE_TABS_FIELD_TRIAL_H_
+#define CHROME_COMMON_PAUSE_TABS_FIELD_TRIAL_H_
+
+#include "base/feature_list.h"
+
+namespace pausetabs {
+
+const base::Feature kFeature{"PauseBackgroundTabs",
+                             base::FEATURE_DISABLED_BY_DEFAULT};
+
+const char kFeatureName[] = "pause-background-tabs";
+
+// Mode values.
+const char kModeParamMinimal[] = "minimal";
+const char kModeParamLow[] = "low";
+const char kModeParamMedium[] = "medium";
+const char kModeParamHigh[] = "high";
+const char kModeParamMax[] = "max";
+
+}  // namespace pausetabs
+
+#endif  // CHROME_COMMON_PAUSE_TABS_FIELD_TRIAL_H_
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 0c4f7b2..76271e0 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -28,6 +28,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/crash_keys.h"
 #include "chrome/common/features.h"
+#include "chrome/common/pause_tabs_field_trial.h"
 #include "chrome/common/pepper_permission_util.h"
 #include "chrome/common/prerender_types.h"
 #include "chrome/common/render_messages.h"
@@ -1098,7 +1099,8 @@
 #if defined(OS_ANDROID)
   return true;
 #else
-  return false;
+  // TODO(ojan): Plumb the engagement values for this feature to WebViewImpl.
+  return base::FeatureList::IsEnabled(pausetabs::kFeature);
 #endif
 }
 
diff --git a/chrome/test/chromedriver/alert_commands.cc b/chrome/test/chromedriver/alert_commands.cc
index 682ab962..b4bd3c10 100644
--- a/chrome/test/chromedriver/alert_commands.cc
+++ b/chrome/test/chromedriver/alert_commands.cc
@@ -69,10 +69,22 @@
   if (!params.GetString("text", &text))
     return Status(kUnknownError, "missing or invalid 'text'");
 
-  if (!web_view->GetJavaScriptDialogManager()->IsDialogOpen())
+  JavaScriptDialogManager* dialog_manager =
+      web_view->GetJavaScriptDialogManager();
+
+  if (!dialog_manager->IsDialogOpen())
     return Status(kNoAlertOpen);
 
-  session->prompt_text.reset(new std::string(text));
+  std::string type;
+  Status status = dialog_manager->GetTypeOfDialog(&type);
+  if (status.IsError())
+    return status;
+
+  if (type == "prompt")
+    session->prompt_text.reset(new std::string(text));
+  else
+    return Status(kElementNotVisible,
+                  " User dialog does not have a text box input field.");
   return Status(kOk);
 }
 
diff --git a/chrome/test/chromedriver/chrome/javascript_dialog_manager.cc b/chrome/test/chromedriver/chrome/javascript_dialog_manager.cc
index 7eff15d..f8e72c1f 100644
--- a/chrome/test/chromedriver/chrome/javascript_dialog_manager.cc
+++ b/chrome/test/chromedriver/chrome/javascript_dialog_manager.cc
@@ -27,6 +27,14 @@
   return Status(kOk);
 }
 
+Status JavaScriptDialogManager::GetTypeOfDialog(std::string* type) {
+  if (!IsDialogOpen())
+    return Status(kNoAlertOpen);
+
+  *type = dialog_type_queue_.front();
+  return Status(kOk);
+}
+
 Status JavaScriptDialogManager::HandleDialog(bool accept,
                                              const std::string* text) {
   if (!IsDialogOpen())
@@ -50,11 +58,16 @@
   // response.
   if (unhandled_dialog_queue_.size())
     unhandled_dialog_queue_.pop_front();
+
+  if (dialog_type_queue_.size())
+    dialog_type_queue_.pop_front();
+
   return Status(kOk);
 }
 
 Status JavaScriptDialogManager::OnConnected(DevToolsClient* client) {
   unhandled_dialog_queue_.clear();
+  dialog_type_queue_.clear();
   base::DictionaryValue params;
   return client_->SendCommand("Page.enable", params);
 }
@@ -68,10 +81,17 @@
       return Status(kUnknownError, "dialog event missing or invalid 'message'");
 
     unhandled_dialog_queue_.push_back(message);
+
+    std::string type;
+    if (!params.GetString("type", &type))
+      return Status(kUnknownError, "dialog has invalid 'type'");
+
+    dialog_type_queue_.push_back(type);
   } else if (method == "Page.javascriptDialogClosed") {
     // Inspector only sends this event when all dialogs have been closed.
     // Clear the unhandled queue in case the user closed a dialog manually.
     unhandled_dialog_queue_.clear();
+    dialog_type_queue_.clear();
   }
   return Status(kOk);
 }
diff --git a/chrome/test/chromedriver/chrome/javascript_dialog_manager.h b/chrome/test/chromedriver/chrome/javascript_dialog_manager.h
index 598e1b45..04c25a9 100644
--- a/chrome/test/chromedriver/chrome/javascript_dialog_manager.h
+++ b/chrome/test/chromedriver/chrome/javascript_dialog_manager.h
@@ -29,6 +29,8 @@
 
   Status GetDialogMessage(std::string* message);
 
+  Status GetTypeOfDialog(std::string* type);
+
   Status HandleDialog(bool accept, const std::string* text);
 
   // Overridden from DevToolsEventListener:
@@ -45,6 +47,8 @@
   // the event, a script was injected via Inspector that triggered an alert.
   std::list<std::string> unhandled_dialog_queue_;
 
+  std::list<std::string> dialog_type_queue_;
+
   DISALLOW_COPY_AND_ASSIGN(JavaScriptDialogManager);
 };
 
diff --git a/chrome/test/chromedriver/chrome/javascript_dialog_manager_unittest.cc b/chrome/test/chromedriver/chrome/javascript_dialog_manager_unittest.cc
index ca163689..fd81238 100644
--- a/chrome/test/chromedriver/chrome/javascript_dialog_manager_unittest.cc
+++ b/chrome/test/chromedriver/chrome/javascript_dialog_manager_unittest.cc
@@ -28,6 +28,7 @@
   JavaScriptDialogManager manager(&client);
   base::DictionaryValue params;
   params.SetString("message", "hi");
+  params.SetString("type", "prompt");
   ASSERT_EQ(
       kOk,
       manager.OnEvent(&client, "Page.javascriptDialogOpening", params).code());
@@ -44,6 +45,7 @@
   JavaScriptDialogManager manager(&client);
   base::DictionaryValue params;
   params.SetString("message", "hi");
+  params.SetString("type", "prompt");
   ASSERT_EQ(
       kOk,
       manager.OnEvent(&client, "Page.javascriptDialogOpening", params).code());
@@ -57,6 +59,7 @@
   JavaScriptDialogManager manager(&client);
   base::DictionaryValue params;
   params.SetString("message", "hi");
+  params.SetString("type", "alert");
   ASSERT_EQ(
       kOk,
       manager.OnEvent(&client, "Page.javascriptDialogOpening", params).code());
@@ -113,6 +116,7 @@
   JavaScriptDialogManager manager(&client);
   base::DictionaryValue params;
   params.SetString("message", "hi");
+  params.SetString("type", "alert");
   ASSERT_FALSE(manager.IsDialogOpen());
   std::string message;
   ASSERT_EQ(kNoAlertOpen, manager.GetDialogMessage(&message).code());
@@ -123,6 +127,9 @@
   ASSERT_TRUE(manager.IsDialogOpen());
   ASSERT_EQ(kOk, manager.GetDialogMessage(&message).code());
   ASSERT_EQ("hi", message);
+  std::string type;
+  ASSERT_EQ(kOk, manager.GetTypeOfDialog(&type).code());
+  ASSERT_EQ("alert", type);
 
   client.set_closing_count(1);
   ASSERT_EQ(kOk, manager.HandleDialog(false, NULL).code());
@@ -136,23 +143,30 @@
   JavaScriptDialogManager manager(&client);
   base::DictionaryValue params;
   params.SetString("message", "1");
+  params.SetString("type", "confirm");
   ASSERT_EQ(
       kOk,
       manager.OnEvent(&client, "Page.javascriptDialogOpening", params).code());
   params.SetString("message", "2");
+  params.SetString("type", "alert");
   ASSERT_EQ(
       kOk,
       manager.OnEvent(&client, "Page.javascriptDialogOpening", params).code());
 
   std::string message;
+  std::string type;
   ASSERT_EQ(kOk, manager.GetDialogMessage(&message).code());
+  ASSERT_EQ(kOk, manager.GetTypeOfDialog(&type).code());
   ASSERT_TRUE(manager.IsDialogOpen());
   ASSERT_EQ("1", message);
+  ASSERT_EQ("confirm", type);
 
   ASSERT_EQ(kOk, manager.HandleDialog(false, NULL).code());
   ASSERT_TRUE(manager.IsDialogOpen());
   ASSERT_EQ(kOk, manager.GetDialogMessage(&message).code());
+  ASSERT_EQ(kOk, manager.GetTypeOfDialog(&type).code());
   ASSERT_EQ("2", message);
+  ASSERT_EQ("alert", type);
 
   client.set_closing_count(2);
   ASSERT_EQ(kOk, manager.HandleDialog(false, NULL).code());
@@ -166,6 +180,7 @@
   JavaScriptDialogManager manager(&client);
   base::DictionaryValue params;
   params.SetString("message", "hi");
+  params.SetString("type", "alert");
   ASSERT_FALSE(manager.IsDialogOpen());
   std::string message;
   ASSERT_EQ(kNoAlertOpen, manager.GetDialogMessage(&message).code());
@@ -176,6 +191,9 @@
   ASSERT_TRUE(manager.IsDialogOpen());
   ASSERT_EQ(kOk, manager.GetDialogMessage(&message).code());
   ASSERT_EQ("hi", message);
+  std::string type;
+  ASSERT_EQ(kOk, manager.GetTypeOfDialog(&type).code());
+  ASSERT_EQ("alert", type);
 
   ASSERT_EQ(
       kOk,
diff --git a/chrome/test/chromedriver/test/test_expectations b/chrome/test/chromedriver/test/test_expectations
index 3ada83e..2f0a982 100644
--- a/chrome/test/chromedriver/test/test_expectations
+++ b/chrome/test/chromedriver/test/test_expectations
@@ -13,7 +13,6 @@
 _REVISION_NEGATIVE_FILTER = {}
 _REVISION_NEGATIVE_FILTER['HEAD'] = [
     'AlertsTest.testIncludesAlertTextInUnhandledAlertException',
-    'AlertsTest.testSettingTheValueOfAnAlertThrows',
     'AlertsTest.testShouldGetTextOfAlertOpenedInSetTimeout',
     'AlertsTest.testShouldHandleAlertOnPageUnload',
     'AlertsTest.testShouldHandleAlertOnWindowClose',
diff --git a/chromeos/network/managed_network_configuration_handler_unittest.cc b/chromeos/network/managed_network_configuration_handler_unittest.cc
index 06c197c..abdf830 100644
--- a/chromeos/network/managed_network_configuration_handler_unittest.cc
+++ b/chromeos/network/managed_network_configuration_handler_unittest.cc
@@ -549,9 +549,7 @@
   EXPECT_EQ(1, policy_observer_.GetPoliciesAppliedCountAndReset());
 }
 
-// TODO(stevenjb): https://crbug.com/710241
-TEST_F(ManagedNetworkConfigurationHandlerTest,
-       DISABLED_PolicyApplicationRunning) {
+TEST_F(ManagedNetworkConfigurationHandlerTest, PolicyApplicationRunning) {
   InitializeStandardProfiles();
   EXPECT_CALL(*mock_profile_client_, GetProperties(_, _, _)).Times(AnyNumber());
   EXPECT_CALL(*mock_manager_client_, ConfigureServiceForProfile(_, _, _, _))
@@ -582,9 +580,7 @@
   EXPECT_FALSE(managed_handler()->IsAnyPolicyApplicationRunning());
 }
 
-// TODO(stevenjb): https://crbug.com/710241
-TEST_F(ManagedNetworkConfigurationHandlerTest,
-       DISABLED_UpdatePolicyAfterFinished) {
+TEST_F(ManagedNetworkConfigurationHandlerTest, UpdatePolicyAfterFinished) {
   InitializeStandardProfiles();
   EXPECT_CALL(*mock_profile_client_, GetProperties(_, _, _));
   EXPECT_CALL(*mock_manager_client_, ConfigureServiceForProfile(_, _, _, _));
@@ -690,9 +686,7 @@
   base::RunLoop().RunUntilIdle();
 }
 
-// TODO(stevenjb): https://crbug.com/710241
-TEST_F(ManagedNetworkConfigurationHandlerTest,
-       DISABLED_SetPolicyUpdateManagedNewGUID) {
+TEST_F(ManagedNetworkConfigurationHandlerTest, SetPolicyUpdateManagedNewGUID) {
   InitializeStandardProfiles();
   SetUpEntry("policy/shill_managed_wifi1.json",
              kUser1ProfilePath,
@@ -753,9 +747,7 @@
   VerifyAndClearExpectations();
 }
 
-// TODO(stevenjb): https://crbug.com/710241
-TEST_F(ManagedNetworkConfigurationHandlerTest,
-       DISABLED_SetPolicyReapplyToManaged) {
+TEST_F(ManagedNetworkConfigurationHandlerTest, SetPolicyReapplyToManaged) {
   InitializeStandardProfiles();
   SetUpEntry("policy/shill_policy_on_unmanaged_wifi1.json",
              kUser1ProfilePath,
diff --git a/chromeos/network/policy_util.cc b/chromeos/network/policy_util.cc
index b28f30b..ebea93e 100644
--- a/chromeos/network/policy_util.cc
+++ b/chromeos/network/policy_util.cc
@@ -35,18 +35,17 @@
 void RemoveFakeCredentials(
     const onc::OncValueSignature& signature,
     base::DictionaryValue* onc_object) {
-  base::DictionaryValue::Iterator it(*onc_object);
-  while (!it.IsAtEnd()) {
-    base::Value* value = NULL;
+  std::vector<std::string> entries_to_remove;
+  for (base::DictionaryValue::Iterator it(*onc_object); !it.IsAtEnd();
+       it.Advance()) {
+    base::Value* value = nullptr;
     std::string field_name = it.key();
     // We need the non-const entry to remove nested values but DictionaryValue
     // has no non-const iterator.
     onc_object->GetWithoutPathExpansion(field_name, &value);
-    // Advance before delete.
-    it.Advance();
 
     // If |value| is a dictionary, recurse.
-    base::DictionaryValue* nested_object = NULL;
+    base::DictionaryValue* nested_object = nullptr;
     if (value->GetAsDictionary(&nested_object)) {
       const onc::OncFieldSignature* field_signature =
           onc::GetFieldSignature(signature, field_name);
@@ -64,12 +63,14 @@
       if (string_value == kFakeCredential) {
         // The value wasn't modified by the UI, thus we remove the field to keep
         // the existing value that is stored in Shill.
-        onc_object->RemoveWithoutPathExpansion(field_name, NULL);
+        entries_to_remove.push_back(field_name);
       }
       // Otherwise, the value is set and modified by the UI, thus we keep that
       // value to overwrite whatever is stored in Shill.
     }
   }
+  for (auto field_name : entries_to_remove)
+    onc_object->RemoveWithoutPathExpansion(field_name, nullptr);
 }
 
 // Returns true if |policy| matches |actual_network|, which must be part of a
diff --git a/components/favicon/core/large_icon_service.cc b/components/favicon/core/large_icon_service.cc
index 5817bfcf..c202c244 100644
--- a/components/favicon/core/large_icon_service.cc
+++ b/components/favicon/core/large_icon_service.cc
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/stringprintf.h"
 #include "base/task_runner.h"
 #include "base/threading/sequenced_worker_pool.h"
@@ -217,6 +218,10 @@
       favicon_base::LargeIconImageResult(fallback_icon_style_.release()));
 }
 
+void ReportDownloadedSize(int size) {
+  UMA_HISTOGRAM_COUNTS_1000("Favicons.LargeIconService.DownloadedSize", size);
+}
+
 void OnFetchIconFromGoogleServerComplete(
     FaviconService* favicon_service,
     const GURL& page_url,
@@ -229,9 +234,12 @@
     favicon_service->UnableToDownloadFavicon(GURL(server_request_url));
     base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
                                                   base::Bind(callback, false));
+    ReportDownloadedSize(0);
     return;
   }
 
+  ReportDownloadedSize(image.Width());
+
   // If given, use the original favicon URL from Content-Location http header.
   // Otherwise, use the request URL as fallback.
   std::string original_icon_url = metadata.content_location_header;
diff --git a/components/favicon/core/large_icon_service_unittest.cc b/components/favicon/core/large_icon_service_unittest.cc
index 434baf1..bc27de1 100644
--- a/components/favicon/core/large_icon_service_unittest.cc
+++ b/components/favicon/core/large_icon_service_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/task/cancelable_task_tracker.h"
+#include "base/test/histogram_tester.h"
 #include "base/test/mock_callback.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/favicon/core/favicon_client.h"
@@ -35,6 +36,7 @@
 namespace favicon {
 namespace {
 
+using testing::IsEmpty;
 using testing::IsNull;
 using testing::Eq;
 using testing::NiceMock;
@@ -128,6 +130,7 @@
   NiceMock<MockImageFetcher>* mock_image_fetcher_;
   testing::NiceMock<MockFaviconService> mock_favicon_service_;
   LargeIconService large_icon_service_;
+  base::HistogramTester histogram_tester_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(LargeIconServiceTest);
@@ -157,6 +160,8 @@
 
   EXPECT_CALL(callback, Run(true));
   base::RunLoop().RunUntilIdle();
+  histogram_tester_.ExpectUniqueSample(
+      "Favicons.LargeIconService.DownloadedSize", 64, /*expected_count=*/1);
 }
 
 TEST_F(LargeIconServiceTest, ShouldGetFromGoogleServerWithOriginalUrl) {
@@ -225,6 +230,9 @@
 
   EXPECT_CALL(callback, Run(false));
   base::RunLoop().RunUntilIdle();
+  EXPECT_THAT(histogram_tester_.GetAllSamples(
+                  "Favicons.LargeIconService.DownloadedSize"),
+              IsEmpty());
 }
 
 TEST_F(LargeIconServiceTest, ShouldReportUnavailableIfFetchFromServerFails) {
@@ -250,9 +258,12 @@
 
   EXPECT_CALL(callback, Run(false));
   base::RunLoop().RunUntilIdle();
+  // Verify that download failure gets recorded.
+  histogram_tester_.ExpectUniqueSample(
+      "Favicons.LargeIconService.DownloadedSize", 0, /*expected_count=*/1);
 }
 
-TEST_F(LargeIconServiceTest, ShoutNotGetFromGoogleServerIfUnavailable) {
+TEST_F(LargeIconServiceTest, ShouldNotGetFromGoogleServerIfUnavailable) {
   ON_CALL(
       mock_favicon_service_,
       WasUnableToDownloadFavicon(GURL(
@@ -274,6 +285,9 @@
 
   EXPECT_CALL(callback, Run(false));
   base::RunLoop().RunUntilIdle();
+  EXPECT_THAT(histogram_tester_.GetAllSamples(
+                  "Favicons.LargeIconService.DownloadedSize"),
+              IsEmpty());
 }
 
 class LargeIconServiceGetterTest : public LargeIconServiceTest,
diff --git a/components/feature_engagement_tracker/internal/BUILD.gn b/components/feature_engagement_tracker/internal/BUILD.gn
index 662d630..fcdd0828 100644
--- a/components/feature_engagement_tracker/internal/BUILD.gn
+++ b/components/feature_engagement_tracker/internal/BUILD.gn
@@ -29,8 +29,12 @@
     "model.h",
     "model_impl.cc",
     "model_impl.h",
+    "never_condition_validator.cc",
+    "never_condition_validator.h",
     "once_condition_validator.cc",
     "once_condition_validator.h",
+    "single_invalid_configuration.cc",
+    "single_invalid_configuration.h",
     "store.h",
   ]
 
@@ -61,7 +65,9 @@
     "feature_engagement_tracker_impl_unittest.cc",
     "in_memory_store_unittest.cc",
     "model_impl_unittest.cc",
+    "never_condition_validator_unittest.cc",
     "once_condition_validator_unittest.cc",
+    "single_invalid_configuration_unittest.cc",
   ]
 
   deps = [
diff --git a/components/feature_engagement_tracker/internal/feature_constants.cc b/components/feature_engagement_tracker/internal/feature_constants.cc
index 7fb59c9..570ff107 100644
--- a/components/feature_engagement_tracker/internal/feature_constants.cc
+++ b/components/feature_engagement_tracker/internal/feature_constants.cc
@@ -8,6 +8,9 @@
 
 namespace feature_engagement_tracker {
 
+const base::Feature kIPHDemoMode{"IPH_DemoMode",
+                                 base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kIPHDummyFeature{"IPH_DummyFeature",
                                      base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl.cc b/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl.cc
index 5b48c708..24c499d 100644
--- a/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl.cc
+++ b/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl.cc
@@ -11,24 +11,37 @@
 #include "components/feature_engagement_tracker/internal/feature_list.h"
 #include "components/feature_engagement_tracker/internal/in_memory_store.h"
 #include "components/feature_engagement_tracker/internal/model_impl.h"
+#include "components/feature_engagement_tracker/internal/never_condition_validator.h"
 #include "components/feature_engagement_tracker/internal/once_condition_validator.h"
+#include "components/feature_engagement_tracker/internal/single_invalid_configuration.h"
+#include "components/feature_engagement_tracker/public/feature_constants.h"
 
 namespace feature_engagement_tracker {
 
-// Set up all feature configurations.
-// TODO(nyquist): Create FinchConfiguration to parse configuration.
-std::unique_ptr<Configuration> CreateAndParseConfiguration() {
+namespace {
+
+// Creates a FeatureEngagementTrackerImpl that is usable for a demo mode.
+std::unique_ptr<FeatureEngagementTracker>
+CreateDemoModeFeatureEngagementTracker() {
   std::unique_ptr<EditableConfiguration> configuration =
       base::MakeUnique<EditableConfiguration>();
+
+  // Create valid configurations for all features to ensure that the
+  // OnceConditionValidator acknowledges that thet meet conditions once.
   std::vector<const base::Feature*> features = GetAllFeatures();
-  for (auto* it : features) {
+  for (auto* feature : features) {
     FeatureConfig feature_config;
     feature_config.valid = true;
-    configuration->SetConfiguration(it, feature_config);
+    configuration->SetConfiguration(feature, feature_config);
   }
-  return std::move(configuration);
+
+  return base::MakeUnique<FeatureEngagementTrackerImpl>(
+      base::MakeUnique<InMemoryStore>(), std::move(configuration),
+      base::MakeUnique<OnceConditionValidator>());
 }
 
+}  // namespace
+
 // This method is declared in //components/feature_engagement_tracker/public/
 //     feature_engagement_tracker.h
 // and should be linked in to any binary using FeatureEngagementTracker::Create.
@@ -36,10 +49,15 @@
 FeatureEngagementTracker* FeatureEngagementTracker::Create(
     const base::FilePath& storage_dir,
     const scoped_refptr<base::SequencedTaskRunner>& background__task_runner) {
+  if (base::FeatureList::IsEnabled(kIPHDemoMode))
+    return CreateDemoModeFeatureEngagementTracker().release();
+
   std::unique_ptr<Store> store = base::MakeUnique<InMemoryStore>();
-  std::unique_ptr<Configuration> configuration = CreateAndParseConfiguration();
+  // TODO(nyquist): Create FinchConfiguration to parse configuration.
+  std::unique_ptr<Configuration> configuration =
+      base::MakeUnique<SingleInvalidConfiguration>();
   std::unique_ptr<ConditionValidator> validator =
-      base::MakeUnique<OnceConditionValidator>();
+      base::MakeUnique<NeverConditionValidator>();
 
   return new FeatureEngagementTrackerImpl(
       std::move(store), std::move(configuration), std::move(validator));
diff --git a/components/feature_engagement_tracker/internal/never_condition_validator.cc b/components/feature_engagement_tracker/internal/never_condition_validator.cc
new file mode 100644
index 0000000..317c66c0
--- /dev/null
+++ b/components/feature_engagement_tracker/internal/never_condition_validator.cc
@@ -0,0 +1,20 @@
+// 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 "components/feature_engagement_tracker/internal/never_condition_validator.h"
+
+#include "components/feature_engagement_tracker/internal/model.h"
+
+namespace feature_engagement_tracker {
+
+NeverConditionValidator::NeverConditionValidator() = default;
+
+NeverConditionValidator::~NeverConditionValidator() = default;
+
+bool NeverConditionValidator::MeetsConditions(const base::Feature& feature,
+                                              const Model& model) {
+  return false;
+}
+
+}  // namespace feature_engagement_tracker
diff --git a/components/feature_engagement_tracker/internal/never_condition_validator.h b/components/feature_engagement_tracker/internal/never_condition_validator.h
new file mode 100644
index 0000000..b9fca64
--- /dev/null
+++ b/components/feature_engagement_tracker/internal/never_condition_validator.h
@@ -0,0 +1,37 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_FEATURE_ENGAGEMENT_TRACKER_INTERNAL_NEVER_CONDITION_VALIDATOR_H_
+#define COMPONENTS_FEATURE_ENGAGEMENT_TRACKER_INTERNAL_NEVER_CONDITION_VALIDATOR_H_
+
+#include <unordered_set>
+
+#include "base/macros.h"
+#include "components/feature_engagement_tracker/internal/condition_validator.h"
+#include "components/feature_engagement_tracker/internal/model.h"
+
+namespace base {
+struct Feature;
+}  // namespace base
+
+namespace feature_engagement_tracker {
+
+// An ConditionValidator that never acknowledges that a feature has met its
+// conditions.
+class NeverConditionValidator : public ConditionValidator {
+ public:
+  NeverConditionValidator();
+  ~NeverConditionValidator() override;
+
+  // ConditionValidator implementation.
+  bool MeetsConditions(const base::Feature& feature,
+                       const Model& model) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NeverConditionValidator);
+};
+
+}  // namespace feature_engagement_tracker
+
+#endif  // COMPONENTS_FEATURE_ENGAGEMENT_TRACKER_INTERNAL_NEVER_CONDITION_VALIDATOR_H_
diff --git a/components/feature_engagement_tracker/internal/never_condition_validator_unittest.cc b/components/feature_engagement_tracker/internal/never_condition_validator_unittest.cc
new file mode 100644
index 0000000..4bcd08e
--- /dev/null
+++ b/components/feature_engagement_tracker/internal/never_condition_validator_unittest.cc
@@ -0,0 +1,70 @@
+// 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 "components/feature_engagement_tracker/internal/never_condition_validator.h"
+
+#include "base/feature_list.h"
+#include "base/metrics/field_trial.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/feature_engagement_tracker/internal/model.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace feature_engagement_tracker {
+
+namespace {
+
+const base::Feature kTestFeatureFoo{"test_foo",
+                                    base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kTestFeatureBar{"test_bar",
+                                    base::FEATURE_DISABLED_BY_DEFAULT};
+
+// A Model that is always postive to show in-product help.
+class TestModel : public Model {
+ public:
+  TestModel() {
+    feature_config_.valid = true;
+    feature_config_.feature_used_event = "foobar";
+  }
+
+  void Initialize(const OnModelInitializationFinished& callback) override {}
+
+  bool IsReady() const override { return true; }
+
+  const FeatureConfig& GetFeatureConfig(
+      const base::Feature& feature) const override {
+    return feature_config_;
+  }
+
+  void SetIsCurrentlyShowing(bool is_showing) override {}
+
+  bool IsCurrentlyShowing() const override { return false; }
+
+ private:
+  FeatureConfig feature_config_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestModel);
+};
+
+class NeverConditionValidatorTest : public ::testing::Test {
+ public:
+  NeverConditionValidatorTest() = default;
+
+ protected:
+  base::test::ScopedFeatureList scoped_feature_list_;
+  TestModel model_;
+  NeverConditionValidator validator_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NeverConditionValidatorTest);
+};
+
+}  // namespace
+
+TEST_F(NeverConditionValidatorTest, ShouldNeverMeetConditions) {
+  scoped_feature_list_.InitWithFeatures({kTestFeatureFoo, kTestFeatureBar}, {});
+  EXPECT_FALSE(validator_.MeetsConditions(kTestFeatureFoo, model_));
+  EXPECT_FALSE(validator_.MeetsConditions(kTestFeatureBar, model_));
+}
+
+}  // namespace feature_engagement_tracker
diff --git a/components/feature_engagement_tracker/internal/once_condition_validator.cc b/components/feature_engagement_tracker/internal/once_condition_validator.cc
index 2e14015..1240766 100644
--- a/components/feature_engagement_tracker/internal/once_condition_validator.cc
+++ b/components/feature_engagement_tracker/internal/once_condition_validator.cc
@@ -16,9 +16,6 @@
 
 bool OnceConditionValidator::MeetsConditions(const base::Feature& feature,
                                              const Model& model) {
-  if (!base::FeatureList::IsEnabled(feature))
-    return false;
-
   if (!model.IsReady())
     return false;
 
diff --git a/components/feature_engagement_tracker/internal/once_condition_validator.h b/components/feature_engagement_tracker/internal/once_condition_validator.h
index 9cc0d54..83b265e 100644
--- a/components/feature_engagement_tracker/internal/once_condition_validator.h
+++ b/components/feature_engagement_tracker/internal/once_condition_validator.h
@@ -20,16 +20,16 @@
 // An ConditionValidator that will ensure that each base::Feature will meet
 // conditions maximum one time for any given session.
 // It has the following requirements:
-// - The base::Feature is enabled
 // - The Model is ready.
 // - No other in-product help is currently showing.
-// - FeatureServerConfig for the feature is valid.
+// - FeatureConfig for the feature is valid.
 // - This is the first time the given base::Feature meets all above stated
 //   conditions.
 //
-// NOTE: This ConditionValidator fully ignores any other configuration specified
-// in the FeatureServerConfig. In practice this leads this ConditionValidator
-// to be well suited for a demonstration mode of in-product help.
+// NOTE: This ConditionValidator fully ignores whether the base::Feature is
+// enabled or not and any other configuration specified in the FeatureConfig.
+// In practice this leads this ConditionValidator to be well suited for a
+// demonstration mode of in-product help.
 class OnceConditionValidator : public ConditionValidator {
  public:
   OnceConditionValidator();
diff --git a/components/feature_engagement_tracker/internal/once_condition_validator_unittest.cc b/components/feature_engagement_tracker/internal/once_condition_validator_unittest.cc
index ecc58e4c..c7c7598 100644
--- a/components/feature_engagement_tracker/internal/once_condition_validator_unittest.cc
+++ b/components/feature_engagement_tracker/internal/once_condition_validator_unittest.cc
@@ -52,6 +52,8 @@
 
 class OnceConditionValidatorTest : public ::testing::Test {
  public:
+  OnceConditionValidatorTest() = default;
+
   void SetUp() override {
     // By default, model should be ready.
     model_.SetIsReady(true);
@@ -67,6 +69,9 @@
   base::test::ScopedFeatureList scoped_feature_list_;
   TestModel model_;
   OnceConditionValidator validator_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(OnceConditionValidatorTest);
 };
 
 }  // namespace
@@ -82,7 +87,8 @@
   EXPECT_FALSE(validator_.MeetsConditions(kTestFeatureFoo, model_));
 }
 
-TEST_F(OnceConditionValidatorTest, OnlyEnabledFeaturesShouldTrigger) {
+TEST_F(OnceConditionValidatorTest,
+       BothEnabledAndDisabledFeaturesShouldTrigger) {
   scoped_feature_list_.InitWithFeatures({kTestFeatureFoo}, {kTestFeatureBar});
 
   // Initialize validator with one enabled and one disabled feature, both valid.
@@ -93,11 +99,11 @@
   // kTestFeatureBar is disabled. Ordering disabled feature first to ensure this
   // captures a different behavior than the
   // OnlyOneFeatureShouldTriggerPerSession test below.
-  EXPECT_FALSE(validator_.MeetsConditions(kTestFeatureBar, model_));
+  EXPECT_TRUE(validator_.MeetsConditions(kTestFeatureBar, model_));
   EXPECT_TRUE(validator_.MeetsConditions(kTestFeatureFoo, model_));
 }
 
-TEST_F(OnceConditionValidatorTest, NeverTriggerWhenAllFeaturesDisabled) {
+TEST_F(OnceConditionValidatorTest, StillTriggerWhenAllFeaturesDisabled) {
   scoped_feature_list_.InitWithFeatures({}, {kTestFeatureFoo, kTestFeatureBar});
 
   // Initialize validator with two enabled features, both valid.
@@ -105,8 +111,8 @@
   AddFeature(kTestFeatureBar, true);
 
   // No features should get to show enlightenment.
-  EXPECT_FALSE(validator_.MeetsConditions(kTestFeatureFoo, model_));
-  EXPECT_FALSE(validator_.MeetsConditions(kTestFeatureBar, model_));
+  EXPECT_TRUE(validator_.MeetsConditions(kTestFeatureFoo, model_));
+  EXPECT_TRUE(validator_.MeetsConditions(kTestFeatureBar, model_));
 }
 
 TEST_F(OnceConditionValidatorTest, OnlyTriggerWhenModelIsReady) {
diff --git a/components/feature_engagement_tracker/internal/single_invalid_configuration.cc b/components/feature_engagement_tracker/internal/single_invalid_configuration.cc
new file mode 100644
index 0000000..ed20e661
--- /dev/null
+++ b/components/feature_engagement_tracker/internal/single_invalid_configuration.cc
@@ -0,0 +1,23 @@
+// 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 "components/feature_engagement_tracker/internal/single_invalid_configuration.h"
+
+#include "components/feature_engagement_tracker/internal/configuration.h"
+
+namespace feature_engagement_tracker {
+
+SingleInvalidConfiguration::SingleInvalidConfiguration() {
+  invalid_feature_config_.valid = false;
+  invalid_feature_config_.feature_used_event = "nothing_to_see_here";
+};
+
+SingleInvalidConfiguration::~SingleInvalidConfiguration() = default;
+
+const FeatureConfig& SingleInvalidConfiguration::GetFeatureConfig(
+    const base::Feature& feature) const {
+  return invalid_feature_config_;
+}
+
+}  // namespace feature_engagement_tracker
diff --git a/components/feature_engagement_tracker/internal/single_invalid_configuration.h b/components/feature_engagement_tracker/internal/single_invalid_configuration.h
new file mode 100644
index 0000000..449cee9
--- /dev/null
+++ b/components/feature_engagement_tracker/internal/single_invalid_configuration.h
@@ -0,0 +1,39 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_FEATURE_ENGAGEMENT_TRACKER_INTERNAL_SINGLE_INVALID_CONFIGURATION_H_
+#define COMPONENTS_FEATURE_ENGAGEMENT_TRACKER_INTERNAL_SINGLE_INVALID_CONFIGURATION_H_
+
+#include <unordered_set>
+
+#include "base/macros.h"
+#include "components/feature_engagement_tracker/internal/configuration.h"
+
+namespace base {
+struct Feature;
+}  // namespace base
+
+namespace feature_engagement_tracker {
+
+// An Configuration that always returns the same single invalid configuration,
+// regardless of which feature.
+class SingleInvalidConfiguration : public Configuration {
+ public:
+  SingleInvalidConfiguration();
+  ~SingleInvalidConfiguration() override;
+
+  // Configuration implementation.
+  const FeatureConfig& GetFeatureConfig(
+      const base::Feature& feature) const override;
+
+ private:
+  // The invalid configuration to always return.
+  FeatureConfig invalid_feature_config_;
+
+  DISALLOW_COPY_AND_ASSIGN(SingleInvalidConfiguration);
+};
+
+}  // namespace feature_engagement_tracker
+
+#endif  // COMPONENTS_FEATURE_ENGAGEMENT_TRACKER_INTERNAL_SINGLE_INVALID_CONFIGURATION_H_
diff --git a/components/feature_engagement_tracker/internal/single_invalid_configuration_unittest.cc b/components/feature_engagement_tracker/internal/single_invalid_configuration_unittest.cc
new file mode 100644
index 0000000..d4c2666
--- /dev/null
+++ b/components/feature_engagement_tracker/internal/single_invalid_configuration_unittest.cc
@@ -0,0 +1,46 @@
+// 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 "components/feature_engagement_tracker/internal/single_invalid_configuration.h"
+
+#include "base/feature_list.h"
+#include "base/metrics/field_trial.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/feature_engagement_tracker/internal/configuration.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace feature_engagement_tracker {
+
+namespace {
+
+const base::Feature kTestFeatureFoo{"test_foo",
+                                    base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kTestFeatureBar{"test_bar",
+                                    base::FEATURE_DISABLED_BY_DEFAULT};
+
+class SingleInvalidConfigurationTest : public ::testing::Test {
+ public:
+  SingleInvalidConfigurationTest() = default;
+
+ protected:
+  base::test::ScopedFeatureList scoped_feature_list_;
+  SingleInvalidConfiguration configuration_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SingleInvalidConfigurationTest);
+};
+
+}  // namespace
+
+TEST_F(SingleInvalidConfigurationTest, AllConfigurationsAreInvalid) {
+  scoped_feature_list_.InitWithFeatures({kTestFeatureFoo, kTestFeatureBar}, {});
+
+  FeatureConfig foo_config = configuration_.GetFeatureConfig(kTestFeatureFoo);
+  EXPECT_FALSE(foo_config.valid);
+
+  FeatureConfig bar_config = configuration_.GetFeatureConfig(kTestFeatureBar);
+  EXPECT_FALSE(bar_config.valid);
+}
+
+}  // namespace feature_engagement_tracker
diff --git a/components/feature_engagement_tracker/public/feature_constants.h b/components/feature_engagement_tracker/public/feature_constants.h
index 919d6005..c26608a 100644
--- a/components/feature_engagement_tracker/public/feature_constants.h
+++ b/components/feature_engagement_tracker/public/feature_constants.h
@@ -8,7 +8,11 @@
 #include "base/feature_list.h"
 
 namespace feature_engagement_tracker {
-// All the features declared in this file should also be declared in the Java
+
+// A feature for enabling a demonstration mode for In-Product Help.
+extern const base::Feature kIPHDemoMode;
+
+// All the features declared below should also be declared in the Java
 // version: org.chromium.components.feature_engagement_tracker.FeatureConstants.
 
 // A dummy feature until real features start using the backend.
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc b/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
index 6716ef4..4465246 100644
--- a/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
@@ -89,10 +89,6 @@
   return arg.id().id_within_category() == expected_id;
 }
 
-MATCHER_P(IsCategory, id, "") {
-  return arg.id() == static_cast<int>(id);
-}
-
 MATCHER_P(HasCode, code, "") {
   return arg.code == code;
 }
diff --git a/components/translate/core/browser/BUILD.gn b/components/translate/core/browser/BUILD.gn
index 30bb6fb..1e56c7e 100644
--- a/components/translate/core/browser/BUILD.gn
+++ b/components/translate/core/browser/BUILD.gn
@@ -62,6 +62,7 @@
     "//components/strings",
     "//components/translate/core/browser/proto",
     "//components/translate/core/common",
+    "//components/ukm",
     "//components/variations",
     "//google_apis",
     "//net",
@@ -116,6 +117,8 @@
     "//components/sync_preferences:test_support",
     "//components/translate/core/browser/proto",
     "//components/translate/core/common",
+    "//components/ukm",
+    "//components/ukm:test_support",
     "//components/variations",
     "//net:test_support",
     "//testing/gtest",
diff --git a/components/translate/core/browser/DEPS b/components/translate/core/browser/DEPS
index d7adaf63..5b24a63 100644
--- a/components/translate/core/browser/DEPS
+++ b/components/translate/core/browser/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "+components/keyed_service/core",
   "+components/metrics",
+  "+components/ukm",
 ]
diff --git a/components/translate/core/browser/mock_translate_ranker.cc b/components/translate/core/browser/mock_translate_ranker.cc
index 252c88f..2c9418f8 100644
--- a/components/translate/core/browser/mock_translate_ranker.cc
+++ b/components/translate/core/browser/mock_translate_ranker.cc
@@ -5,6 +5,7 @@
 #include "components/translate/core/browser/mock_translate_ranker.h"
 
 #include "components/metrics/proto/translate_event.pb.h"
+#include "url/gurl.h"
 
 namespace translate {
 namespace testing {
@@ -37,7 +38,8 @@
 }
 
 void MockTranslateRanker::AddTranslateEvent(
-    const metrics::TranslateEventProto& translate_event) {
+    const metrics::TranslateEventProto& translate_event,
+    const GURL& /* url */) {
   event_cache_.push_back(translate_event);
 }
 
diff --git a/components/translate/core/browser/mock_translate_ranker.h b/components/translate/core/browser/mock_translate_ranker.h
index 218cc2c..ea71eb8e 100644
--- a/components/translate/core/browser/mock_translate_ranker.h
+++ b/components/translate/core/browser/mock_translate_ranker.h
@@ -11,6 +11,8 @@
 
 #include "components/translate/core/browser/translate_ranker.h"
 
+class GURL;
+
 namespace metrics {
 class TranslateEventProto;
 }
@@ -42,8 +44,8 @@
   bool ShouldOfferTranslation(const TranslatePrefs& translate_prefs,
                               const std::string& src_lang,
                               const std::string& dst_lang) override;
-  void AddTranslateEvent(
-      const metrics::TranslateEventProto& translate_event) override;
+  void AddTranslateEvent(const metrics::TranslateEventProto& translate_event,
+                         const GURL& url) override;
   void FlushTranslateEvents(
       std::vector<metrics::TranslateEventProto>* events) override;
 
diff --git a/components/translate/core/browser/translate_manager.cc b/components/translate/core/browser/translate_manager.cc
index 114a93fb..0f2e8e4 100644
--- a/components/translate/core/browser/translate_manager.cc
+++ b/components/translate/core/browser/translate_manager.cc
@@ -606,7 +606,8 @@
       static_cast<metrics::TranslateEventProto::EventType>(event_type));
   translate_event_->set_event_timestamp_sec(
       (base::TimeTicks::Now() - base::TimeTicks()).InSeconds());
-  translate_ranker_->AddTranslateEvent(*translate_event_);
+  translate_ranker_->AddTranslateEvent(*translate_event_,
+                                       translate_driver_->GetVisibleURL());
 }
 
 }  // namespace translate
diff --git a/components/translate/core/browser/translate_ranker.h b/components/translate/core/browser/translate_ranker.h
index 0be5f5f..b10c303a 100644
--- a/components/translate/core/browser/translate_ranker.h
+++ b/components/translate/core/browser/translate_ranker.h
@@ -12,6 +12,8 @@
 #include "base/macros.h"
 #include "components/keyed_service/core/keyed_service.h"
 
+class GURL;
+
 namespace metrics {
 class TranslateEventProto;
 }  // namespace metrics
@@ -49,7 +51,8 @@
 
   // Caches the translate event.
   virtual void AddTranslateEvent(
-      const metrics::TranslateEventProto& translate_event) = 0;
+      const metrics::TranslateEventProto& translate_event,
+      const GURL& url) = 0;
 
   // Transfers cached translate events to the given vector pointer and clears
   // the cache.
diff --git a/components/translate/core/browser/translate_ranker_impl.cc b/components/translate/core/browser/translate_ranker_impl.cc
index 04abc20..7be53a8 100644
--- a/components/translate/core/browser/translate_ranker_impl.cc
+++ b/components/translate/core/browser/translate_ranker_impl.cc
@@ -13,6 +13,7 @@
 #include "base/files/file_util.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/metrics/metrics_hashes.h"
 #include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -26,6 +27,8 @@
 #include "components/translate/core/browser/translate_prefs.h"
 #include "components/translate/core/browser/translate_url_fetcher.h"
 #include "components/translate/core/common/translate_switches.h"
+#include "components/ukm/ukm_entry_builder.h"
+#include "components/ukm/ukm_service.h"
 #include "components/variations/variations_associated_data.h"
 #include "url/gurl.h"
 
@@ -132,8 +135,10 @@
 }
 
 TranslateRankerImpl::TranslateRankerImpl(const base::FilePath& model_path,
-                                         const GURL& model_url)
-    : is_logging_enabled_(
+                                         const GURL& model_url,
+                                         ukm::UkmService* ukm_service)
+    : ukm_service_(ukm_service),
+      is_logging_enabled_(
           base::FeatureList::IsEnabled(kTranslateRankerLogging)),
       is_query_enabled_(base::FeatureList::IsEnabled(kTranslateRankerQuery)),
       is_enforcement_enabled_(
@@ -274,11 +279,43 @@
   event_cache_.clear();
 }
 
+void TranslateRankerImpl::SendEventToUKM(
+    const metrics::TranslateEventProto& event,
+    const GURL& url) {
+  if (!ukm_service_) {
+    DVLOG(3) << "No UKM service.";
+    return;
+  }
+  DVLOG(3) << "Sending event for url: " << url.spec();
+  int32_t source_id = ukm_service_->GetNewSourceID();
+  ukm_service_->UpdateSourceURL(source_id, url);
+  std::unique_ptr<ukm::UkmEntryBuilder> builder =
+      ukm_service_->GetEntryBuilder(source_id, "Translate");
+  // The metrics added here should be kept in sync with the documented
+  // metrics in tools/metrics/ukm/ukm.xml.
+  // TODO(hamelphi): Remove hashing functions once UKM accepts strings metrics.
+  builder->AddMetric("SourceLanguage",
+                     base::HashMetricName(event.source_language()));
+  builder->AddMetric("TargetLanguage",
+                     base::HashMetricName(event.target_language()));
+  builder->AddMetric("Country", base::HashMetricName(event.country()));
+  builder->AddMetric("AcceptCount", event.accept_count());
+  builder->AddMetric("DeclineCount", event.decline_count());
+  builder->AddMetric("IgnoreCount", event.ignore_count());
+  builder->AddMetric("RankerVersion", event.ranker_version());
+  builder->AddMetric("RankerResponse", event.ranker_response());
+  builder->AddMetric("EventType", event.event_type());
+}
+
 void TranslateRankerImpl::AddTranslateEvent(
-    const metrics::TranslateEventProto& event) {
+    const metrics::TranslateEventProto& event,
+    const GURL& url) {
   DCHECK(sequence_checker_.CalledOnValidSequence());
   if (IsLoggingEnabled()) {
     DVLOG(3) << "Adding translate ranker event.";
+    if (url.is_valid()) {
+      SendEventToUKM(event, url);
+    }
     event_cache_.push_back(event);
   }
 }
diff --git a/components/translate/core/browser/translate_ranker_impl.h b/components/translate/core/browser/translate_ranker_impl.h
index f233633..f9d71ab 100644
--- a/components/translate/core/browser/translate_ranker_impl.h
+++ b/components/translate/core/browser/translate_ranker_impl.h
@@ -18,10 +18,16 @@
 #include "components/translate/core/browser/translate_ranker.h"
 #include "url/gurl.h"
 
+class GURL;
+
 namespace chrome_intelligence {
 class RankerModel;
 }  // namespace chrome_intelligence
 
+namespace ukm {
+class UkmService;
+}  // namespace ukm
+
 namespace metrics {
 class TranslateEventProto;
 }  // namespace metrics
@@ -76,7 +82,9 @@
 // whether the user should be given a translation prompt or not.
 class TranslateRankerImpl : public TranslateRanker {
  public:
-  TranslateRankerImpl(const base::FilePath& model_path, const GURL& model_url);
+  TranslateRankerImpl(const base::FilePath& model_path,
+                      const GURL& model_url,
+                      ukm::UkmService* ukm_service);
   ~TranslateRankerImpl() override;
 
   // Get the file path of the translate ranker model, by default with a fixed
@@ -98,8 +106,8 @@
   bool ShouldOfferTranslation(const TranslatePrefs& translate_prefs,
                               const std::string& src_lang,
                               const std::string& dst_lang) override;
-  void AddTranslateEvent(
-      const metrics::TranslateEventProto& translate_event) override;
+  void AddTranslateEvent(const metrics::TranslateEventProto& translate_event,
+                         const GURL& url) override;
   void FlushTranslateEvents(
       std::vector<metrics::TranslateEventProto>* events) override;
 
@@ -114,6 +122,12 @@
   bool CheckModelLoaderForTesting();
 
  private:
+  void SendEventToUKM(const metrics::TranslateEventProto& translate_event,
+                      const GURL& url);
+
+  // Used to log URL-keyed metrics. This pointer will outlive |this|.
+  ukm::UkmService* ukm_service_;
+
   // Used to sanity check the threading of this ranker.
   base::SequenceChecker sequence_checker_;
 
diff --git a/components/translate/core/browser/translate_ranker_impl_unittest.cc b/components/translate/core/browser/translate_ranker_impl_unittest.cc
index b4c2dcfe..a1fea53 100644
--- a/components/translate/core/browser/translate_ranker_impl_unittest.cc
+++ b/components/translate/core/browser/translate_ranker_impl_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_scheduler.h"
 #include "components/metrics/proto/translate_event.pb.h"
+#include "components/metrics/proto/ukm/source.pb.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "components/translate/core/browser/proto/ranker_model.pb.h"
@@ -23,9 +24,12 @@
 #include "components/translate/core/browser/ranker_model.h"
 #include "components/translate/core/browser/translate_download_manager.h"
 #include "components/translate/core/browser/translate_prefs.h"
+#include "components/ukm/test_ukm_service.h"
+#include "components/ukm/ukm_source.h"
 #include "net/url_request/test_url_fetcher_factory.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
 
 namespace {
 
@@ -72,7 +76,13 @@
   std::unique_ptr<sync_preferences::TestingPrefServiceSyncable> prefs_;
   std::unique_ptr<translate::TranslatePrefs> translate_prefs_;
 
+  ukm::TestUkmService* GetTestUkmService() {
+    return ukm_service_test_harness_.test_ukm_service();
+  }
+
  private:
+  ukm::UkmServiceTestingHarness ukm_service_test_harness_;
+
   // Override the default URL fetcher to return custom responses for tests.
   net::TestURLFetcherFactory url_fetcher_factory_;
 
@@ -165,7 +175,8 @@
   locale_weight["en-ca"] = 0.11f;
   locale_weight["zh-cn"] = 0.12f;  // Normalized to lowercase.
 
-  auto impl = base::MakeUnique<TranslateRankerImpl>(base::FilePath(), GURL());
+  auto impl = base::MakeUnique<TranslateRankerImpl>(base::FilePath(), GURL(),
+                                                    GetTestUkmService());
   impl->OnModelAvailable(std::move(model));
   base::RunLoop().RunUntilIdle();
   return impl;
@@ -311,13 +322,16 @@
   std::unique_ptr<translate::TranslateRanker> ranker = GetRankerForTest(0.0f);
   std::vector<metrics::TranslateEventProto> flushed_events;
 
+  GURL url0("https://www.google.com");
+  GURL url1("https://www.gmail.com");
+
   // Check that flushing an empty cache will return an empty vector.
   ranker->FlushTranslateEvents(&flushed_events);
   EXPECT_EQ(0U, flushed_events.size());
 
-  ranker->AddTranslateEvent(CreateTranslateEvent("fr", "en", 1, 0, 3));
-  ranker->AddTranslateEvent(CreateTranslateEvent("jp", "en", 2, 0, 3));
-  ranker->AddTranslateEvent(CreateTranslateEvent("es", "de", 4, 5, 6));
+  ranker->AddTranslateEvent(CreateTranslateEvent("fr", "en", 1, 0, 3), url0);
+  ranker->AddTranslateEvent(CreateTranslateEvent("jp", "en", 2, 0, 3), GURL());
+  ranker->AddTranslateEvent(CreateTranslateEvent("es", "de", 4, 5, 6), url1);
 
   // Capture the data and verify that it is as expected.
   ranker->FlushTranslateEvents(&flushed_events);
@@ -329,6 +343,14 @@
   // Check that the cache has been cleared.
   ranker->FlushTranslateEvents(&flushed_events);
   EXPECT_EQ(0U, flushed_events.size());
+
+  ASSERT_EQ(2U, GetTestUkmService()->sources_count());
+  EXPECT_EQ(
+      url0.spec(),
+      GetTestUkmService()->GetSourceForUrl(url0.spec().c_str())->url().spec());
+  EXPECT_EQ(
+      url1.spec(),
+      GetTestUkmService()->GetSourceForUrl(url1.spec().c_str())->url().spec());
 }
 
 TEST_F(TranslateRankerImplTest, LoggingDisabled) {
@@ -339,13 +361,14 @@
   ranker->FlushTranslateEvents(&flushed_events);
   EXPECT_EQ(0U, flushed_events.size());
 
-  ranker->AddTranslateEvent(CreateTranslateEvent("fr", "en", 1, 0, 3));
-  ranker->AddTranslateEvent(CreateTranslateEvent("jp", "en", 2, 0, 3));
-  ranker->AddTranslateEvent(CreateTranslateEvent("es", "de", 4, 5, 6));
+  ranker->AddTranslateEvent(CreateTranslateEvent("fr", "en", 1, 0, 3), GURL());
+  ranker->AddTranslateEvent(CreateTranslateEvent("jp", "en", 2, 0, 3), GURL());
+  ranker->AddTranslateEvent(CreateTranslateEvent("es", "de", 4, 5, 6), GURL());
 
   // Logging is disabled, so no events should be cached.
   ranker->FlushTranslateEvents(&flushed_events);
   EXPECT_EQ(0U, flushed_events.size());
+  EXPECT_EQ(0ul, GetTestUkmService()->sources_count());
 }
 
 TEST_F(TranslateRankerImplTest, LoggingDisabledViaOverride) {
@@ -357,9 +380,9 @@
   ranker->FlushTranslateEvents(&flushed_events);
   EXPECT_EQ(0U, flushed_events.size());
 
-  ranker->AddTranslateEvent(CreateTranslateEvent("fr", "en", 1, 0, 3));
-  ranker->AddTranslateEvent(CreateTranslateEvent("jp", "en", 2, 0, 3));
-  ranker->AddTranslateEvent(CreateTranslateEvent("es", "de", 4, 5, 6));
+  ranker->AddTranslateEvent(CreateTranslateEvent("fr", "en", 1, 0, 3), GURL());
+  ranker->AddTranslateEvent(CreateTranslateEvent("jp", "en", 2, 0, 3), GURL());
+  ranker->AddTranslateEvent(CreateTranslateEvent("es", "de", 4, 5, 6), GURL());
 
   // Logging is disabled, so no events should be cached.
   ranker->FlushTranslateEvents(&flushed_events);
@@ -368,9 +391,9 @@
   // Override the feature setting to disable logging.
   ranker->EnableLogging(false);
 
-  ranker->AddTranslateEvent(CreateTranslateEvent("fr", "en", 1, 0, 3));
-  ranker->AddTranslateEvent(CreateTranslateEvent("jp", "en", 2, 0, 3));
-  ranker->AddTranslateEvent(CreateTranslateEvent("es", "de", 4, 5, 6));
+  ranker->AddTranslateEvent(CreateTranslateEvent("fr", "en", 1, 0, 3), GURL());
+  ranker->AddTranslateEvent(CreateTranslateEvent("jp", "en", 2, 0, 3), GURL());
+  ranker->AddTranslateEvent(CreateTranslateEvent("es", "de", 4, 5, 6), GURL());
 
   // Logging is disabled, so no events should be cached.
   ranker->FlushTranslateEvents(&flushed_events);
diff --git a/components/ukm/ukm_service.h b/components/ukm/ukm_service.h
index 7247e4d4..61f7939 100644
--- a/components/ukm/ukm_service.h
+++ b/components/ukm/ukm_service.h
@@ -29,6 +29,10 @@
 class AutofillMetrics;
 }  // namespace autofill
 
+namespace translate {
+class TranslateRankerImpl;
+}
+
 namespace metrics {
 class MetricsServiceClient;
 }
@@ -112,6 +116,7 @@
   friend autofill::AutofillMetrics;
   friend PluginInfoMessageFilter;
   friend UkmPageLoadMetricsObserver;
+  friend translate::TranslateRankerImpl;
   FRIEND_TEST_ALL_PREFIXES(UkmServiceTest, AddEntryOnlyWithNonEmptyMetrics);
   FRIEND_TEST_ALL_PREFIXES(UkmServiceTest, EntryBuilderAndSerialization);
   FRIEND_TEST_ALL_PREFIXES(UkmServiceTest,
diff --git a/components/url_formatter/url_formatter.cc b/components/url_formatter/url_formatter.cc
index b8a802c..d54b679 100644
--- a/components/url_formatter/url_formatter.cc
+++ b/components/url_formatter/url_formatter.cc
@@ -324,8 +324,9 @@
   non_ascii_latin_letters_.freeze();
 
   // These letters are parts of |dangerous_patterns_|.
-  kana_letters_exceptions_ = icu::UnicodeSet(UNICODE_STRING_SIMPLE(
-      "[\\u3078-\\u307a\\u30d8-\\u30da\\u30fb\\u30fc]"), status);
+  kana_letters_exceptions_ = icu::UnicodeSet(
+      UNICODE_STRING_SIMPLE("[\\u3078-\\u307a\\u30d8-\\u30da\\u30fb-\\u30fe]"),
+      status);
   kana_letters_exceptions_.freeze();
 
   // These Cyrillic letters look like Latin. A domain label entirely made of
@@ -406,6 +407,8 @@
     // TODO(jshin): adjust the pattern once the above ICU bug is fixed.
     // - Disallow U+30FB (Katakana Middle Dot) and U+30FC (Hiragana-Katakana
     //   Prolonged Sound) used out-of-context.
+    // - Dislallow U+30FD/E (Katakana iteration mark/voiced iteration mark)
+    //   unless they're preceded by a Katakana.
     // - Disallow three Hiragana letters (U+307[8-A]) or Katakana letters
     //   (U+30D[8-A]) that look exactly like each other when they're used in a
     //   label otherwise entirely in Katakna or Hiragana.
@@ -417,15 +420,16 @@
             "[^\\p{scx=kana}\\p{scx=hira}\\p{scx=hani}]"
             "[\\u30ce\\u30f3\\u30bd\\u30be]"
             "[^\\p{scx=kana}\\p{scx=hira}\\p{scx=hani}]|"
-            "[^\\p{scx=kana}\\p{scx=hira}]\\u30fc|"
-            "\\u30fc[^\\p{scx=kana}\\p{scx=hira}]|"
+            "[^\\p{scx=kana}\\p{scx=hira}]\\u30fc|^\\u30fc|"
+            "[^\\p{scx=kana}][\\u30fd\\u30fe]|^[\\u30fd\\u30fe]|"
             "^[\\p{scx=kana}]+[\\u3078-\\u307a][\\p{scx=kana}]+$|"
             "^[\\p{scx=hira}]+[\\u30d8-\\u30da][\\p{scx=hira}]+$|"
             "[a-z]\\u30fb|\\u30fb[a-z]|"
             "^[\\u0585\\u0581]+[a-z]|[a-z][\\u0585\\u0581]+$|"
             "[a-z][\\u0585\\u0581]+[a-z]|"
             "^[og]+[\\p{scx=armn}]|[\\p{scx=armn}][og]+$|"
-            "[\\p{scx=armn}][og]+[\\p{scx=armn}]", -1, US_INV),
+            "[\\p{scx=armn}][og]+[\\p{scx=armn}]",
+            -1, US_INV),
         0, status);
     tls_index.Set(dangerous_pattern);
   }
diff --git a/components/url_formatter/url_formatter_unittest.cc b/components/url_formatter/url_formatter_unittest.cc
index 5b2646c..a59b4b6 100644
--- a/components/url_formatter/url_formatter_unittest.cc
+++ b/components/url_formatter/url_formatter_unittest.cc
@@ -187,6 +187,8 @@
   {"xn--a-xbba.com", L"a\x0301\x0301.com", false},
   // 'a' with acuted accent + another acute accent
   {"xn--1ca20i.com", L"\x00e1\x0301.com", false},
+  // Combining mark at the beginning
+  {"xn--abc-fdc.jp", L"\x0300" L"abc.jp", false},
 
   // Mixed script confusable
   // google with Armenian Small Letter Oh(U+0585)
@@ -199,17 +201,51 @@
   // Hiragana HE(U+3078) mixed with Katakana
   {"xn--49jxi3as0d0fpc.com",
     L"\x30e2\x30d2\x30fc\x30c8\x3078\x30d6\x30f3.com", false},
+
+  // U+30FC should be preceded by a Hiragana/Katakana.
+  // Katakana + U+30FC + Han
+  {"xn--lck0ip02qw5ya.jp", L"\x30ab\x30fc\x91ce\x7403.jp", true},
+  // Hiragana + U+30FC + Han
+  {"xn--u8j5tr47nw5ya.jp", L"\x304b\x30fc\x91ce\x7403.jp", true},
   // U+30FC + Han
   {"xn--weka801xo02a.com", L"\x30fc\x52d5\x753b\x30fc.com", false},
   // Han + U+30FC + Han
   {"xn--wekz60nb2ay85atj0b.jp", L"\x65e5\x672c\x30fc\x91ce\x7403.jp", false},
+  // U+30FC at the beginning
+  {"xn--wek060nb2a.jp", L"\x30fc\x65e5\x672c", false},
   // Latin + U+30FC + Latin
   {"xn--abcdef-r64e.jp", L"abc\x30fc" L"def.jp", false},
+
+  // U+30FB (・) is not allowed next to Latin, but allowed otherwise.
+  // U+30FB + Han
+  {"xn--vekt920a.jp", L"\x30fb\x91ce.jp", true},
+  // Han + U+30FB + Han
+  {"xn--vek160nb2ay85atj0b.jp", L"\x65e5\x672c\x30fb\x91ce\x7403.jp", true},
   // Latin + U+30FB + Latin
   {"xn--abcdef-k64e.jp", L"abc\x30fb" L"def.jp", false},
   // U+30FB + Latin
   {"xn--abc-os4b.jp", L"\x30fb" L"abc.jp", false},
 
+  // U+30FD (ヽ) is allowed only after Katakana.
+  // Katakana + U+30FD
+  {"xn--lck2i.jp", L"\x30ab\x30fd.jp", true},
+  // Hiragana + U+30FD
+  {"xn--u8j7t.jp", L"\x304b\x30fd.jp", false},
+  // Han + U+30FD
+  {"xn--xek368f.jp", L"\x4e00\x30fd.jp", false},
+  {"xn--aa-mju.jp", L"a\x30fd.jp", false},
+  {"xn--a1-bo4a.jp", L"a1\x30fd.jp", false},
+
+  // U+30FE (ヾ) is allowed only after Katakana.
+  // Katakana + U+30FE
+  {"xn--lck4i.jp", L"\x30ab\x30fe.jp", true},
+  // Hiragana + U+30FE
+  {"xn--u8j9t.jp", L"\x304b\x30fe.jp", false},
+  // Han + U+30FE
+  {"xn--yek168f.jp", L"\x4e00\x30fe.jp", false},
+  {"xn--a-oju.jp", L"a\x30fe.jp", false},
+  {"xn--a1-eo4a.jp", L"a1\x30fe.jp", false},
+
   // Cyrillic labels made of Latin-look-alike Cyrillic letters.
   // ѕсоре.com with ѕсоре in Cyrillic
   {"xn--e1argc3h.com", L"\x0455\x0441\x043e\x0440\x0435.com", false},
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index af97bb3..be6a517 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -1316,13 +1316,7 @@
                                .size());
 }
 
-// TODO(701223): Enable this on android.
-#if defined(OS_ANDROID)
-#define MAYBE_VirtualTimeTest DISABLED_VirtualTimeTest
-#else
-#define MAYBE_VirtualTimeTest VirtualTimeTest
-#endif
-IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, MAYBE_VirtualTimeTest) {
+IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, VirtualTimeTest) {
   NavigateToURLBlockUntilNavigationsComplete(shell(), GURL("about:blank"), 1);
   Attach();
 
@@ -1330,16 +1324,18 @@
   params->SetString("policy", "pause");
   SendCommand("Emulation.setVirtualTimePolicy", std::move(params), true);
 
+  // TODO(scheduler-dev): Revisit timing when we have strict ordering
+  // guarantees.
   params.reset(new base::DictionaryValue());
   params->SetString("expression",
                     "setTimeout(function(){console.log('before')}, 1000);"
-                    "setTimeout(function(){console.log('after')}, 1001);");
+                    "setTimeout(function(){console.log('after')}, 1002);");
   SendCommand("Runtime.evaluate", std::move(params), true);
 
   // Let virtual time advance for one second.
   params.reset(new base::DictionaryValue());
   params->SetString("policy", "advance");
-  params->SetInteger("budget", 1000);
+  params->SetInteger("budget", 1001);
   SendCommand("Emulation.setVirtualTimePolicy", std::move(params), true);
 
   WaitForNotification("Emulation.virtualTimeBudgetExpired");
diff --git a/content/browser/download/download_manager_impl_unittest.cc b/content/browser/download/download_manager_impl_unittest.cc
index e76bb53..3e606c2 100644
--- a/content/browser/download/download_manager_impl_unittest.cc
+++ b/content/browser/download/download_manager_impl_unittest.cc
@@ -70,13 +70,6 @@
 
 namespace {
 
-// Matches a DownloadCreateInfo* that points to the same object as |info| and
-// has a |default_download_directory| that matches |download_directory|.
-MATCHER_P2(DownloadCreateInfoWithDefaultPath, info, download_directory, "") {
-  return arg == info &&
-      arg->default_download_directory == download_directory;
-}
-
 class MockDownloadManagerDelegate : public DownloadManagerDelegate {
  public:
   MockDownloadManagerDelegate();
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index 7e11be1b..39d00af7 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -171,7 +171,8 @@
     switches::kGpuTestingGLVersion,
     switches::kDisableGpuDriverBugWorkarounds,
     switches::kUsePassthroughCmdDecoder,
-    switches::kEnableHDR};
+    switches::kEnableHDR,
+    switches::kIgnoreGpuBlacklist};
 
 enum GPUProcessLifetimeEvent {
   LAUNCHED,
diff --git a/content/browser/service_manager/service_manager_context.cc b/content/browser/service_manager/service_manager_context.cc
index a818801..42f0624 100644
--- a/content/browser/service_manager/service_manager_context.cc
+++ b/content/browser/service_manager/service_manager_context.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/json/json_reader.h"
 #include "base/lazy_instance.h"
 #include "base/macros.h"
@@ -28,6 +29,7 @@
 #include "content/public/browser/utility_process_host.h"
 #include "content/public/browser/utility_process_host_client.h"
 #include "content/public/common/content_client.h"
+#include "content/public/common/content_switches.h"
 #include "content/public/common/service_manager_connection.h"
 #include "content/public/common/service_names.mojom.h"
 #include "mojo/edk/embedder/embedder.h"
@@ -331,6 +333,12 @@
   GetContentClient()
       ->browser()
       ->RegisterUnsandboxedOutOfProcessServices(&unsandboxed_services);
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableNetworkService)) {
+    unsandboxed_services.insert(
+        std::make_pair(content::mojom::kNetworkServiceName,
+                       base::ASCIIToUTF16("Network Service")));
+  }
   for (const auto& service : unsandboxed_services) {
     packaged_services_connection_->AddServiceRequestHandler(
         service.first, base::Bind(&StartServiceInUtilityProcess, service.first,
diff --git a/content/browser/utility_process_host_impl.cc b/content/browser/utility_process_host_impl.cc
index 4afd9e93..ea61cbf 100644
--- a/content/browser/utility_process_host_impl.cc
+++ b/content/browser/utility_process_host_impl.cc
@@ -314,6 +314,7 @@
 
     // Browser command-line switches to propagate to the utility process.
     static const char* const kSwitchNames[] = {
+      switches::kEnableNetworkService,
       switches::kNoSandbox,
       switches::kProfilerTiming,
 #if defined(OS_MACOSX)
diff --git a/content/network/BUILD.gn b/content/network/BUILD.gn
new file mode 100644
index 0000000..3d7b212
--- /dev/null
+++ b/content/network/BUILD.gn
@@ -0,0 +1,26 @@
+# 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.
+
+import("//services/service_manager/public/service_manifest.gni")
+
+source_set("lib") {
+  sources = [
+    "network_service.cc",
+    "network_service.h",
+  ]
+
+  configs += [ "//content:content_implementation" ]
+
+  deps = [
+    "//base",
+    "//content/common:mojo_bindings",
+    "//mojo/public/cpp/bindings",
+    "//services/service_manager/public/cpp",
+  ]
+}
+
+service_manifest("manifest") {
+  name = "network"
+  source = "manifest.json"
+}
diff --git a/content/network/DEPS b/content/network/DEPS
new file mode 100644
index 0000000..5860594d
--- /dev/null
+++ b/content/network/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+services/service_manager/public",
+]
diff --git a/content/network/manifest.json b/content/network/manifest.json
new file mode 100644
index 0000000..daa39ec
--- /dev/null
+++ b/content/network/manifest.json
@@ -0,0 +1,16 @@
+{
+  "name": "network",
+  "display_name": "Network Service",
+  "interface_provider_specs": {
+    "service_manager:connector": {
+      "provides": {
+        "url_loader": [
+          "content::mojom::URLLoaderFactory"
+        ]
+      },
+      "requires": {
+        "service_manager": [ "service_manager:all_users" ]
+      }
+    }
+  }
+}
diff --git a/content/network/network_service.cc b/content/network/network_service.cc
new file mode 100644
index 0000000..2e7a4f9
--- /dev/null
+++ b/content/network/network_service.cc
@@ -0,0 +1,39 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/network/network_service.h"
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "services/service_manager/public/cpp/service_info.h"
+
+namespace content {
+
+NetworkService::NetworkService() {
+  registry_.AddInterface<mojom::URLLoaderFactory>(this);
+}
+
+NetworkService::~NetworkService() = default;
+
+// static
+std::unique_ptr<service_manager::Service>
+NetworkService::CreateNetworkService() {
+  return base::MakeUnique<NetworkService>();
+}
+
+void NetworkService::OnBindInterface(
+    const service_manager::ServiceInfo& source_info,
+    const std::string& interface_name,
+    mojo::ScopedMessagePipeHandle interface_pipe) {
+  registry_.BindInterface(source_info.identity, interface_name,
+                          std::move(interface_pipe));
+}
+
+void NetworkService::Create(const service_manager::Identity& remote_identity,
+                            mojom::URLLoaderFactoryRequest request) {
+  // TODO(yzshen): Create URLLoaderFactoryImpl.
+  NOTIMPLEMENTED();
+}
+
+}  // namespace content
diff --git a/content/network/network_service.h b/content/network/network_service.h
new file mode 100644
index 0000000..4b705a2
--- /dev/null
+++ b/content/network/network_service.h
@@ -0,0 +1,44 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_NETWORK_NETWORK_SERVICE_H_
+#define CONTENT_NETWORK_NETWORK_SERVICE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "content/common/url_loader_factory.mojom.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+#include "services/service_manager/public/cpp/interface_factory.h"
+#include "services/service_manager/public/cpp/service.h"
+
+namespace content {
+
+class NetworkService
+    : public service_manager::Service,
+      public service_manager::InterfaceFactory<mojom::URLLoaderFactory> {
+ public:
+  NetworkService();
+  ~NetworkService() override;
+
+  static std::unique_ptr<service_manager::Service> CreateNetworkService();
+
+ private:
+  // service_manager::Service implementation.
+  void OnBindInterface(const service_manager::ServiceInfo& source_info,
+                       const std::string& interface_name,
+                       mojo::ScopedMessagePipeHandle interface_pipe) override;
+
+  // service_manager::InterfaceFactory<mojom::UrlLoaderFactory>:
+  void Create(const service_manager::Identity& remote_identity,
+              mojom::URLLoaderFactoryRequest request) override;
+
+  service_manager::BinderRegistry registry_;
+
+  DISALLOW_COPY_AND_ASSIGN(NetworkService);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_NETWORK_NETWORK_SERVICE_H_
diff --git a/content/public/app/BUILD.gn b/content/public/app/BUILD.gn
index 782cd3a9..82f65ba0 100644
--- a/content/public/app/BUILD.gn
+++ b/content/public/app/BUILD.gn
@@ -178,6 +178,7 @@
   name = "content_packaged_services"
   source = "mojo/content_packaged_services_manifest.json"
   packaged_services = [
+    "//content/network:manifest",
     "//media/mojo/services:media_manifest",
     "//services/data_decoder:manifest",
     "//services/device:manifest",
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 4230229..1704d64d 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -419,6 +419,9 @@
 // Enables the network information API.
 const char kEnableNetworkInformation[]      = "enable-network-information";
 
+// Enables the network service.
+const char kEnableNetworkService[] = "enable-network-service";
+
 // Disables the video decoder from drawing to an NV12 textures instead of ARGB.
 const char kDisableNv12DxgiVideo[] = "disable-nv12-dxgi-video";
 
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index 3b9f899..545ec54 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -135,6 +135,7 @@
 CONTENT_EXPORT extern const char kEnableLCDText[];
 CONTENT_EXPORT extern const char kEnableLogging[];
 CONTENT_EXPORT extern const char kEnableNetworkInformation[];
+CONTENT_EXPORT extern const char kEnableNetworkService[];
 CONTENT_EXPORT extern const char kDisableNv12DxgiVideo[];
 CONTENT_EXPORT extern const char kEnablePinch[];
 CONTENT_EXPORT extern const char kEnablePluginPlaceholderTesting[];
diff --git a/content/public/common/service_names.mojom b/content/public/common/service_names.mojom
index d2bffb3b..7064122 100644
--- a/content/public/common/service_names.mojom
+++ b/content/public/common/service_names.mojom
@@ -33,3 +33,6 @@
 // them to the Service Manager. This must match the name in
 // src/content/public/app/mojo/content_utility_manifest.json.
 const string kUtilityServiceName = "content_utility";
+
+// The name for the network service running in the utility process.
+const string kNetworkServiceName = "network";
diff --git a/content/test/data/gpu/pixel_offscreenCanvas_2d_commit_main.html b/content/test/data/gpu/pixel_offscreenCanvas_2d_commit_main.html
index 193d1cd..4a008142 100644
--- a/content/test/data/gpu/pixel_offscreenCanvas_2d_commit_main.html
+++ b/content/test/data/gpu/pixel_offscreenCanvas_2d_commit_main.html
@@ -16,11 +16,11 @@
 </style>
 </head>
 <body onload="main()">
-<div style="position:relative; width:400px; height:200px; background-color:white">
+<div style="position:relative; width:360px; height:200px; background-color:white">
 </div>
 <div id="container" style="position:absolute; top:0px; left:0px">
-<canvas id="canvas1" width="200" height="200" class="nomargin"></canvas>
-<canvas id="canvas2" width="200" height="200" class="nomargin"></canvas>
+<canvas id="canvas1" width="180" height="200" class="nomargin"></canvas>
+<canvas id="canvas2" width="180" height="200" class="nomargin"></canvas>
 </div>
 <script>
 /* This pixel test checks the following:
@@ -47,12 +47,12 @@
   var ctx2 = getOffscreenContext("canvas2");
 
   ctx1.fillStyle = "green";
-  ctx1.fillRect(0, 0, 200, 200);
+  ctx1.fillRect(0, 0, 180, 200);
   // The promise returned by this ctx1.commit() must be resolved at
   // about the same time as the other ctx2.commit() below as they are in the
   // same JS task.
   ctx1.commit().then(function() {
-    ctx2.fillRect(0, 0, 200, 200);
+    ctx2.fillRect(0, 0, 180, 200);
     // This ctx2.commit() must happen after the other ctx2.commit() below.
     ctx2.commit();
     if (--g_asyncCallbackNumber == 0) waitForFinish();
@@ -88,18 +88,18 @@
 
   // Do something complex to ctx2.
   ctx2.fillStyle = "blue";
-  ctx2.fillRect(0, 0, 200, 200);
-  drawStar(ctx2, 100, 100, 25, 60, 40);
+  ctx2.fillRect(0, 0, 180, 200);
+  drawStar(ctx2, 90, 100, 25, 60, 40);
   // This ctx2.commit() must be resolved at about the same time as the first
   // ctx1.commit() above because they are in the same JS task, no matter how
   // complex the drawing operation is.
   ctx2.commit().then(function() {
-    drawStar(ctx1, 80, 80, 7, 60, 30);
+    drawStar(ctx1, 70, 80, 7, 60, 30);
     ctx1.commit();
 
     // The following fill is never committed
     ctx1.fillStyle = "red";
-    ctx1.fillRect(0, 0, 200, 200);
+    ctx1.fillRect(0, 0, 180, 200);
     if (--g_asyncCallbackNumber == 0) waitForFinish();
   });
 
diff --git a/content/test/data/gpu/pixel_offscreenCanvas_2d_commit_worker.html b/content/test/data/gpu/pixel_offscreenCanvas_2d_commit_worker.html
index feced124..0ceb819 100644
--- a/content/test/data/gpu/pixel_offscreenCanvas_2d_commit_worker.html
+++ b/content/test/data/gpu/pixel_offscreenCanvas_2d_commit_worker.html
@@ -38,12 +38,12 @@
 
 function startTest() {
   g_ctx1.fillStyle = "green";
-  g_ctx1.fillRect(0, 0, 200, 200);
+  g_ctx1.fillRect(0, 0, 180, 200);
   // The promise returned by this g_ctx1.commit() must be resolved at
   // about the same time as the other g_ctx2.commit() below as they are in the
   // same JS task.
   g_ctx1.commit().then(function() {
-    g_ctx2.fillRect(0, 0, 200, 200);
+    g_ctx2.fillRect(0, 0, 180, 200);
     // This g_ctx2.commit() must happen after the other g_ctx2.commit() below.
     g_ctx2.commit();
     if (--g_asyncCallbackNumber == 0) self.postMessage("");
@@ -79,18 +79,18 @@
 
   // Do something complex to g_ctx2.
   g_ctx2.fillStyle = "blue";
-  g_ctx2.fillRect(0, 0, 200, 200);
-  drawStar(g_ctx2, 100, 100, 25, 60, 40);
+  g_ctx2.fillRect(0, 0, 180, 200);
+  drawStar(g_ctx2, 90, 100, 25, 60, 40);
   // This g_ctx2.commit() must be resolved at about the same time as the first
   // g_ctx1.commit() above because they are in the same JS task, no matter how
   // complex the drawing operation is.
   g_ctx2.commit().then(function() {
-    drawStar(g_ctx1, 80, 80, 7, 60, 30);
+    drawStar(g_ctx1, 70, 80, 7, 60, 30);
     g_ctx1.commit();
 
     // The following fill is never committed
     g_ctx1.fillStyle = "red";
-    g_ctx1.fillRect(0, 0, 200, 200);
+    g_ctx1.fillRect(0, 0, 180, 200);
     if (--g_asyncCallbackNumber == 0) self.postMessage("");
   });
 
@@ -135,11 +135,11 @@
 </script>
 </head>
 <body onload="main()">
-<div style="position:relative; width:400px; height:200px; background-color:white">
+<div style="position:relative; width:360px; height:200px; background-color:white">
 </div>
 <div id="container" style="position:absolute; top:0px; left:0px">
-<canvas id="canvas1" width="200" height="200" class="nomargin"></canvas>
-<canvas id="canvas2" width="200" height="200" class="nomargin"></canvas>
+<canvas id="canvas1" width="180" height="200" class="nomargin"></canvas>
+<canvas id="canvas2" width="180" height="200" class="nomargin"></canvas>
 </div>
 </body>
 </html>
diff --git a/content/test/data/gpu/pixel_offscreenCanvas_webgl_commit_main.html b/content/test/data/gpu/pixel_offscreenCanvas_webgl_commit_main.html
index 3f0895d2..d314ffc 100644
--- a/content/test/data/gpu/pixel_offscreenCanvas_webgl_commit_main.html
+++ b/content/test/data/gpu/pixel_offscreenCanvas_webgl_commit_main.html
@@ -127,11 +127,11 @@
 </script>
 </head>
 <body onload="main()">
-<div style="position:relative; width:400px; height:200px; background-color:white">
+<div style="position:relative; width:360px; height:200px; background-color:white">
 </div>
 <div id="container" style="position:absolute; top:0px; left:0px">
-<canvas id="canvas1" width="200" height="200" class="nomargin"></canvas>
-<canvas id="canvas2" width="200" height="200" class="nomargin"></canvas>
+<canvas id="canvas1" width="180" height="200" class="nomargin"></canvas>
+<canvas id="canvas2" width="180" height="200" class="nomargin"></canvas>
 </div>
 </body>
 </html>
diff --git a/content/test/data/gpu/pixel_offscreenCanvas_webgl_commit_worker.html b/content/test/data/gpu/pixel_offscreenCanvas_webgl_commit_worker.html
index a3f916bb..d6b4ca5 100644
--- a/content/test/data/gpu/pixel_offscreenCanvas_webgl_commit_worker.html
+++ b/content/test/data/gpu/pixel_offscreenCanvas_webgl_commit_worker.html
@@ -147,11 +147,11 @@
 </script>
 </head>
 <body onload="main()">
-<div style="position:relative; width:400px; height:200px; background-color:white">
+<div style="position:relative; width:360px; height:200px; background-color:white">
 </div>
 <div id="container" style="position:absolute; top:0px; left:0px">
-<canvas id="canvas1" width="200" height="200" class="nomargin"></canvas>
-<canvas id="canvas2" width="200" height="200" class="nomargin"></canvas>
+<canvas id="canvas1" width="180" height="200" class="nomargin"></canvas>
+<canvas id="canvas2" width="180" height="200" class="nomargin"></canvas>
 </div>
 </body>
 </html>
diff --git a/content/test/gpu/gpu_tests/pixel_expectations.py b/content/test/gpu/gpu_tests/pixel_expectations.py
index 511b009..681236c 100644
--- a/content/test/gpu/gpu_tests/pixel_expectations.py
+++ b/content/test/gpu/gpu_tests/pixel_expectations.py
@@ -53,17 +53,17 @@
     # TODO(jbauman): Re-enable when references images created.
     self.Fail('Pixel_DirectComposition_Video_*', ['win'], bug=704389)
 
-    # TODO(xlai): Remove this after test dimension is shrunk
-    # Intended to skip only Nexus 5
-    self.Skip('Pixel_OffscreenCanvasAccelerated2D',
-        ['android', ('qualcomm', 'Adreno (TM) 330')], bug=706647)
-    self.Skip('Pixel_OffscreenCanvasAccelerated2DWorker',
-        ['android', ('qualcomm', 'Adreno (TM) 330')], bug=706647)
-    self.Skip('Pixel_OffscreenCanvasUnaccelerated2DGPUCompositing',
-        ['android', ('qualcomm', 'Adreno (TM) 330')], bug=706647)
-    self.Skip('Pixel_OffscreenCanvasUnaccelerated2DGPUCompositingWorker',
-        ['android', ('qualcomm', 'Adreno (TM) 330')], bug=706647)
-    self.Skip('Pixel_OffscreenCanvasWebGLDefault',
-        ['android', ('qualcomm', 'Adreno (TM) 330')], bug=706647)
-    self.Skip('Pixel_OffscreenCanvasWebGLDefaultWorker',
-        ['android', ('qualcomm', 'Adreno (TM) 330')], bug=706647)
+    # TODO(xlai): Remove this after verifying reference images.
+    self.Fail('Pixel_OffscreenCanvasAccelerated2D', bug=706647)
+    self.Fail('Pixel_OffscreenCanvasAccelerated2DWorker', bug=706647)
+    self.Fail('Pixel_OffscreenCanvasUnaccelerated2D', bug=706647)
+    self.Fail('Pixel_OffscreenCanvasUnaccelerated2DWorker', bug=706647)
+    self.Fail('Pixel_OffscreenCanvasUnaccelerated2DGPUCompositing',
+        bug=706647)
+    self.Fail('Pixel_OffscreenCanvasUnaccelerated2DGPUCompositingWorker',
+        bug=706647)
+    self.Fail('Pixel_OffscreenCanvasWebGLDefault', bug=706647)
+    self.Fail('Pixel_OffscreenCanvasWebGLDefaultWorker', bug=706647)
+    self.Fail('Pixel_OffscreenCanvasWebGLSoftwareCompositing', bug=706647)
+    self.Fail('Pixel_OffscreenCanvasWebGLSoftwareCompositingWorker',
+        bug=706647)
diff --git a/content/test/gpu/gpu_tests/pixel_test_pages.py b/content/test/gpu/gpu_tests/pixel_test_pages.py
index acc40096..5d85784 100644
--- a/content/test/gpu/gpu_tests/pixel_test_pages.py
+++ b/content/test/gpu/gpu_tests/pixel_test_pages.py
@@ -319,71 +319,71 @@
     PixelTestPage(
       'pixel_offscreenCanvas_webgl_commit_main.html',
       base_name + '_OffscreenCanvasWebGLDefault',
-      test_rect=[0, 0, 400, 200],
-      revision=3,
+      test_rect=[0, 0, 360, 200],
+      revision=4,
       browser_args=browser_args),
 
     PixelTestPage(
       'pixel_offscreenCanvas_webgl_commit_worker.html',
       base_name + '_OffscreenCanvasWebGLDefaultWorker',
-      test_rect=[0, 0, 400, 200],
-      revision=3,
+      test_rect=[0, 0, 360, 200],
+      revision=4,
       browser_args=browser_args),
 
     PixelTestPage(
       'pixel_offscreenCanvas_webgl_commit_main.html',
       base_name + '_OffscreenCanvasWebGLSoftwareCompositing',
-      test_rect=[0, 0, 400, 200],
-      revision=4,
+      test_rect=[0, 0, 360, 200],
+      revision=5,
       browser_args=browser_args + ['--disable-gpu-compositing']),
 
     PixelTestPage(
       'pixel_offscreenCanvas_webgl_commit_worker.html',
       base_name + '_OffscreenCanvasWebGLSoftwareCompositingWorker',
-      test_rect=[0, 0, 400, 200],
-      revision=4,
+      test_rect=[0, 0, 360, 200],
+      revision=5,
       browser_args=browser_args + ['--disable-gpu-compositing']),
 
     PixelTestPage(
       'pixel_offscreenCanvas_2d_commit_main.html',
       base_name + '_OffscreenCanvasAccelerated2D',
-      test_rect=[0, 0, 400, 200],
-      revision=5,
+      test_rect=[0, 0, 360, 200],
+      revision=6,
       browser_args=browser_args),
 
     PixelTestPage(
       'pixel_offscreenCanvas_2d_commit_worker.html',
       base_name + '_OffscreenCanvasAccelerated2DWorker',
-      test_rect=[0, 0, 400, 200],
-      revision=5,
+      test_rect=[0, 0, 360, 200],
+      revision=6,
       browser_args=browser_args),
 
     PixelTestPage(
       'pixel_offscreenCanvas_2d_commit_main.html',
       base_name + '_OffscreenCanvasUnaccelerated2D',
-      test_rect=[0, 0, 400, 200],
-      revision=4,
+      test_rect=[0, 0, 360, 200],
+      revision=5,
       browser_args=browser_args + unaccelerated_args),
 
     PixelTestPage(
       'pixel_offscreenCanvas_2d_commit_worker.html',
       base_name + '_OffscreenCanvasUnaccelerated2DWorker',
-      test_rect=[0, 0, 400, 200],
-      revision=4,
+      test_rect=[0, 0, 360, 200],
+      revision=5,
       browser_args=browser_args + unaccelerated_args),
 
     PixelTestPage(
       'pixel_offscreenCanvas_2d_commit_main.html',
       base_name + '_OffscreenCanvasUnaccelerated2DGPUCompositing',
-      test_rect=[0, 0, 400, 200],
-      revision=6,
+      test_rect=[0, 0, 360, 200],
+      revision=7,
       browser_args=browser_args + ['--disable-accelerated-2d-canvas']),
 
     PixelTestPage(
       'pixel_offscreenCanvas_2d_commit_worker.html',
       base_name + '_OffscreenCanvasUnaccelerated2DGPUCompositingWorker',
-      test_rect=[0, 0, 400, 200],
-      revision=6,
+      test_rect=[0, 0, 360, 200],
+      revision=7,
       browser_args=browser_args + ['--disable-accelerated-2d-canvas']),
 
     PixelTestPage(
diff --git a/content/utility/BUILD.gn b/content/utility/BUILD.gn
index a589df1..011ff73 100644
--- a/content/utility/BUILD.gn
+++ b/content/utility/BUILD.gn
@@ -31,6 +31,7 @@
     "//base",
     "//content:export",
     "//content/child",
+    "//content/network:lib",
     "//content/public/child:child_sources",
     "//content/public/common:common_sources",
     "//mojo/common",
diff --git a/content/utility/DEPS b/content/utility/DEPS
index 74ac29f..26456d68 100644
--- a/content/utility/DEPS
+++ b/content/utility/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+components/scheduler/child",
   "+content/child",
+  "+content/network",
   "+content/public/utility",
   "+services/data_decoder",
   "+services/service_manager",
diff --git a/content/utility/utility_service_factory.cc b/content/utility/utility_service_factory.cc
index 9a28ba4..a1c8336 100644
--- a/content/utility/utility_service_factory.cc
+++ b/content/utility/utility_service_factory.cc
@@ -5,7 +5,11 @@
 #include "content/utility/utility_service_factory.h"
 
 #include "base/bind.h"
+#include "base/command_line.h"
+#include "content/network/network_service.h"
 #include "content/public/common/content_client.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/service_names.mojom.h"
 #include "content/public/utility/content_utility_client.h"
 #include "content/public/utility/utility_thread.h"
 #include "content/utility/utility_thread_impl.h"
@@ -51,6 +55,14 @@
   data_decoder_info.factory = base::Bind(&CreateDataDecoderService);
   services->insert(
       std::make_pair(data_decoder::mojom::kServiceName, data_decoder_info));
+
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableNetworkService)) {
+    ServiceInfo network_info;
+    network_info.factory = base::Bind(&NetworkService::CreateNetworkService);
+    services->insert(
+        std::make_pair(content::mojom::kNetworkServiceName, network_info));
+  }
 }
 
 void UtilityServiceFactory::OnServiceQuit() {
diff --git a/device/bluetooth/bluetooth_low_energy_device_mac.h b/device/bluetooth/bluetooth_low_energy_device_mac.h
index 0b1aafd..e2823d1 100644
--- a/device/bluetooth/bluetooth_low_energy_device_mac.h
+++ b/device/bluetooth/bluetooth_low_energy_device_mac.h
@@ -28,6 +28,7 @@
 
 class BluetoothAdapterMac;
 class BluetoothRemoteGattServiceMac;
+class BluetoothRemoteGattDescriptorMac;
 
 class DEVICE_BLUETOOTH_EXPORT BluetoothLowEnergyDeviceMac
     : public BluetoothDeviceMac {
@@ -90,6 +91,8 @@
   void DidUpdateNotificationState(CBCharacteristic* characteristic,
                                   NSError* error);
   void DidDiscoverDescriptors(CBCharacteristic* characteristic, NSError* error);
+  void DidUpdateValueForDescriptor(CBDescriptor* cb_descriptor, NSError* error);
+  void DidWriteValueForDescriptor(CBDescriptor* descriptor, NSError* error);
 
   static std::string GetPeripheralIdentifier(CBPeripheral* peripheral);
 
@@ -123,6 +126,10 @@
   BluetoothRemoteGattServiceMac* GetBluetoothRemoteGattService(
       CBService* service) const;
 
+  // Returns BluetoothRemoteGattDescriptorMac based on the CBDescriptor.
+  BluetoothRemoteGattDescriptorMac* GetBluetoothRemoteGattDescriptor(
+      CBDescriptor* cb_descriptor) const;
+
   // Callback used when the CoreBluetooth Peripheral is disconnected.
   void DidDisconnectPeripheral(NSError* error);
 
diff --git a/device/bluetooth/bluetooth_low_energy_device_mac.mm b/device/bluetooth/bluetooth_low_energy_device_mac.mm
index b5777be..20694e4 100644
--- a/device/bluetooth/bluetooth_low_energy_device_mac.mm
+++ b/device/bluetooth/bluetooth_low_energy_device_mac.mm
@@ -16,6 +16,8 @@
 #include "device/bluetooth/bluetooth_adapter_mac.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/bluetooth_low_energy_peripheral_delegate.h"
+#include "device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h"
+#include "device/bluetooth/bluetooth_remote_gatt_descriptor_mac.h"
 #include "device/bluetooth/bluetooth_remote_gatt_service_mac.h"
 
 using device::BluetoothDevice;
@@ -332,6 +334,24 @@
   SendNotificationIfDiscoveryComplete();
 }
 
+void BluetoothLowEnergyDeviceMac::DidUpdateValueForDescriptor(
+    CBDescriptor* cb_descriptor,
+    NSError* error) {
+  BluetoothRemoteGattDescriptorMac* gatt_descriptor =
+      GetBluetoothRemoteGattDescriptor(cb_descriptor);
+  DCHECK(gatt_descriptor);
+  gatt_descriptor->DidUpdateValueForDescriptor(error);
+}
+
+void BluetoothLowEnergyDeviceMac::DidWriteValueForDescriptor(
+    CBDescriptor* cb_descriptor,
+    NSError* error) {
+  BluetoothRemoteGattDescriptorMac* gatt_descriptor =
+      GetBluetoothRemoteGattDescriptor(cb_descriptor);
+  DCHECK(gatt_descriptor);
+  gatt_descriptor->DidWriteValueForDescriptor(error);
+}
+
 // static
 std::string BluetoothLowEnergyDeviceMac::GetPeripheralIdentifier(
     CBPeripheral* peripheral) {
@@ -400,6 +420,20 @@
   return nullptr;
 }
 
+device::BluetoothRemoteGattDescriptorMac*
+BluetoothLowEnergyDeviceMac::GetBluetoothRemoteGattDescriptor(
+    CBDescriptor* cb_descriptor) const {
+  CBCharacteristic* cb_characteristic = cb_descriptor.characteristic;
+  device::BluetoothRemoteGattServiceMac* gatt_service =
+      GetBluetoothRemoteGattService(cb_characteristic.service);
+  DCHECK(gatt_service);
+  device::BluetoothRemoteGattCharacteristicMac* gatt_characteristic =
+      gatt_service->GetBluetoothRemoteGattCharacteristicMac(cb_characteristic);
+  DCHECK(gatt_characteristic);
+  return gatt_characteristic->GetBluetoothRemoteGattDescriptorMac(
+      cb_descriptor);
+}
+
 void BluetoothLowEnergyDeviceMac::DidDisconnectPeripheral(NSError* error) {
   VLOG(1) << *this << ": Disconnected from peripheral.";
   if (error) {
diff --git a/device/bluetooth/bluetooth_low_energy_peripheral_delegate.mm b/device/bluetooth/bluetooth_low_energy_peripheral_delegate.mm
index 4f8a1b85..e0287fff 100644
--- a/device/bluetooth/bluetooth_low_energy_peripheral_delegate.mm
+++ b/device/bluetooth/bluetooth_low_energy_peripheral_delegate.mm
@@ -49,6 +49,14 @@
     device_mac_->DidDiscoverDescriptors(characteristic, error);
   }
 
+  void DidUpdateValueForDescriptor(CBDescriptor* descriptor, NSError* error) {
+    device_mac_->DidUpdateValueForDescriptor(descriptor, error);
+  }
+
+  void DidWriteValueForDescriptor(CBDescriptor* descriptor, NSError* error) {
+    device_mac_->DidWriteValueForDescriptor(descriptor, error);
+  }
+
   CBPeripheral* GetPeripheral() { return device_mac_->GetPeripheral(); }
 
  private:
@@ -113,4 +121,16 @@
   bridge_->DidDiscoverDescriptors(characteristic, error);
 }
 
+- (void)peripheral:(CBPeripheral*)peripheral
+    didUpdateValueForDescriptor:(CBDescriptor*)descriptor
+                          error:(nullable NSError*)error {
+  bridge_->DidUpdateValueForDescriptor(descriptor, error);
+}
+
+- (void)peripheral:(CBPeripheral*)peripheral
+    didWriteValueForDescriptor:(CBDescriptor*)descriptor
+                         error:(nullable NSError*)error {
+  bridge_->DidWriteValueForDescriptor(descriptor, error);
+}
+
 @end
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h b/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h
index 229daff6..384c2658 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h
@@ -7,18 +7,12 @@
 
 #include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
 
+#import <CoreBluetooth/CoreBluetooth.h>
 #include <unordered_map>
 
 #include "base/mac/scoped_nsobject.h"
 #include "base/memory/weak_ptr.h"
 
-#if defined(__OBJC__)
-#import <CoreBluetooth/CoreBluetooth.h>
-#else
-@class CBCharacteristic;
-typedef NS_ENUM(NSInteger, CBCharacteristicWriteType);
-#endif  // defined(__OBJC__)
-
 namespace device {
 
 class BluetoothAdapterMac;
@@ -66,6 +60,7 @@
       const ErrorCallback& error_callback) override;
 
  private:
+  friend class BluetoothLowEnergyDeviceMac;
   friend class BluetoothRemoteGattDescriptorMac;
   friend class BluetoothRemoteGattServiceMac;
   friend class BluetoothTestMac;
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.h b/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.h
index 60d4062..e7c10354 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.h
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.h
@@ -44,8 +44,19 @@
                              const ErrorCallback& error_callback) override;
 
  private:
+  friend class BluetoothLowEnergyDeviceMac;
   friend class BluetoothRemoteGattCharacteristicMac;
+  friend class BluetoothTestMac;
 
+  // Calls callbacks, when -[id<CBPeripheralDelegate>
+  // peripheral:didUpdateValueForDescriptor:error:] is called.
+  void DidUpdateValueForDescriptor(NSError* error);
+  // Calls callbacks, when -[id<CBPeripheralDelegate>
+  // peripheral:didWriteValueForDescriptor:error:] is called.
+  void DidWriteValueForDescriptor(NSError* error);
+
+  // Returns CoreBluetooth peripheral.
+  CBPeripheral* GetCBPeripheral() const;
   // Returns CoreBluetooth descriptor.
   CBDescriptor* GetCBDescriptor() const;
   // gatt_characteristic_ owns instances of this class.
@@ -58,6 +69,12 @@
   BluetoothUUID uuid_;
   // Descriptor value.
   std::vector<uint8_t> value_;
+  // True if a gatt read or write request is in progress.
+  bool value_read_or_write_in_progress_;
+  // ReadRemoteDescriptor request callbacks.
+  std::pair<ValueCallback, ErrorCallback> read_value_callbacks_;
+  // WriteRemoteDescriptor request callbacks.
+  std::pair<base::Closure, ErrorCallback> write_value_callbacks_;
 };
 
 // Stream operator for logging.
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.mm b/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.mm
index 76cda0c..d7c5d6c7 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.mm
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.mm
@@ -4,19 +4,51 @@
 
 #include "device/bluetooth/bluetooth_remote_gatt_descriptor_mac.h"
 
+#include "base/bind.h"
+#import "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
-#include "device/bluetooth/bluetooth_adapter_mac.h"
-#include "device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h"
+#include "base/threading/thread_task_runner_handle.h"
+#import "device/bluetooth/bluetooth_adapter_mac.h"
+#import "device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h"
+
+using base::mac::ObjCCast;
 
 namespace device {
 
+std::vector<uint8_t> VectorValueFromObjC(id objc_value) {
+  // According to
+  // https://developer.apple.com/reference/corebluetooth/cbdescriptor some
+  // descriptor values can be NSData, NSString or NSNumber.
+  std::vector<uint8_t> value;
+  NSData* data = ObjCCast<NSData>(objc_value);
+  NSString* as_string = ObjCCast<NSString>(objc_value);
+  NSNumber* as_number = ObjCCast<NSNumber>(objc_value);
+
+  if (!data && !as_string && as_number) {
+    unsigned short descriptor = [as_number shortValue];
+    data = [NSData dataWithBytes:&descriptor length:sizeof(descriptor)];
+  }
+
+  if (!data && as_string)
+    data = [as_string dataUsingEncoding:NSUTF8StringEncoding];
+
+  if (data) {
+    value.resize([data length]);
+    [data getBytes:value.data() length:value.size()];
+  } else {
+    LOG(WARNING) << "Unexpected value: "
+                 << base::SysNSStringToUTF8([objc_value description]);
+  }
+  return value;
+}
+
 BluetoothRemoteGattDescriptorMac::BluetoothRemoteGattDescriptorMac(
     BluetoothRemoteGattCharacteristicMac* characteristic,
     CBDescriptor* descriptor)
     : gatt_characteristic_(characteristic),
-      cb_descriptor_(descriptor, base::scoped_policy::RETAIN) {
-  uuid_ =
-      BluetoothAdapterMac::BluetoothUUIDWithCBUUID([cb_descriptor_.get() UUID]);
+      cb_descriptor_(descriptor, base::scoped_policy::RETAIN),
+      value_read_or_write_in_progress_(false) {
+  uuid_ = BluetoothAdapterMac::BluetoothUUIDWithCBUUID([cb_descriptor_ UUID]);
   identifier_ = base::SysNSStringToUTF8(
       [NSString stringWithFormat:@"%s-%p", uuid_.canonical_value().c_str(),
                                  (void*)cb_descriptor_]);
@@ -40,10 +72,19 @@
   return value_;
 }
 
-BluetoothRemoteGattDescriptorMac::~BluetoothRemoteGattDescriptorMac() {}
+BluetoothRemoteGattDescriptorMac::~BluetoothRemoteGattDescriptorMac() {
+  if (!read_value_callbacks_.first.is_null()) {
+    std::pair<ValueCallback, ErrorCallback> callbacks;
+    callbacks.swap(read_value_callbacks_);
+    callbacks.second.Run(BluetoothGattService::GATT_ERROR_FAILED);
+  }
+  if (!write_value_callbacks_.first.is_null()) {
+    std::pair<base::Closure, ErrorCallback> callbacks;
+    callbacks.swap(write_value_callbacks_);
+    callbacks.second.Run(BluetoothGattService::GATT_ERROR_FAILED);
+  }
+}
 
-// Returns a pointer to the GATT characteristic that this characteristic
-// descriptor belongs to.
 BluetoothRemoteGattCharacteristic*
 BluetoothRemoteGattDescriptorMac::GetCharacteristic() const {
   return static_cast<BluetoothRemoteGattCharacteristic*>(gatt_characteristic_);
@@ -55,23 +96,91 @@
 void BluetoothRemoteGattDescriptorMac::ReadRemoteDescriptor(
     const ValueCallback& callback,
     const ErrorCallback& error_callback) {
-  NOTIMPLEMENTED();
+  if (value_read_or_write_in_progress_) {
+    VLOG(1) << *this << ": Read failed, already in progress.";
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::Bind(error_callback,
+                   BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS));
+    return;
+  }
+  VLOG(1) << *this << ": Read value.";
+  value_read_or_write_in_progress_ = true;
+  read_value_callbacks_ = std::make_pair(callback, error_callback);
+  [GetCBPeripheral() readValueForDescriptor:cb_descriptor_];
 }
 
-// Sends a write request to a remote characteristic descriptor, to modify the
-// value of the descriptor with the new value |new_value|. |callback| is
-// called to signal success and |error_callback| for failures. This method
-// only applies to remote descriptors and will fail for those that are locally
-// hosted.
 void BluetoothRemoteGattDescriptorMac::WriteRemoteDescriptor(
-    const std::vector<uint8_t>& new_value,
+    const std::vector<uint8_t>& value,
     const base::Closure& callback,
     const ErrorCallback& error_callback) {
-  NOTIMPLEMENTED();
+  if (value_read_or_write_in_progress_) {
+    VLOG(1) << *this << ": Write failed, already in progress.";
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::Bind(error_callback,
+                   BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS));
+    return;
+  }
+  VLOG(1) << *this << ": Write value.";
+  value_read_or_write_in_progress_ = true;
+  write_value_callbacks_ = std::make_pair(callback, error_callback);
+  base::scoped_nsobject<NSData> nsdata_value(
+      [[NSData alloc] initWithBytes:value.data() length:value.size()]);
+  [GetCBPeripheral() writeValue:nsdata_value forDescriptor:GetCBDescriptor()];
+}
+
+void BluetoothRemoteGattDescriptorMac::DidUpdateValueForDescriptor(
+    NSError* error) {
+  if (!value_read_or_write_in_progress_) {
+    VLOG(1) << *this << ": Value updated, no read in progress.";
+    return;
+  }
+  std::pair<ValueCallback, ErrorCallback> callbacks;
+  callbacks.swap(read_value_callbacks_);
+  value_read_or_write_in_progress_ = false;
+  if (error) {
+    BluetoothGattService::GattErrorCode error_code =
+        BluetoothDeviceMac::GetGattErrorCodeFromNSError(error);
+    VLOG(1) << *this << ": Read value failed with error: "
+            << BluetoothAdapterMac::String(error)
+            << ", converted to error code: " << error_code;
+    callbacks.second.Run(error_code);
+    return;
+  }
+  VLOG(1) << *this << ": Value read.";
+  value_ = VectorValueFromObjC([cb_descriptor_ value]);
+  callbacks.first.Run(value_);
+}
+
+void BluetoothRemoteGattDescriptorMac::DidWriteValueForDescriptor(
+    NSError* error) {
+  if (!value_read_or_write_in_progress_) {
+    VLOG(1) << *this << ": Value written, no write in progress.";
+    return;
+  }
+  std::pair<base::Closure, ErrorCallback> callbacks;
+  callbacks.swap(write_value_callbacks_);
+  value_read_or_write_in_progress_ = false;
+  if (error) {
+    BluetoothGattService::GattErrorCode error_code =
+        BluetoothDeviceMac::GetGattErrorCodeFromNSError(error);
+    VLOG(1) << *this << ": Write value failed with error: "
+            << BluetoothAdapterMac::String(error)
+            << ", converted to error code: " << error_code;
+    callbacks.second.Run(error_code);
+    return;
+  }
+  VLOG(1) << *this << ": Value written.";
+  callbacks.first.Run();
+}
+
+CBPeripheral* BluetoothRemoteGattDescriptorMac::GetCBPeripheral() const {
+  return gatt_characteristic_->GetCBPeripheral();
 }
 
 CBDescriptor* BluetoothRemoteGattDescriptorMac::GetCBDescriptor() const {
-  return cb_descriptor_.get();
+  return cb_descriptor_;
 }
 
 DEVICE_BLUETOOTH_EXPORT std::ostream& operator<<(
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc b/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc
index 3b886bb..a43c0e7 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc
@@ -184,9 +184,13 @@
 }
 #endif  // defined(OS_ANDROID) || defined(OS_MACOSX)
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
 // Tests ReadRemoteDescriptor and GetValue with empty value buffer.
 TEST_F(BluetoothRemoteGattDescriptorTest, ReadRemoteDescriptor_Empty) {
+  if (!PlatformSupportsLowEnergy()) {
+    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+    return;
+  }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
   descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED),
@@ -203,11 +207,15 @@
   EXPECT_EQ(empty_vector, last_read_value_);
   EXPECT_EQ(empty_vector, descriptor1_->GetValue());
 }
-#endif  // defined(OS_ANDROID)
+#endif  // defined(OS_ANDROID) || defined(OS_MACOSX)
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
 // Tests WriteRemoteDescriptor with empty value buffer.
 TEST_F(BluetoothRemoteGattDescriptorTest, WriteRemoteDescriptor_Empty) {
+  if (!PlatformSupportsLowEnergy()) {
+    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+    return;
+  }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
   std::vector<uint8_t> empty_vector;
@@ -223,11 +231,17 @@
 
   EXPECT_EQ(empty_vector, last_write_value_);
 }
-#endif  // defined(OS_ANDROID)
+#endif  // defined(OS_ANDROID) || defined(OS_MACOSX)
 
 #if defined(OS_ANDROID)
 // Tests ReadRemoteDescriptor completing after Chrome objects are deleted.
+// macOS: Not applicable: This can never happen if CBPeripheral delegate is set
+// to nil.
 TEST_F(BluetoothRemoteGattDescriptorTest, ReadRemoteDescriptor_AfterDeleted) {
+  if (!PlatformSupportsLowEnergy()) {
+    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+    return;
+  }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
   descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::NOT_EXPECTED),
@@ -246,7 +260,13 @@
 
 #if defined(OS_ANDROID)
 // Tests WriteRemoteDescriptor completing after Chrome objects are deleted.
+// macOS: Not applicable: This can never happen if CBPeripheral delegate is set
+// to nil.
 TEST_F(BluetoothRemoteGattDescriptorTest, WriteRemoteDescriptor_AfterDeleted) {
+  if (!PlatformSupportsLowEnergy()) {
+    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+    return;
+  }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
   std::vector<uint8_t> empty_vector;
@@ -263,9 +283,13 @@
 }
 #endif  // defined(OS_ANDROID)
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
 // Tests ReadRemoteDescriptor and GetValue with non-empty value buffer.
 TEST_F(BluetoothRemoteGattDescriptorTest, ReadRemoteDescriptor) {
+  if (!PlatformSupportsLowEnergy()) {
+    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+    return;
+  }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
   descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED),
@@ -285,11 +309,15 @@
   EXPECT_EQ(test_vector, last_read_value_);
   EXPECT_EQ(test_vector, descriptor1_->GetValue());
 }
-#endif  // defined(OS_ANDROID)
+#endif  // defined(OS_ANDROID) || defined(OS_MACOSX)
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
 // Tests WriteRemoteDescriptor with non-empty value buffer.
 TEST_F(BluetoothRemoteGattDescriptorTest, WriteRemoteDescriptor) {
+  if (!PlatformSupportsLowEnergy()) {
+    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+    return;
+  }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
   uint8_t values[] = {0, 1, 2, 3, 4, 0xf, 0xf0, 0xff};
@@ -303,11 +331,15 @@
 
   EXPECT_EQ(test_vector, last_write_value_);
 }
-#endif  // defined(OS_ANDROID)
+#endif  // defined(OS_ANDROID) || defined(OS_MACOSX)
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
 // Tests ReadRemoteDescriptor and GetValue multiple times.
 TEST_F(BluetoothRemoteGattDescriptorTest, ReadRemoteDescriptor_Twice) {
+  if (!PlatformSupportsLowEnergy()) {
+    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+    return;
+  }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
   descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED),
@@ -336,11 +368,15 @@
   EXPECT_EQ(empty_vector, last_read_value_);
   EXPECT_EQ(empty_vector, descriptor1_->GetValue());
 }
-#endif  // defined(OS_ANDROID)
+#endif  // defined(OS_ANDROID) || defined(OS_MACOSX)
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
 // Tests WriteRemoteDescriptor multiple times.
 TEST_F(BluetoothRemoteGattDescriptorTest, WriteRemoteDescriptor_Twice) {
+  if (!PlatformSupportsLowEnergy()) {
+    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+    return;
+  }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
   uint8_t values[] = {0, 1, 2, 3, 4, 0xf, 0xf0, 0xff};
@@ -367,12 +403,16 @@
   EXPECT_EQ(0, error_callback_count_);
   EXPECT_EQ(empty_vector, last_write_value_);
 }
-#endif  // defined(OS_ANDROID)
+#endif  // defined(OS_ANDROID) || defined(OS_MACOSX)
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
 // Tests ReadRemoteDescriptor on two descriptors.
 TEST_F(BluetoothRemoteGattDescriptorTest,
        ReadRemoteDescriptor_MultipleDescriptors) {
+  if (!PlatformSupportsLowEnergy()) {
+    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+    return;
+  }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
   descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED),
@@ -400,12 +440,16 @@
   EXPECT_EQ(test_vector1, descriptor1_->GetValue());
   EXPECT_EQ(test_vector2, descriptor2_->GetValue());
 }
-#endif  // defined(OS_ANDROID)
+#endif  // defined(OS_ANDROID) || defined(OS_MACOSX)
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
 // Tests WriteRemoteDescriptor on two descriptors.
 TEST_F(BluetoothRemoteGattDescriptorTest,
        WriteRemoteDescriptor_MultipleDescriptors) {
+  if (!PlatformSupportsLowEnergy()) {
+    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+    return;
+  }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
   std::vector<uint8_t> test_vector1;
@@ -431,11 +475,15 @@
   EXPECT_EQ(2, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
 }
-#endif  // defined(OS_ANDROID)
+#endif  // defined(OS_ANDROID) || defined(OS_MACOSX)
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
 // Tests ReadRemoteDescriptor asynchronous error.
 TEST_F(BluetoothRemoteGattDescriptorTest, ReadError) {
+  if (!PlatformSupportsLowEnergy()) {
+    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+    return;
+  }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
   descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::NOT_EXPECTED),
@@ -448,11 +496,15 @@
   EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_INVALID_LENGTH,
             last_gatt_error_code_);
 }
-#endif  // defined(OS_ANDROID)
+#endif  // defined(OS_ANDROID) || defined(OS_MACOSX)
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
 // Tests WriteRemoteDescriptor asynchronous error.
 TEST_F(BluetoothRemoteGattDescriptorTest, WriteError) {
+  if (!PlatformSupportsLowEnergy()) {
+    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+    return;
+  }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
   std::vector<uint8_t> empty_vector;
@@ -468,11 +520,17 @@
   EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_INVALID_LENGTH,
             last_gatt_error_code_);
 }
-#endif  // defined(OS_ANDROID)
+#endif  // defined(OS_ANDROID) || defined(OS_MACOSX)
 
 #if defined(OS_ANDROID)
 // Tests ReadRemoteDescriptor synchronous error.
+// Test not relevant for macOS since descriptor read cannot generate
+// synchronous error.
 TEST_F(BluetoothRemoteGattDescriptorTest, ReadSynchronousError) {
+  if (!PlatformSupportsLowEnergy()) {
+    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+    return;
+  }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
   SimulateGattDescriptorReadWillFailSynchronouslyOnce(descriptor1_);
@@ -500,7 +558,13 @@
 
 #if defined(OS_ANDROID)
 // Tests WriteRemoteDescriptor synchronous error.
+// Test not relevant for macOS since descriptor write cannot generate
+// synchronous error.
 TEST_F(BluetoothRemoteGattDescriptorTest, WriteSynchronousError) {
+  if (!PlatformSupportsLowEnergy()) {
+    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+    return;
+  }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
   SimulateGattDescriptorWriteWillFailSynchronouslyOnce(descriptor1_);
@@ -527,9 +591,13 @@
 }
 #endif  // defined(OS_ANDROID)
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
 // Tests ReadRemoteDescriptor error with a pending read operation.
 TEST_F(BluetoothRemoteGattDescriptorTest, ReadRemoteDescriptor_ReadPending) {
+  if (!PlatformSupportsLowEnergy()) {
+    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+    return;
+  }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
   descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED),
@@ -552,11 +620,15 @@
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
 }
-#endif  // defined(OS_ANDROID)
+#endif  // defined(OS_ANDROID) || defined(OS_MACOSX)
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
 // Tests WriteRemoteDescriptor error with a pending write operation.
 TEST_F(BluetoothRemoteGattDescriptorTest, WriteRemoteDescriptor_WritePending) {
+  if (!PlatformSupportsLowEnergy()) {
+    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+    return;
+  }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
   std::vector<uint8_t> empty_vector;
@@ -580,11 +652,15 @@
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
 }
-#endif  // defined(OS_ANDROID)
+#endif  // defined(OS_ANDROID) || defined(OS_MACOSX)
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
 // Tests ReadRemoteDescriptor error with a pending write operation.
 TEST_F(BluetoothRemoteGattDescriptorTest, ReadRemoteDescriptor_WritePending) {
+  if (!PlatformSupportsLowEnergy()) {
+    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+    return;
+  }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
   std::vector<uint8_t> empty_vector;
@@ -607,11 +683,15 @@
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
 }
-#endif  // defined(OS_ANDROID)
+#endif  // defined(OS_ANDROID) || defined(OS_MACOSX)
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
 // Tests WriteRemoteDescriptor error with a pending Read operation.
 TEST_F(BluetoothRemoteGattDescriptorTest, WriteRemoteDescriptor_ReadPending) {
+  if (!PlatformSupportsLowEnergy()) {
+    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+    return;
+  }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
   std::vector<uint8_t> empty_vector;
@@ -634,7 +714,7 @@
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
 }
-#endif  // defined(OS_ANDROID)
+#endif  // defined(OS_ANDROID) || defined(OS_MACOSX)
 
 #if defined(OS_ANDROID)
 // Tests that read requests after a device disconnects but before the
@@ -685,4 +765,51 @@
 }
 #endif  // defined(OS_ANDROID)
 
+#if defined(OS_MACOSX)
+// Tests NSString for descriptor value for macOS.
+// https://developer.apple.com/reference/corebluetooth/cbdescriptor
+TEST_F(BluetoothRemoteGattDescriptorTest, ReadRemoteDescriptor_NSString) {
+  if (!PlatformSupportsLowEnergy()) {
+    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+    return;
+  }
+  ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
+
+  descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED),
+                                     GetGattErrorCallback(Call::NOT_EXPECTED));
+  EXPECT_EQ(1, gatt_read_descriptor_attempts_);
+
+  std::string test_string = "Hello";
+  SimulateGattDescriptorReadNSString(descriptor1_, test_string);
+  base::RunLoop().RunUntilIdle();
+
+  std::vector<uint8_t> test_vector(test_string.begin(), test_string.end());
+  EXPECT_EQ(test_vector, last_read_value_);
+  EXPECT_EQ(test_vector, descriptor1_->GetValue());
+}
+
+// Tests NSNumber for descriptor value for macOS.
+// https://developer.apple.com/reference/corebluetooth/cbdescriptor
+TEST_F(BluetoothRemoteGattDescriptorTest, ReadRemoteDescriptor_NSNumber) {
+  if (!PlatformSupportsLowEnergy()) {
+    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+    return;
+  }
+  ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
+
+  descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED),
+                                     GetGattErrorCallback(Call::NOT_EXPECTED));
+  EXPECT_EQ(1, gatt_read_descriptor_attempts_);
+
+  const short test_number = 0x1234;
+  SimulateGattDescriptorReadNSNumber(descriptor1_, test_number);
+  base::RunLoop().RunUntilIdle();
+
+  uint8_t values[] = {0x34, 0x12};
+  std::vector<uint8_t> test_vector(values, values + arraysize(values));
+  EXPECT_EQ(test_vector, last_read_value_);
+  EXPECT_EQ(test_vector, descriptor1_->GetValue());
+}
+#endif  // defined(OS_MACOSX)
+
 }  // namespace device
diff --git a/device/bluetooth/test/bluetooth_test_mac.h b/device/bluetooth/test/bluetooth_test_mac.h
index 46e168a..5eb00426 100644
--- a/device/bluetooth/test/bluetooth_test_mac.h
+++ b/device/bluetooth/test/bluetooth_test_mac.h
@@ -11,9 +11,11 @@
 #include "device/bluetooth/test/bluetooth_test.h"
 
 #if __OBJC__
+@class MockCBDescriptor;
 @class MockCBCharacteristic;
 @class MockCBPeripheral;
 #else   // __OBJC__
+class MockCBDescriptor;
 class MockCBCharacteristic;
 class MockCBPeripheral;
 #endif  // __OBJC__
@@ -85,6 +87,16 @@
   void SimulateGattCharacteristicRemoved(
       BluetoothRemoteGattService* service,
       BluetoothRemoteGattCharacteristic* characteristic) override;
+  void SimulateGattDescriptorRead(BluetoothRemoteGattDescriptor* descriptor,
+                                  const std::vector<uint8_t>& value) override;
+  void SimulateGattDescriptorReadError(
+      BluetoothRemoteGattDescriptor* descriptor,
+      BluetoothRemoteGattService::GattErrorCode error_code) override;
+  void SimulateGattDescriptorWrite(
+      BluetoothRemoteGattDescriptor* descriptor) override;
+  void SimulateGattDescriptorWriteError(
+      BluetoothRemoteGattDescriptor* descriptor,
+      BluetoothRemoteGattService::GattErrorCode error_code) override;
   void ExpectedChangeNotifyValueAttempts(int attempts) override;
   void ExpectedNotifyValue(NotifyValueState expected_value_state) override;
 
@@ -93,6 +105,21 @@
   // set of attributes.
   void SimulateDidDiscoverServices(BluetoothDevice* device,
                                    const std::vector<std::string>& uuids);
+  // CoreBluetooth can return NSData when reading remote gatt descriptors.
+  // This methods simulate receiving NSData from CoreBluetooth.
+  void SimulateGattDescriptorReadNSData(
+      BluetoothRemoteGattDescriptor* descriptor,
+      const std::vector<uint8_t>& value);
+  // CoreBluetooth can return NSString when reading remote gatt descriptors.
+  // This methods simulate receiving NSString from CoreBluetooth.
+  void SimulateGattDescriptorReadNSString(
+      BluetoothRemoteGattDescriptor* descriptor,
+      const std::string& value);
+  // CoreBluetooth can return NSString when reading remote gatt descriptors.
+  // This methods simulate receiving NSString from CoreBluetooth.
+  void SimulateGattDescriptorReadNSNumber(
+      BluetoothRemoteGattDescriptor* descriptor,
+      short value);
 
   // Callback for the bluetooth central manager mock.
   void OnFakeBluetoothDeviceConnectGattCalled();
@@ -104,6 +131,8 @@
   void OnFakeBluetoothCharacteristicReadValue();
   void OnFakeBluetoothCharacteristicWriteValue(std::vector<uint8_t> value);
   void OnFakeBluetoothGattSetCharacteristicNotification(bool notify_value);
+  void OnFakeBluetoothDescriptorReadValue();
+  void OnFakeBluetoothDescriptorWriteValue(std::vector<uint8_t> value);
 
   // Returns the service UUIDs used to retrieve connected peripherals.
   BluetoothDevice::UUIDSet RetrieveConnectedPeripheralServiceUUIDs();
@@ -121,9 +150,15 @@
   // Returns MockCBPeripheral from BluetoothRemoteGattCharacteristic.
   MockCBPeripheral* GetMockCBPeripheral(
       BluetoothRemoteGattCharacteristic* characteristic) const;
+  // Returns MockCBPeripheral from BluetoothRemoteGattDescriptor.
+  MockCBPeripheral* GetMockCBPeripheral(
+      BluetoothRemoteGattDescriptor* descriptor) const;
   // Returns MockCBCharacteristic from BluetoothRemoteGattCharacteristic.
   MockCBCharacteristic* GetCBMockCharacteristic(
       BluetoothRemoteGattCharacteristic* characteristic) const;
+  // Returns MockCBDescriptor from BluetoothRemoteGattDescriptor.
+  MockCBDescriptor* GetCBMockDescriptor(
+      BluetoothRemoteGattDescriptor* descriptor) const;
   // Adds services in MockCBPeripheral.
   void AddServicesToDevice(BluetoothDevice* device,
                            const std::vector<std::string>& uuids);
diff --git a/device/bluetooth/test/bluetooth_test_mac.mm b/device/bluetooth/test/bluetooth_test_mac.mm
index 04c61e3..b308802d 100644
--- a/device/bluetooth/test/bluetooth_test_mac.mm
+++ b/device/bluetooth/test/bluetooth_test_mac.mm
@@ -13,8 +13,10 @@
 #include "device/bluetooth/bluetooth_adapter_mac.h"
 #include "device/bluetooth/bluetooth_device_mac.h"
 #include "device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h"
+#include "device/bluetooth/bluetooth_remote_gatt_descriptor_mac.h"
 #include "device/bluetooth/bluetooth_remote_gatt_service_mac.h"
 #include "device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.h"
+#include "device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.h"
 #include "device/bluetooth/test/mock_bluetooth_cbperipheral_mac.h"
 #include "device/bluetooth/test/mock_bluetooth_cbservice_mac.h"
 #include "device/bluetooth/test/mock_bluetooth_central_manager_mac.h"
@@ -446,6 +448,31 @@
   [peripheral_mock mockDidDiscoverEvents];
 }
 
+void BluetoothTestMac::SimulateGattDescriptorRead(
+    BluetoothRemoteGattDescriptor* descriptor,
+    const std::vector<uint8_t>& value) {
+  SimulateGattDescriptorReadNSData(descriptor, value);
+}
+
+void BluetoothTestMac::SimulateGattDescriptorReadError(
+    BluetoothRemoteGattDescriptor* descriptor,
+    BluetoothRemoteGattService::GattErrorCode error_code) {
+  NSError* error = BluetoothDeviceMac::GetNSErrorFromGattErrorCode(error_code);
+  [GetCBMockDescriptor(descriptor) simulateReadWithValue:nil error:error];
+}
+
+void BluetoothTestMac::SimulateGattDescriptorWrite(
+    BluetoothRemoteGattDescriptor* descriptor) {
+  [GetCBMockDescriptor(descriptor) simulateWriteWithError:nil];
+}
+
+void BluetoothTestMac::SimulateGattDescriptorWriteError(
+    BluetoothRemoteGattDescriptor* descriptor,
+    BluetoothRemoteGattService::GattErrorCode error_code) {
+  NSError* error = BluetoothDeviceMac::GetNSErrorFromGattErrorCode(error_code);
+  [GetCBMockDescriptor(descriptor) simulateWriteWithError:error];
+}
+
 void BluetoothTestMac::ExpectedChangeNotifyValueAttempts(int attempts) {
   EXPECT_EQ(attempts, gatt_notify_characteristic_attempts_);
 }
@@ -470,6 +497,28 @@
   [GetMockCBPeripheral(device) mockDidDiscoverServices];
 }
 
+void BluetoothTestMac::SimulateGattDescriptorReadNSData(
+    BluetoothRemoteGattDescriptor* descriptor,
+    const std::vector<uint8_t>& value) {
+  scoped_nsobject<NSData> data(
+      [[NSData alloc] initWithBytes:value.data() length:value.size()]);
+  [GetCBMockDescriptor(descriptor) simulateReadWithValue:data error:nil];
+}
+
+void BluetoothTestMac::SimulateGattDescriptorReadNSString(
+    BluetoothRemoteGattDescriptor* descriptor,
+    const std::string& value) {
+  NSString* string = base::SysUTF8ToNSString(value);
+  [GetCBMockDescriptor(descriptor) simulateReadWithValue:string error:nil];
+}
+
+void BluetoothTestMac::SimulateGattDescriptorReadNSNumber(
+    BluetoothRemoteGattDescriptor* descriptor,
+    short value) {
+  NSNumber* number = [NSNumber numberWithShort:value];
+  [GetCBMockDescriptor(descriptor) simulateReadWithValue:number error:nil];
+}
+
 void BluetoothTestMac::OnFakeBluetoothDeviceConnectGattCalled() {
   gatt_connection_attempts_++;
 }
@@ -502,6 +551,16 @@
   gatt_notify_characteristic_attempts_++;
 }
 
+void BluetoothTest::OnFakeBluetoothDescriptorReadValue() {
+  gatt_read_descriptor_attempts_++;
+}
+
+void BluetoothTest::OnFakeBluetoothDescriptorWriteValue(
+    std::vector<uint8_t> value) {
+  last_write_value_ = value;
+  gatt_write_descriptor_attempts_++;
+}
+
 BluetoothDevice::UUIDSet
 BluetoothTestMac::RetrieveConnectedPeripheralServiceUUIDs() {
   BluetoothDevice::UUIDSet service_uuids;
@@ -540,6 +599,11 @@
   return GetMockCBPeripheral(characteristic->GetService());
 }
 
+MockCBPeripheral* BluetoothTestMac::GetMockCBPeripheral(
+    BluetoothRemoteGattDescriptor* descriptor) const {
+  return GetMockCBPeripheral(descriptor->GetCharacteristic());
+}
+
 MockCBCharacteristic* BluetoothTest::GetCBMockCharacteristic(
     BluetoothRemoteGattCharacteristic* characteristic) const {
   device::BluetoothRemoteGattCharacteristicMac* mac_gatt_characteristic =
@@ -550,6 +614,14 @@
   return ObjCCast<MockCBCharacteristic>(cb_characteristic);
 }
 
+MockCBDescriptor* BluetoothTest::GetCBMockDescriptor(
+    BluetoothRemoteGattDescriptor* descriptor) const {
+  device::BluetoothRemoteGattDescriptorMac* mac_gatt_descriptor =
+      static_cast<device::BluetoothRemoteGattDescriptorMac*>(descriptor);
+  CBDescriptor* cb_descriptor = mac_gatt_descriptor->GetCBDescriptor();
+  return ObjCCast<MockCBDescriptor>(cb_descriptor);
+}
+
 void BluetoothTest::AddServicesToDevice(BluetoothDevice* device,
                                         const std::vector<std::string>& uuids) {
   scoped_nsobject<NSMutableArray> services([[NSMutableArray alloc] init]);
diff --git a/device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.h b/device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.h
index e3bbabf..825d168 100644
--- a/device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.h
+++ b/device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.h
@@ -22,7 +22,7 @@
                      properties:(int)properties;
 
 // Methods for faking events.
-- (void)simulateReadWithValue:(NSData*)value error:(NSError*)error;
+- (void)simulateReadWithValue:(id)value error:(NSError*)error;
 - (void)simulateWriteWithError:(NSError*)error;
 - (void)simulateGattNotifySessionStarted;
 - (void)simulateGattNotifySessionFailedWithError:(NSError*)error;
diff --git a/device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.mm b/device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.mm
index 2cc9eb7b..1cf1d18 100644
--- a/device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.mm
+++ b/device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.mm
@@ -96,7 +96,7 @@
   CBCharacteristicProperties _cb_properties;
   scoped_nsobject<NSMutableArray> _simulatedDescriptors;
   scoped_nsobject<NSArray> _descriptors;
-  scoped_nsobject<NSData> _value;
+  scoped_nsobject<NSObject> _value;
   BOOL _notifying;
 }
 @end
@@ -134,7 +134,7 @@
   return [super isKindOfClass:aClass];
 }
 
-- (void)simulateReadWithValue:(NSData*)value error:(NSError*)error {
+- (void)simulateReadWithValue:(id)value error:(NSError*)error {
   _value.reset([value copy]);
   CBPeripheral* peripheral = _service.peripheral;
   [peripheral.delegate peripheral:peripheral
@@ -220,7 +220,7 @@
   return _descriptors;
 }
 
-- (NSData*)value {
+- (id)value {
   return _value.get();
 }
 
diff --git a/device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.h b/device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.h
index 7a0caaa..0a2baed4 100644
--- a/device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.h
+++ b/device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.h
@@ -19,6 +19,9 @@
 - (instancetype)initWithCharacteristic:(CBCharacteristic*)characteristic
                                 CBUUID:(CBUUID*)uuid;
 
+- (void)simulateReadWithValue:(id)value error:(NSError*)error;
+- (void)simulateWriteWithError:(NSError*)error;
+
 @end
 
 #endif  // DEVICE_BLUETOOTH_TEST_MOCK_BLUETOOTH_CBDESCRIPTOR_MAC_H_
diff --git a/device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.mm b/device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.mm
index 9c8512b5..3a2f6152 100644
--- a/device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.mm
+++ b/device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.mm
@@ -15,6 +15,7 @@
   // Owner of this instance.
   CBCharacteristic* _characteristic;
   scoped_nsobject<CBUUID> _UUID;
+  scoped_nsobject<NSData> _value;
 }
 @end
 
@@ -50,6 +51,10 @@
   return _UUID.get();
 }
 
+- (NSData*)value {
+  return _value.get();
+}
+
 - (CBDescriptor*)descriptor {
   return ObjCCast<CBDescriptor>(self);
 }
@@ -58,4 +63,19 @@
   return _characteristic;
 }
 
+- (void)simulateReadWithValue:(id)value error:(NSError*)error {
+  _value.reset([value copy]);
+  CBPeripheral* peripheral = _characteristic.service.peripheral;
+  [peripheral.delegate peripheral:peripheral
+      didUpdateValueForDescriptor:self.descriptor
+                            error:error];
+}
+
+- (void)simulateWriteWithError:(NSError*)error {
+  CBPeripheral* peripheral = _characteristic.service.peripheral;
+  [peripheral.delegate peripheral:peripheral
+       didWriteValueForDescriptor:self.descriptor
+                            error:error];
+}
+
 @end
diff --git a/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.mm b/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.mm
index 637ffee..f0a4a733 100644
--- a/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.mm
+++ b/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.mm
@@ -8,6 +8,7 @@
 #include "base/mac/scoped_nsobject.h"
 #include "device/bluetooth/test/bluetooth_test_mac.h"
 #include "device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.h"
+#include "device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.h"
 #include "device/bluetooth/test/mock_bluetooth_cbservice_mac.h"
 
 using base::mac::ObjCCast;
@@ -112,6 +113,18 @@
   _bluetoothTestMac->OnFakeBluetoothCharacteristicWriteValue(value);
 }
 
+- (void)readValueForDescriptor:(CBDescriptor*)descriptor {
+  DCHECK(_bluetoothTestMac);
+  _bluetoothTestMac->OnFakeBluetoothDescriptorReadValue();
+}
+
+- (void)writeValue:(NSData*)data forDescriptor:(CBDescriptor*)descriptor {
+  DCHECK(_bluetoothTestMac);
+  const uint8_t* buffer = static_cast<const uint8_t*>(data.bytes);
+  std::vector<uint8_t> value(buffer, buffer + data.length);
+  _bluetoothTestMac->OnFakeBluetoothDescriptorWriteValue(value);
+}
+
 - (void)removeAllServices {
   [_services.get() removeAllObjects];
 }
diff --git a/device/vr/BUILD.gn b/device/vr/BUILD.gn
index 6445234b..2e40989 100644
--- a/device/vr/BUILD.gn
+++ b/device/vr/BUILD.gn
@@ -47,8 +47,11 @@
       "vr_display_impl.cc",
       "vr_display_impl.h",
       "vr_export.h",
+      "vr_math.cc",
+      "vr_math.h",
       "vr_service_impl.cc",
       "vr_service_impl.h",
+      "vr_types.h",
     ]
 
     deps += [
diff --git a/device/vr/android/gvr/gvr_delegate.cc b/device/vr/android/gvr/gvr_delegate.cc
index 5358f73b..9b05efc 100644
--- a/device/vr/android/gvr/gvr_delegate.cc
+++ b/device/vr/android/gvr/gvr_delegate.cc
@@ -5,7 +5,9 @@
 #include "device/vr/android/gvr/gvr_delegate.h"
 
 #include "base/trace_event/trace_event.h"
+#include "device/vr/vr_math.h"
 #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr.h"
+#include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
 #include "ui/gfx/transform.h"
 #include "ui/gfx/transform_util.h"
 
@@ -28,35 +30,17 @@
 // less than a frame.
 static constexpr int64_t kAngularVelocityEpsilonNanos = 1000000;
 
-// Matrix math copied from vr_shell's vr_math.cc, can't use that here
-// due to dependency ordering. TODO(mthiesse): move the vr_math code
-// to this directory so that both locations can use it.
-
-// Rotation only, ignore translation components.
-gvr::Vec3f MatrixVectorRotate(const gvr::Mat4f& m, const gvr::Vec3f& v) {
-  gvr::Vec3f res;
-  res.x = m.m[0][0] * v.x + m.m[0][1] * v.y + m.m[0][2] * v.z;
-  res.y = m.m[1][0] * v.x + m.m[1][1] * v.y + m.m[1][2] * v.z;
-  res.z = m.m[2][0] * v.x + m.m[2][1] * v.y + m.m[2][2] * v.z;
-  return res;
+void GvrMatToMatf(const gvr::Mat4f& in, vr::Mat4f* out) {
+  // If our std::array implementation doesn't have any non-data members, we can
+  // just cast the gvr matrix to an std::array.
+  static_assert(sizeof(in) == sizeof(*out),
+                "Cannot reinterpret gvr::Mat4f as vr::Matf");
+  *out = *reinterpret_cast<vr::Mat4f*>(const_cast<gvr::Mat4f*>(&in));
 }
 
-gvr::Mat4f MatrixMul(const gvr::Mat4f& matrix1, const gvr::Mat4f& matrix2) {
-  gvr::Mat4f result;
-  for (int i = 0; i < 4; ++i) {
-    for (int j = 0; j < 4; ++j) {
-      result.m[i][j] = 0.0f;
-      for (int k = 0; k < 4; ++k) {
-        result.m[i][j] += matrix1.m[i][k] * matrix2.m[k][j];
-      }
-    }
-  }
-  return result;
-}
-
-gvr::Vec3f GetAngularVelocityFromPoses(gvr::Mat4f head_mat,
-                                       gvr::Mat4f head_mat_2,
-                                       double epsilon_seconds) {
+gfx::Vector3dF GetAngularVelocityFromPoses(vr::Mat4f head_mat,
+                                           vr::Mat4f head_mat_2,
+                                           double epsilon_seconds) {
   // The angular velocity is a 3-element vector pointing along the rotation
   // axis with magnitude equal to rotation speed in radians/second, expressed
   // in the seated frame of reference.
@@ -74,8 +58,8 @@
   // See:
   // https://en.wikipedia.org/wiki/Angular_velocity#Calculation_from_the_orientation_matrix
 
-  gvr::Mat4f delta_mat;
-  gvr::Mat4f inverse_head_mat;
+  vr::Mat4f delta_mat;
+  vr::Mat4f inverse_head_mat;
   // Calculate difference matrix, and inverse head matrix rotation.
   // For the inverse rotation, just transpose the 3x3 subsection.
   //
@@ -83,41 +67,35 @@
   // provided by the caller.
   for (int j = 0; j < 3; ++j) {
     for (int i = 0; i < 3; ++i) {
-      delta_mat.m[j][i] =
-          (head_mat_2.m[j][i] - head_mat.m[j][i]) / epsilon_seconds;
-      inverse_head_mat.m[j][i] = head_mat.m[i][j];
+      delta_mat[j][i] = (head_mat_2[j][i] - head_mat[j][i]) / epsilon_seconds;
+      inverse_head_mat[j][i] = head_mat[i][j];
     }
-    delta_mat.m[j][3] = delta_mat.m[3][j] = 0.0;
-    inverse_head_mat.m[j][3] = inverse_head_mat.m[3][j] = 0.0;
+    delta_mat[j][3] = delta_mat[3][j] = 0.0;
+    inverse_head_mat[j][3] = inverse_head_mat[3][j] = 0.0;
   }
-  delta_mat.m[3][3] = 1.0;
-  inverse_head_mat.m[3][3] = 1.0;
-  gvr::Mat4f omega_mat = device::MatrixMul(delta_mat, inverse_head_mat);
-  gvr::Vec3f omega_vec;
-  omega_vec.x = -omega_mat.m[2][1];
-  omega_vec.y = omega_mat.m[2][0];
-  omega_vec.z = -omega_mat.m[1][0];
+  delta_mat[3][3] = 1.0;
+  inverse_head_mat[3][3] = 1.0;
+  vr::Mat4f omega_mat;
+  vr::MatrixMul(delta_mat, inverse_head_mat, &omega_mat);
+  gfx::Vector3dF omega_vec(-omega_mat[2][1], omega_mat[2][0], -omega_mat[1][0]);
 
   // Rotate by inverse head matrix to bring into seated space.
-  gvr::Vec3f angular_velocity =
-      device::MatrixVectorRotate(inverse_head_mat, omega_vec);
-
-  return angular_velocity;
+  return vr::MatrixVectorRotate(inverse_head_mat, omega_vec);
 }
 
 }  // namespace
 
 /* static */
-mojom::VRPosePtr GvrDelegate::VRPosePtrFromGvrPose(gvr::Mat4f head_mat) {
+mojom::VRPosePtr GvrDelegate::VRPosePtrFromGvrPose(const vr::Mat4f& head_mat) {
   mojom::VRPosePtr pose = mojom::VRPose::New();
 
   pose->orientation.emplace(4);
 
   gfx::Transform inv_transform(
-      head_mat.m[0][0], head_mat.m[0][1], head_mat.m[0][2], head_mat.m[0][3],
-      head_mat.m[1][0], head_mat.m[1][1], head_mat.m[1][2], head_mat.m[1][3],
-      head_mat.m[2][0], head_mat.m[2][1], head_mat.m[2][2], head_mat.m[2][3],
-      head_mat.m[3][0], head_mat.m[3][1], head_mat.m[3][2], head_mat.m[3][3]);
+      head_mat[0][0], head_mat[0][1], head_mat[0][2], head_mat[0][3],
+      head_mat[1][0], head_mat[1][1], head_mat[1][2], head_mat[1][3],
+      head_mat[2][0], head_mat[2][1], head_mat[2][2], head_mat[2][3],
+      head_mat[3][0], head_mat[3][1], head_mat[3][2], head_mat[3][3]);
 
   gfx::Transform transform;
   if (inv_transform.GetInverse(&transform)) {
@@ -139,50 +117,56 @@
 }
 
 /* static */
-gvr::Mat4f GvrDelegate::GetGvrPoseWithNeckModel(gvr::GvrApi* gvr_api) {
+void GvrDelegate::GetGvrPoseWithNeckModel(gvr::GvrApi* gvr_api,
+                                          vr::Mat4f* out) {
   gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow();
   target_time.monotonic_system_time_nanos += kPredictionTimeWithoutVsyncNanos;
 
   gvr::Mat4f head_mat = gvr_api->ApplyNeckModel(
       gvr_api->GetHeadSpaceFromStartSpaceRotation(target_time), 1.0f);
 
-  return head_mat;
+  GvrMatToMatf(head_mat, out);
 }
 
 /* static */
 mojom::VRPosePtr GvrDelegate::GetVRPosePtrWithNeckModel(
     gvr::GvrApi* gvr_api,
-    gvr::Mat4f* head_mat_out) {
+    vr::Mat4f* head_mat_out) {
   gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow();
   target_time.monotonic_system_time_nanos += kPredictionTimeWithoutVsyncNanos;
 
-  gvr::Mat4f head_mat = gvr_api->ApplyNeckModel(
+  gvr::Mat4f gvr_head_mat = gvr_api->ApplyNeckModel(
       gvr_api->GetHeadSpaceFromStartSpaceRotation(target_time), 1.0f);
 
-  if (head_mat_out)
-    *head_mat_out = head_mat;
+  vr::Mat4f* head_mat_ptr = head_mat_out;
+  vr::Mat4f head_mat;
+  if (!head_mat_ptr)
+    head_mat_ptr = &head_mat;
+  GvrMatToMatf(gvr_head_mat, head_mat_ptr);
 
-  mojom::VRPosePtr pose = GvrDelegate::VRPosePtrFromGvrPose(head_mat);
+  mojom::VRPosePtr pose = GvrDelegate::VRPosePtrFromGvrPose(*head_mat_ptr);
 
   // Get a second pose a bit later to calculate angular velocity.
   target_time.monotonic_system_time_nanos += kAngularVelocityEpsilonNanos;
-  gvr::Mat4f head_mat_2 =
+  gvr::Mat4f gvr_head_mat_2 =
       gvr_api->GetHeadSpaceFromStartSpaceRotation(target_time);
+  vr::Mat4f head_mat_2;
+  GvrMatToMatf(gvr_head_mat_2, &head_mat_2);
 
   // Add headset angular velocity to the pose.
   pose->angularVelocity.emplace(3);
   double epsilon_seconds = kAngularVelocityEpsilonNanos * 1e-9;
-  gvr::Vec3f angular_velocity =
-      GetAngularVelocityFromPoses(head_mat, head_mat_2, epsilon_seconds);
-  pose->angularVelocity.value()[0] = angular_velocity.x;
-  pose->angularVelocity.value()[1] = angular_velocity.y;
-  pose->angularVelocity.value()[2] = angular_velocity.z;
+  gfx::Vector3dF angular_velocity =
+      GetAngularVelocityFromPoses(*head_mat_ptr, head_mat_2, epsilon_seconds);
+  pose->angularVelocity.value()[0] = angular_velocity.x();
+  pose->angularVelocity.value()[1] = angular_velocity.y();
+  pose->angularVelocity.value()[2] = angular_velocity.z();
 
   return pose;
 }
 
 /* static */
-gvr::Sizei GvrDelegate::GetRecommendedWebVrSize(gvr::GvrApi* gvr_api) {
+gfx::Size GvrDelegate::GetRecommendedWebVrSize(gvr::GvrApi* gvr_api) {
   // Pick a reasonable default size for the WebVR transfer surface
   // based on a downscaled 1:1 render resolution. This size will also
   // be reported to the client via CreateVRDisplayInfo as the
@@ -192,10 +176,11 @@
   // framebuffer to match.
   gvr::Sizei render_target_size =
       gvr_api->GetMaximumEffectiveRenderTargetSize();
-  gvr::Sizei webvr_size = {static_cast<int>(render_target_size.width *
-                                            kWebVrRecommendedResolutionScale),
-                           static_cast<int>(render_target_size.height *
-                                            kWebVrRecommendedResolutionScale)};
+
+  gfx::Size webvr_size(
+      render_target_size.width * kWebVrRecommendedResolutionScale,
+      render_target_size.height * kWebVrRecommendedResolutionScale);
+
   // Ensure that the width is an even number so that the eyes each
   // get the same size, the recommended renderWidth is per eye
   // and the client will use the sum of the left and right width.
@@ -203,15 +188,14 @@
   // TODO(klausw,crbug.com/699350): should we round the recommended
   // size to a multiple of 2^N pixels to be friendlier to the GPU? The
   // exact size doesn't matter, and it might be more efficient.
-  webvr_size.width &= ~1;
-
+  webvr_size.set_width(webvr_size.width() & ~1);
   return webvr_size;
 }
 
 /* static */
 mojom::VRDisplayInfoPtr GvrDelegate::CreateVRDisplayInfo(
     gvr::GvrApi* gvr_api,
-    gvr::Sizei recommended_size,
+    gfx::Size recommended_size,
     uint32_t device_id) {
   TRACE_EVENT0("input", "GvrDelegate::CreateVRDisplayInfo");
 
@@ -239,8 +223,8 @@
         (eye == GVR_LEFT_EYE) ? device->leftEye : device->rightEye;
     eye_params->fieldOfView = mojom::VRFieldOfView::New();
     eye_params->offset.resize(3);
-    eye_params->renderWidth = recommended_size.width / 2;
-    eye_params->renderHeight = recommended_size.height;
+    eye_params->renderWidth = recommended_size.width() / 2;
+    eye_params->renderHeight = recommended_size.height();
 
     gvr::BufferViewport eye_viewport = gvr_api->CreateBufferViewport();
     gvr_buffer_viewports.GetBufferViewport(eye, &eye_viewport);
diff --git a/device/vr/android/gvr/gvr_delegate.h b/device/vr/android/gvr/gvr_delegate.h
index 9a21aba..d5158193 100644
--- a/device/vr/android/gvr/gvr_delegate.h
+++ b/device/vr/android/gvr/gvr_delegate.h
@@ -9,30 +9,34 @@
 
 #include "device/vr/vr_export.h"
 #include "device/vr/vr_service.mojom.h"
+#include "device/vr/vr_types.h"
 #include "gpu/command_buffer/common/mailbox_holder.h"
 #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
 
+namespace gvr {
+class GvrApi;
+}
+
 namespace device {
 
 class DEVICE_VR_EXPORT GvrDelegate {
  public:
-  static mojom::VRPosePtr VRPosePtrFromGvrPose(gvr::Mat4f head_mat);
-  static gvr::Mat4f GetGvrPoseWithNeckModel(gvr::GvrApi* gvr_api);
+  static mojom::VRPosePtr VRPosePtrFromGvrPose(const vr::Mat4f& head_mat);
+  static void GetGvrPoseWithNeckModel(gvr::GvrApi* gvr_api, vr::Mat4f* out);
   static mojom::VRPosePtr GetVRPosePtrWithNeckModel(gvr::GvrApi* gvr_api,
-                                                    gvr::Mat4f* head_mat_out);
-  static gvr::Sizei GetRecommendedWebVrSize(gvr::GvrApi* gvr_api);
-  static mojom::VRDisplayInfoPtr CreateVRDisplayInfo(
-      gvr::GvrApi* gvr_api,
-      gvr::Sizei recommended_size,
-      uint32_t device_id);
+                                                    vr::Mat4f* head_mat_out);
+  static gfx::Size GetRecommendedWebVrSize(gvr::GvrApi* gvr_api);
+  static mojom::VRDisplayInfoPtr CreateVRDisplayInfo(gvr::GvrApi* gvr_api,
+                                                     gfx::Size recommended_size,
+                                                     uint32_t device_id);
 
   virtual void SetWebVRSecureOrigin(bool secure_origin) = 0;
   virtual void SubmitWebVRFrame(int16_t frame_index,
                                 const gpu::MailboxHolder& mailbox) = 0;
   virtual void UpdateWebVRTextureBounds(int16_t frame_index,
-                                        const gvr::Rectf& left_bounds,
-                                        const gvr::Rectf& right_bounds,
-                                        const gvr::Sizei& source_size) = 0;
+                                        const gfx::RectF& left_bounds,
+                                        const gfx::RectF& right_bounds,
+                                        const gfx::Size& source_size) = 0;
   virtual void OnVRVsyncProviderRequest(
       mojom::VRVSyncProviderRequest request) = 0;
   virtual void UpdateVSyncInterval(int64_t timebase_nanos,
diff --git a/device/vr/android/gvr/gvr_device.cc b/device/vr/android/gvr/gvr_device.cc
index 9f60361..bf48f6b8 100644
--- a/device/vr/android/gvr/gvr_device.cc
+++ b/device/vr/android/gvr/gvr_device.cc
@@ -13,8 +13,7 @@
 #include "device/vr/android/gvr/gvr_delegate_provider.h"
 #include "device/vr/android/gvr/gvr_device_provider.h"
 #include "device/vr/vr_device_manager.h"
-#include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr.h"
-#include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
+#include "device/vr/vr_types.h"
 #include "ui/gfx/transform.h"
 #include "ui/gfx/transform_util.h"
 
@@ -69,19 +68,11 @@
   if (!delegate)
     return;
 
-  gvr::Rectf left_gvr_bounds;
-  left_gvr_bounds.left = left_bounds->left;
-  left_gvr_bounds.top = 1.0f - left_bounds->top;
-  left_gvr_bounds.right = left_bounds->left + left_bounds->width;
-  left_gvr_bounds.bottom = 1.0f - (left_bounds->top + left_bounds->height);
-
-  gvr::Rectf right_gvr_bounds;
-  right_gvr_bounds.left = right_bounds->left;
-  right_gvr_bounds.top = 1.0f - right_bounds->top;
-  right_gvr_bounds.right = right_bounds->left + right_bounds->width;
-  right_gvr_bounds.bottom = 1.0f - (right_bounds->top + right_bounds->height);
-
-  gvr::Sizei source_size = {source_width, source_height};
+  gfx::RectF left_gvr_bounds(left_bounds->left, left_bounds->top,
+                             left_bounds->width, left_bounds->height);
+  gfx::RectF right_gvr_bounds(right_bounds->left, right_bounds->top,
+                              right_bounds->width, right_bounds->height);
+  gfx::Size source_size(source_width, source_height);
   delegate->UpdateWebVRTextureBounds(frame_index, left_gvr_bounds,
                                      right_gvr_bounds, source_size);
 }
diff --git a/device/vr/android/gvr/gvr_gamepad_data_fetcher.cc b/device/vr/android/gvr/gvr_gamepad_data_fetcher.cc
index 1f4f503e..a18b6c6 100644
--- a/device/vr/android/gvr/gvr_gamepad_data_fetcher.cc
+++ b/device/vr/android/gvr/gvr_gamepad_data_fetcher.cc
@@ -6,8 +6,8 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
-
 #include "device/vr/android/gvr/gvr_gamepad_data_provider.h"
+#include "device/vr/vr_types.h"
 #include "third_party/WebKit/public/platform/WebGamepads.h"
 
 namespace device {
@@ -106,9 +106,9 @@
   pad.timestamp = provided_data.timestamp;
 
   if (provided_data.is_touching) {
-    gvr_vec2f touch_position = provided_data.touch_pos;
-    pad.axes[0] = (touch_position.x * 2.0f) - 1.0f;
-    pad.axes[1] = (touch_position.y * 2.0f) - 1.0f;
+    gfx::Vector2dF touch_position = provided_data.touch_pos;
+    pad.axes[0] = (touch_position.x() * 2.0f) - 1.0f;
+    pad.axes[1] = (touch_position.y() * 2.0f) - 1.0f;
   } else {
     pad.axes[0] = 0.0f;
     pad.axes[1] = 0.0f;
@@ -122,24 +122,24 @@
   pad.pose.has_orientation = true;
   pad.pose.has_position = false;
 
-  gvr_quatf orientation = provided_data.orientation;
+  vr::Quatf orientation = provided_data.orientation;
   pad.pose.orientation.not_null = true;
   pad.pose.orientation.x = orientation.qx;
   pad.pose.orientation.y = orientation.qy;
   pad.pose.orientation.z = orientation.qz;
   pad.pose.orientation.w = orientation.qw;
 
-  gvr_vec3f accel = provided_data.accel;
+  gfx::Vector3dF accel = provided_data.accel;
   pad.pose.linear_acceleration.not_null = true;
-  pad.pose.linear_acceleration.x = accel.x;
-  pad.pose.linear_acceleration.y = accel.y;
-  pad.pose.linear_acceleration.z = accel.z;
+  pad.pose.linear_acceleration.x = accel.x();
+  pad.pose.linear_acceleration.y = accel.y();
+  pad.pose.linear_acceleration.z = accel.z();
 
-  gvr_vec3f gyro = provided_data.gyro;
+  gfx::Vector3dF gyro = provided_data.gyro;
   pad.pose.angular_velocity.not_null = true;
-  pad.pose.angular_velocity.x = gyro.x;
-  pad.pose.angular_velocity.y = gyro.y;
-  pad.pose.angular_velocity.z = gyro.z;
+  pad.pose.angular_velocity.x = gyro.x();
+  pad.pose.angular_velocity.y = gyro.y();
+  pad.pose.angular_velocity.z = gyro.z();
 }
 
 void GvrGamepadDataFetcher::PauseHint(bool paused) {}
diff --git a/device/vr/android/gvr/gvr_gamepad_data_provider.h b/device/vr/android/gvr/gvr_gamepad_data_provider.h
index 7aa09e9..fcfcf68 100644
--- a/device/vr/android/gvr/gvr_gamepad_data_provider.h
+++ b/device/vr/android/gvr/gvr_gamepad_data_provider.h
@@ -5,7 +5,7 @@
 #ifndef DEVICE_VR_ANDROID_GVR_GAMEPAD_DATA_PROVIDER_H
 #define DEVICE_VR_ANDROID_GVR_GAMEPAD_DATA_PROVIDER_H
 
-#include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
+#include "device/vr/vr_types.h"
 
 namespace device {
 
@@ -15,10 +15,10 @@
 // by vr_shell's VrController and consumed by GvrGamepadDataFetcher.
 struct GvrGamepadData {
   int64_t timestamp;
-  gvr_vec2f touch_pos;
-  gvr_quatf orientation;
-  gvr_vec3f accel;
-  gvr_vec3f gyro;
+  gfx::Vector2dF touch_pos;
+  vr::Quatf orientation;
+  gfx::Vector3dF accel;
+  gfx::Vector3dF gyro;
   bool is_touching;
   bool controller_button_pressed;
   bool right_handed;
diff --git a/device/vr/vr_export.h b/device/vr/vr_export.h
index a450834..0afc127 100644
--- a/device/vr/vr_export.h
+++ b/device/vr/vr_export.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef DEVICE_VR_DEVICE_VR_EXPORT_H_
-#define DEVICE_VR_DEVICE_VR_EXPORT_H_
+#ifndef DEVICE_VR_VR_EXPORT_H_
+#define DEVICE_VR_VR_EXPORT_H_
 
 #if defined(COMPONENT_BUILD) && defined(WIN32)
 
@@ -25,4 +25,4 @@
 #define DEVICE_VR_EXPORT
 #endif
 
-#endif  // DEVICE_VR_DEVICE_VR_EXPORT_H_
+#endif  // DEVICE_VR_VR_EXPORT_H_
diff --git a/device/vr/vr_math.cc b/device/vr/vr_math.cc
new file mode 100644
index 0000000..77613963
--- /dev/null
+++ b/device/vr/vr_math.cc
@@ -0,0 +1,232 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/vr/vr_math.h"
+
+#include <cmath>
+
+#include "base/logging.h"
+
+namespace vr {
+
+namespace {
+Mat4f CopyMat(const Mat4f& mat) {
+  Mat4f ret = mat;
+  return ret;
+}
+}
+
+// Internal matrix layout:
+//
+//   m[0][0], m[0][1], m[0][2], m[0][3],
+//   m[1][0], m[1][1], m[1][2], m[1][3],
+//   m[2][0], m[2][1], m[2][2], m[2][3],
+//   m[3][0], m[3][1], m[3][2], m[3][3],
+//
+// The translation component is in the right column m[i][3].
+//
+// The bottom row m[3][i] is (0, 0, 0, 1) for non-perspective transforms.
+//
+// These matrices are intended to be used to premultiply column vectors
+// for transforms, so successive transforms need to be left-multiplied.
+
+void SetIdentityM(Mat4f* mat) {
+  for (int i = 0; i < 4; i++) {
+    for (int j = 0; j < 4; j++) {
+      (*mat)[i][j] = i == j ? 1 : 0;
+    }
+  }
+}
+
+// Left multiply a translation matrix.
+void TranslateM(const Mat4f& mat,
+                const gfx::Vector3dF& translation,
+                Mat4f* out) {
+  if (out != &mat) {
+    for (int i = 0; i < 4; ++i) {
+      for (int j = 0; j < 4; ++j) {
+        (*out)[i][j] = mat[i][j];
+      }
+    }
+  }
+  (*out)[0][3] += translation.x();
+  (*out)[1][3] += translation.y();
+  (*out)[2][3] += translation.z();
+}
+
+// Left multiply a scale matrix.
+void ScaleM(const Mat4f& mat, const gfx::Vector3dF& scale, Mat4f* out) {
+  if (out != &mat) {
+    for (int i = 0; i < 4; ++i) {
+      for (int j = 0; j < 3; ++j) {
+        (*out)[i][j] = mat[i][j];
+      }
+    }
+  }
+  // Multiply all rows including translation components.
+  for (int j = 0; j < 4; ++j) {
+    (*out)[0][j] *= scale.x();
+    (*out)[1][j] *= scale.y();
+    (*out)[2][j] *= scale.z();
+  }
+}
+
+gfx::Vector3dF MatrixVectorMul(const Mat4f& m, const gfx::Vector3dF& v) {
+  return gfx::Vector3dF(
+      m[0][0] * v.x() + m[0][1] * v.y() + m[0][2] * v.z() + m[0][3],
+      m[1][0] * v.x() + m[1][1] * v.y() + m[1][2] * v.z() + m[1][3],
+      m[2][0] * v.x() + m[2][1] * v.y() + m[2][2] * v.z() + m[2][3]);
+}
+
+// Rotation only, ignore translation components.
+gfx::Vector3dF MatrixVectorRotate(const Mat4f& m, const gfx::Vector3dF& v) {
+  return gfx::Vector3dF(m[0][0] * v.x() + m[0][1] * v.y() + m[0][2] * v.z(),
+                        m[1][0] * v.x() + m[1][1] * v.y() + m[1][2] * v.z(),
+                        m[2][0] * v.x() + m[2][1] * v.y() + m[2][2] * v.z());
+}
+
+void MatrixMul(const Mat4f& matrix1, const Mat4f& matrix2, Mat4f* out) {
+  const Mat4f& mat1 = (out == &matrix1) ? CopyMat(matrix1) : matrix1;
+  const Mat4f& mat2 = (out == &matrix2) ? CopyMat(matrix2) : matrix2;
+  for (int i = 0; i < 4; ++i) {
+    for (int j = 0; j < 4; ++j) {
+      (*out)[i][j] = 0.0f;
+      for (int k = 0; k < 4; ++k) {
+        (*out)[i][j] += mat1[i][k] * mat2[k][j];
+      }
+    }
+  }
+}
+
+void PerspectiveMatrixFromView(const gfx::RectF& fov,
+                               float z_near,
+                               float z_far,
+                               Mat4f* out) {
+  const float x_left = -std::tan(fov.x() * M_PI / 180.0f) * z_near;
+  const float x_right = std::tan(fov.right() * M_PI / 180.0f) * z_near;
+  const float y_bottom = -std::tan(fov.bottom() * M_PI / 180.0f) * z_near;
+  const float y_top = std::tan(fov.y() * M_PI / 180.0f) * z_near;
+
+  DCHECK(x_left < x_right && y_bottom < y_top && z_near < z_far &&
+         z_near > 0.0f && z_far > 0.0f);
+  const float X = (2 * z_near) / (x_right - x_left);
+  const float Y = (2 * z_near) / (y_top - y_bottom);
+  const float A = (x_right + x_left) / (x_right - x_left);
+  const float B = (y_top + y_bottom) / (y_top - y_bottom);
+  const float C = (z_near + z_far) / (z_near - z_far);
+  const float D = (2 * z_near * z_far) / (z_near - z_far);
+
+  for (int i = 0; i < 4; ++i) {
+    (*out)[i].fill(0.0f);
+  }
+  (*out)[0][0] = X;
+  (*out)[0][2] = A;
+  (*out)[1][1] = Y;
+  (*out)[1][2] = B;
+  (*out)[2][2] = C;
+  (*out)[2][3] = D;
+  (*out)[3][2] = -1;
+}
+
+gfx::Vector3dF GetForwardVector(const Mat4f& matrix) {
+  // Same as multiplying the inverse of the rotation component of the matrix by
+  // (0, 0, -1, 0).
+  return gfx::Vector3dF(-matrix[2][0], -matrix[2][1], -matrix[2][2]);
+}
+
+gfx::Vector3dF GetTranslation(const Mat4f& matrix) {
+  return gfx::Vector3dF(matrix[0][3], matrix[1][3], matrix[2][3]);
+}
+
+float NormalizeVector(gfx::Vector3dF* vec) {
+  float len = vec->Length();
+  if (len == 0)
+    return 0;
+  vec->Scale(1.0f / len);
+  return len;
+}
+
+void NormalizeQuat(Quatf* quat) {
+  float len = sqrt(quat->qx * quat->qx + quat->qy * quat->qy +
+                   quat->qz * quat->qz + quat->qw * quat->qw);
+  quat->qx /= len;
+  quat->qy /= len;
+  quat->qz /= len;
+  quat->qw /= len;
+}
+
+Quatf QuatFromAxisAngle(const RotationAxisAngle& axis_angle) {
+  // Rotation angle is the product of |angle| and the magnitude of |axis|.
+  gfx::Vector3dF normal(axis_angle.x, axis_angle.y, axis_angle.z);
+  float length = NormalizeVector(&normal);
+  float angle = axis_angle.angle * length;
+
+  Quatf res;
+  float s = sin(angle / 2);
+  res.qx = normal.x() * s;
+  res.qy = normal.y() * s;
+  res.qz = normal.z() * s;
+  res.qw = cos(angle / 2);
+  return res;
+}
+
+Quatf QuatMultiply(const Quatf& a, const Quatf& b) {
+  Quatf res;
+  res.qw = a.qw * b.qw - a.qx * b.qx - a.qy * b.qy - a.qz * b.qz;
+  res.qx = a.qw * b.qx + a.qx * b.qw + a.qy * b.qz - a.qz * b.qy;
+  res.qy = a.qw * b.qy - a.qx * b.qz + a.qy * b.qw + a.qz * b.qx;
+  res.qz = a.qw * b.qz + a.qx * b.qy - a.qy * b.qx + a.qz * b.qw;
+  return res;
+}
+
+void QuatToMatrix(const Quatf& quat, Mat4f* out) {
+  const float x2 = quat.qx * quat.qx;
+  const float y2 = quat.qy * quat.qy;
+  const float z2 = quat.qz * quat.qz;
+  const float xy = quat.qx * quat.qy;
+  const float xz = quat.qx * quat.qz;
+  const float xw = quat.qx * quat.qw;
+  const float yz = quat.qy * quat.qz;
+  const float yw = quat.qy * quat.qw;
+  const float zw = quat.qz * quat.qw;
+
+  const float m11 = 1.0f - 2.0f * y2 - 2.0f * z2;
+  const float m12 = 2.0f * (xy - zw);
+  const float m13 = 2.0f * (xz + yw);
+  const float m21 = 2.0f * (xy + zw);
+  const float m22 = 1.0f - 2.0f * x2 - 2.0f * z2;
+  const float m23 = 2.0f * (yz - xw);
+  const float m31 = 2.0f * (xz - yw);
+  const float m32 = 2.0f * (yz + xw);
+  const float m33 = 1.0f - 2.0f * x2 - 2.0f * y2;
+
+  *out = {{{{m11, m12, m13, 0.0f}},
+           {{m21, m22, m23, 0.0f}},
+           {{m31, m32, m33, 0.0f}},
+           {{0.0f, 0.0f, 0.0f, 1.0f}}}};
+}
+
+gfx::Point3F GetRayPoint(const gfx::Point3F& rayOrigin,
+                         const gfx::Vector3dF& rayVector,
+                         float scale) {
+  return rayOrigin + gfx::ScaleVector3d(rayVector, scale);
+}
+
+float Distance(const gfx::Point3F& p1, const gfx::Point3F& p2) {
+  return std::sqrt(p1.SquaredDistanceTo(p2));
+}
+
+bool XZAngle(const gfx::Vector3dF& vec1,
+             const gfx::Vector3dF& vec2,
+             float* angle) {
+  float len1 = vec1.Length();
+  float len2 = vec2.Length();
+  if (len1 == 0 || len2 == 0)
+    return false;
+  float cross_p = vec1.x() * vec2.z() - vec1.z() * vec2.x();
+  *angle = asin(cross_p / (len1 * len2));
+  return true;
+}
+
+}  // namespace vr
diff --git a/device/vr/vr_math.h b/device/vr/vr_math.h
new file mode 100644
index 0000000..72e21808
--- /dev/null
+++ b/device/vr/vr_math.h
@@ -0,0 +1,58 @@
+// 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.
+
+#ifndef DEVICE_VR_VR_MATH_H_
+#define DEVICE_VR_VR_MATH_H_
+
+#include "device/vr/vr_export.h"
+#include "device/vr/vr_types.h"
+
+namespace vr {
+
+void DEVICE_VR_EXPORT SetIdentityM(Mat4f* mat);
+void DEVICE_VR_EXPORT TranslateM(const Mat4f& mat,
+                                 const gfx::Vector3dF& translation,
+                                 Mat4f* out);
+void DEVICE_VR_EXPORT ScaleM(const Mat4f& mat,
+                             const gfx::Vector3dF& scale,
+                             Mat4f* out);
+
+gfx::Vector3dF DEVICE_VR_EXPORT MatrixVectorMul(const Mat4f& m,
+                                                const gfx::Vector3dF& v);
+gfx::Vector3dF DEVICE_VR_EXPORT MatrixVectorRotate(const Mat4f& m,
+                                                   const gfx::Vector3dF& v);
+void DEVICE_VR_EXPORT MatrixMul(const Mat4f& matrix1,
+                                const Mat4f& matrix2,
+                                Mat4f* out);
+void DEVICE_VR_EXPORT PerspectiveMatrixFromView(const gfx::RectF& fov,
+                                                float z_near,
+                                                float z_far,
+                                                Mat4f* out);
+
+// Provides the direction the head is looking towards as a 3x1 unit vector.
+gfx::Vector3dF DEVICE_VR_EXPORT GetForwardVector(const Mat4f& matrix);
+
+gfx::Vector3dF DEVICE_VR_EXPORT GetTranslation(const Mat4f& matrix);
+
+void DEVICE_VR_EXPORT QuatToMatrix(const Quatf& quat, Mat4f* out);
+
+// Normalize a vector, and return its original length.
+float DEVICE_VR_EXPORT NormalizeVector(gfx::Vector3dF* vec);
+
+void DEVICE_VR_EXPORT NormalizeQuat(Quatf* quat);
+
+Quatf DEVICE_VR_EXPORT QuatFromAxisAngle(const RotationAxisAngle& axis_angle);
+
+gfx::Point3F DEVICE_VR_EXPORT GetRayPoint(const gfx::Point3F& rayOrigin,
+                                          const gfx::Vector3dF& rayVector,
+                                          float scale);
+
+// Angle between the vectors' projections to the XZ plane.
+bool DEVICE_VR_EXPORT XZAngle(const gfx::Vector3dF& vec1,
+                              const gfx::Vector3dF& vec2,
+                              float* angle);
+
+}  // namespace vr
+
+#endif  // DEVICE_VR_VR_MATH_H_
diff --git a/device/vr/vr_types.h b/device/vr/vr_types.h
new file mode 100644
index 0000000..a030bd9
--- /dev/null
+++ b/device/vr/vr_types.h
@@ -0,0 +1,47 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_VR_VR_TYPES_H_
+#define DEVICE_VR_VR_TYPES_H_
+
+#include <array>
+
+#include "ui/gfx/geometry/point3_f.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/geometry/vector3d_f.h"
+
+namespace vr {
+
+using Mat4f = std::array<std::array<float, 4>, 4>;
+
+typedef struct RotationAxisAngle {
+  float x;
+  float y;
+  float z;
+  float angle;
+} RotationAxisAngle;
+
+typedef struct Colorf {
+  float r;
+  float g;
+  float b;
+  float a;
+} Colorf;
+
+// A floating point quaternion, in JPL format.
+typedef struct Quatf {
+  /// qx, qy, qz are the vector component.
+  float qx;
+  float qy;
+  float qz;
+  /// qw is the linear component.
+  float qw;
+} Quatf;
+
+}  // namespace vr
+
+#endif  // DEVICE_VR_VR_TYPES_H_
diff --git a/extensions/browser/mock_external_provider.cc b/extensions/browser/mock_external_provider.cc
index 66e02bc..53d0494 100644
--- a/extensions/browser/mock_external_provider.cc
+++ b/extensions/browser/mock_external_provider.cc
@@ -18,9 +18,19 @@
 MockExternalProvider::~MockExternalProvider() {}
 
 void MockExternalProvider::UpdateOrAddExtension(const ExtensionId& id,
-                                                const std::string& version,
+                                                const std::string& version_str,
                                                 const base::FilePath& path) {
-  extension_map_[id] = std::make_pair(version, path);
+  auto version = base::MakeUnique<base::Version>(version_str);
+  auto info = base::MakeUnique<ExternalInstallInfoFile>(
+      id, std::move(version), path, location_, Extension::NO_FLAGS, false,
+      false);
+  extension_map_[id] = std::move(info);
+}
+
+void MockExternalProvider::UpdateOrAddExtension(
+    std::unique_ptr<ExternalInstallInfoFile> info) {
+  std::string id = info->extension_id;
+  extension_map_[id] = std::move(info);
 }
 
 void MockExternalProvider::RemoveExtension(const ExtensionId& id) {
@@ -29,15 +39,8 @@
 
 void MockExternalProvider::VisitRegisteredExtension() {
   visit_count_++;
-  for (const auto& extension_kv : extension_map_) {
-    std::unique_ptr<base::Version> version =
-        base::MakeUnique<base::Version>(extension_kv.second.first);
-    std::unique_ptr<ExternalInstallInfoFile> info =
-        base::MakeUnique<ExternalInstallInfoFile>(
-            extension_kv.first, std::move(version), extension_kv.second.second,
-            location_, Extension::NO_FLAGS, false, false);
-    visitor_->OnExternalExtensionFileFound(*info);
-  }
+  for (const auto& extension_kv : extension_map_)
+    visitor_->OnExternalExtensionFileFound(*extension_kv.second);
   visitor_->OnExternalProviderReady(this);
 }
 
@@ -54,7 +57,7 @@
     return false;
 
   if (version)
-    version->reset(new base::Version(it->second.first));
+    version->reset(new base::Version(it->second->version->GetString()));
 
   if (location)
     *location = location_;
diff --git a/extensions/browser/mock_external_provider.h b/extensions/browser/mock_external_provider.h
index 7418eda..bc94c4ba 100644
--- a/extensions/browser/mock_external_provider.h
+++ b/extensions/browser/mock_external_provider.h
@@ -30,7 +30,7 @@
   void UpdateOrAddExtension(const ExtensionId& id,
                             const std::string& version,
                             const base::FilePath& path);
-
+  void UpdateOrAddExtension(std::unique_ptr<ExternalInstallInfoFile> info);
   void RemoveExtension(const ExtensionId& id);
 
   // ExternalProviderInterface implementation:
@@ -47,7 +47,8 @@
   void set_visit_count(int visit_count) { visit_count_ = visit_count; }
 
  private:
-  using DataMap = std::map<ExtensionId, std::pair<std::string, base::FilePath>>;
+  using DataMap =
+      std::map<ExtensionId, std::unique_ptr<ExternalInstallInfoFile>>;
   DataMap extension_map_;
   Manifest::Location location_;
   VisitorInterface* visitor_;
diff --git a/extensions/common/api/_permission_features.json b/extensions/common/api/_permission_features.json
index de72ed4..b949931 100644
--- a/extensions/common/api/_permission_features.json
+++ b/extensions/common/api/_permission_features.json
@@ -488,7 +488,11 @@
         "496B6890097EB6E19809ADEADD095A8721FBB2E0",  // FIDO U2F APIs
         "AD8ED80B705E1818AAD4684F9FF62B43D6D79620",  // FIDO U2F APIs (dev)
         "E24F1786D842E91E74C27929B0B3715A4689A473",  // CryptoToken
-        "A28C9619C4C41306FA5236FB4D94DA812F504DE8"   // CryptoToken (dev)
+        "A28C9619C4C41306FA5236FB4D94DA812F504DE8",  // CryptoToken (dev)
+        "D7CD4B3956B1F6E970E4AFCBCD4094B1EF4D07B9",  // http://crbug.com/710541
+        "D4EFCCC0CC612380762758BB96C7997224BD6395",  // http://crbug.com/710541
+        "4839A26B29CD1BD021B2E126EF6D28C9CB84018B",  // http://crbug.com/710541
+        "8F44FBB4474CCDF23450B166C9E83E85BD03AE24"   // http://crbug.com/710541
       ]
     }
   ],
diff --git a/ios/chrome/browser/passwords/password_controller_unittest.mm b/ios/chrome/browser/passwords/password_controller_unittest.mm
index 7e45473..9a6ddbd 100644
--- a/ios/chrome/browser/passwords/password_controller_unittest.mm
+++ b/ios/chrome/browser/passwords/password_controller_unittest.mm
@@ -749,6 +749,49 @@
      "  }"
      "}; result";
 
+// Test html content and expected result for __gCrWeb.hasPasswordField call.
+struct TestDataForPasswordFormDetection {
+  NSString* page_content;
+  BOOL contains_password;
+};
+
+// Tests that the existence of (or the lack of) a password field in the page is
+// detected correctly.
+TEST_F(PasswordControllerTest, HasPasswordField) {
+  TestDataForPasswordFormDetection test_data[] = {
+      // Form without a password field.
+      {@"<form><input type='text' name='password'></form>", NO},
+      // Form with a password field.
+      {@"<form><input type='password' name='password'></form>", YES}};
+  for (size_t i = 0; i < arraysize(test_data); i++) {
+    TestDataForPasswordFormDetection& data = test_data[i];
+    LoadHtml(data.page_content);
+    id result = ExecuteJavaScript(@"__gCrWeb.hasPasswordField()");
+    EXPECT_NSEQ(@(data.contains_password), result)
+        << " in test " << i << ": "
+        << base::SysNSStringToUTF8(data.page_content);
+  }
+}
+
+// Tests that the existence a password field in a nested iframe/ is detected
+// correctly.
+TEST_F(PasswordControllerTest, HasPasswordFieldinFrame) {
+  TestDataForPasswordFormDetection data = {
+    // Form with a password field in a nested iframe.
+    @"<iframe name='pf'></iframe>"
+     "<script>"
+     "  var doc = frames['pf'].document.open();"
+     "  doc.write('<form><input type=\\'password\\'></form>');"
+     "  doc.close();"
+     "</script>",
+    YES
+  };
+  LoadHtml(data.page_content);
+  id result = ExecuteJavaScript(@"__gCrWeb.hasPasswordField()");
+  EXPECT_NSEQ(@(data.contains_password), result)
+      << base::SysNSStringToUTF8(data.page_content);
+}
+
 struct FillPasswordFormTestData {
   const std::string origin;
   const std::string action;
@@ -760,10 +803,14 @@
   NSString* expected_result;
 };
 
-// Test that filling password forms works correctly.
+// Tests that filling password forms works correctly.
 TEST_F(PasswordControllerTest, FillPasswordForm) {
   LoadHtml(kHtmlWithMultiplePasswordForms);
 
+  // TODO(crbug.com/614092): can we remove this assertion? This call is the only
+  // reason why hasPasswordField is a public API on gCrWeb. If the page does
+  // not contain a password field, shouldn't one of the expectations of the
+  // remaining tests also fail?
   EXPECT_NSEQ(@YES, ExecuteJavaScript(@"__gCrWeb.hasPasswordField()"));
 
   const std::string base_url = BaseUrl();
diff --git a/ios/chrome/browser/passwords/resources/password_controller.js b/ios/chrome/browser/passwords/resources/password_controller.js
index f3bb421..c04af67 100644
--- a/ios/chrome/browser/passwords/resources/password_controller.js
+++ b/ios/chrome/browser/passwords/resources/password_controller.js
@@ -28,13 +28,54 @@
    */
   __gCrWeb['findPasswordForms'] = function() {
     var formDataList = [];
-    if (__gCrWeb.hasPasswordField()) {
+    if (hasPasswordField_(window)) {
       __gCrWeb.getPasswordFormDataList(formDataList, window);
     }
     return __gCrWeb.stringify(formDataList);
   };
 
   /**
+   * Returns true if the top window or any frames inside contain an input field
+   * of type 'password'. This method is only used for unit tests and are only
+   * kept for legacy reasons. Prefer to use the private
+   * {@code hasPasswordField_} within this file.
+   * @return {boolean} Whether a password field exists.
+   *
+   * TODO(crbug.com/614092): investigate if this method can be completely
+   * removed from the gCrWeb public interface.
+   */
+  __gCrWeb['hasPasswordField'] = function() {
+    return hasPasswordField_(window);
+  };
+
+  /** Returns true if the supplied window or any frames inside contain an input
+   * field of type 'password'.
+   * @private
+   */
+  var hasPasswordField_ = function(win) {
+    var doc = win.document;
+
+    // We may will not be allowed to read the 'document' property from a frame
+    // that is in a different domain.
+    if (!doc) {
+      return false;
+    }
+
+    if (doc.querySelector('input[type=password]')) {
+      return true;
+    }
+
+    var frames = win.frames;
+    for (var i = 0; i < frames.length; i++) {
+      if (hasPasswordField_(frames[i])) {
+        return true;
+      }
+    }
+
+    return false;
+  };
+
+  /**
    * Returns the password form with the given |name| as a JSON string.
    * @param {string} name The name of the form to extract.
    * @return {string} The password form.
diff --git a/ios/chrome/browser/payments/payment_request.mm b/ios/chrome/browser/payments/payment_request.mm
index e138bae..2a7d7ff 100644
--- a/ios/chrome/browser/payments/payment_request.mm
+++ b/ios/chrome/browser/payments/payment_request.mm
@@ -11,6 +11,7 @@
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/payments/core/currency_formatter.h"
 #include "components/payments/core/payment_request_data_util.h"
+#include "components/payments/core/profile_util.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/web/public/payments/payment_request.h"
 
@@ -90,12 +91,15 @@
   for (const auto* profile : profiles_to_suggest) {
     profile_cache_.push_back(*profile);
     shipping_profiles_.push_back(&profile_cache_.back());
-    // TODO(crbug.com/602666): Implement deduplication rules for profiles.
     contact_profiles_.push_back(&profile_cache_.back());
   }
 
-  // TODO(crbug.com/602666): Implement prioritization rules for shipping and
-  // contact profiles.
+  // TODO(crbug.com/602666): Implement deduplication and prioritization rules
+  // for shipping profiles.
+
+  contact_profiles_ = payments::profile_util::FilterProfilesForContact(
+      contact_profiles_, GetApplicationContext()->GetApplicationLocale(),
+      *this);
 
   if (!shipping_profiles_.empty())
     selected_shipping_profile_ = shipping_profiles_[0];
diff --git a/ios/chrome/browser/payments/payment_request_test_util.mm b/ios/chrome/browser/payments/payment_request_test_util.mm
index 706ec3e..c8f31f8d 100644
--- a/ios/chrome/browser/payments/payment_request_test_util.mm
+++ b/ios/chrome/browser/payments/payment_request_test_util.mm
@@ -43,6 +43,9 @@
   shipping_option2.selected = false;
   web_payment_request.details.shipping_options.push_back(shipping_option2);
   web_payment_request.options.request_shipping = true;
+  web_payment_request.options.request_payer_name = true;
+  web_payment_request.options.request_payer_email = true;
+  web_payment_request.options.request_payer_phone = true;
   return web_payment_request;
 }
 
diff --git a/ios/chrome/browser/translate/translate_ranker_factory.cc b/ios/chrome/browser/translate/translate_ranker_factory.cc
index d55799d8..b6268a8 100644
--- a/ios/chrome/browser/translate/translate_ranker_factory.cc
+++ b/ios/chrome/browser/translate/translate_ranker_factory.cc
@@ -39,7 +39,7 @@
       ios::ChromeBrowserState::FromBrowserState(context);
   return base::MakeUnique<TranslateRankerImpl>(
       TranslateRankerImpl::GetModelPath(browser_state->GetStatePath()),
-      TranslateRankerImpl::GetModelURL());
+      TranslateRankerImpl::GetModelURL(), nullptr /* ukm::UkmService */);
 }
 
 web::BrowserState* TranslateRankerFactory::GetBrowserStateToUse(
diff --git a/ios/chrome/browser/ui/settings/settings_collection_view_controller.mm b/ios/chrome/browser/ui/settings/settings_collection_view_controller.mm
index 73c8da6..a582f938 100644
--- a/ios/chrome/browser/ui/settings/settings_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_collection_view_controller.mm
@@ -200,6 +200,9 @@
   // Mediator to configure the sign-in promo cell. Also used to received
   // identity update notifications.
   base::scoped_nsobject<SigninPromoViewMediator> _signinPromoViewMediator;
+  // Item in the Sign-in section. Either CollectionViewAccountItem,
+  // AccountSignInItem or SigninPromoItem.
+  CollectionViewItem* _signinSectionItem;
 
   // Cached resized profile image.
   base::scoped_nsobject<UIImage> _resizedImage;
@@ -311,6 +314,10 @@
 - (void)viewWillAppear:(BOOL)animated {
   [super viewWillAppear:animated];
   [self updateSearchCell];
+  // Sign-in section should not be reloaded while the controller is overlaid. So
+  // this section has to be reloaded before reappearing.
+  // See -[SettingsCollectionViewController onSignInStateChanged].
+  [self reloadSigninSectionWithAnimation:NO];
 }
 
 #pragma mark SettingsRootCollectionViewController
@@ -322,27 +329,8 @@
 
   // Sign in/Account section
   [model addSectionWithIdentifier:SectionIdentifierSignIn];
-  AuthenticationService* authService =
-      AuthenticationServiceFactory::GetForBrowserState(_mainBrowserState);
-  if (!authService->IsAuthenticated()) {
-    if (!_hasRecordedSigninImpression) {
-      // Once the Settings are open, this button impression will at most be
-      // recorded once until they are closed.
-      base::RecordAction(
-          base::UserMetricsAction("Signin_Impression_FromSettings"));
-      _hasRecordedSigninImpression = YES;
-    }
-    if (experimental_flags::IsSigninPromoEnabled()) {
-      _signinPromoViewMediator.reset([[SigninPromoViewMediator alloc] init]);
-      _signinPromoViewMediator.get().consumer = self;
-    }
-    [model addItem:[self signInTextItem]
-        toSectionWithIdentifier:SectionIdentifierSignIn];
-  } else {
-    _signinPromoViewMediator.reset(nil);
-    [model addItem:[self accountCellItem]
-        toSectionWithIdentifier:SectionIdentifierSignIn];
-  }
+  [model addItem:[self signinSectionItem]
+      toSectionWithIdentifier:SectionIdentifierSignIn];
 
   // Basics section
   [model addSectionWithIdentifier:SectionIdentifierBasics];
@@ -413,6 +401,29 @@
 #endif  // CHROMIUM_BUILD && !defined(NDEBUG)
 }
 
+- (CollectionViewItem*)signinSectionItem {
+  AuthenticationService* authService =
+      AuthenticationServiceFactory::GetForBrowserState(_mainBrowserState);
+  if (!authService->IsAuthenticated()) {
+    if (!_hasRecordedSigninImpression) {
+      // Once the Settings are open, this button impression will at most be
+      // recorded once until they are closed.
+      base::RecordAction(
+          base::UserMetricsAction("Signin_Impression_FromSettings"));
+      _hasRecordedSigninImpression = YES;
+    }
+    if (experimental_flags::IsSigninPromoEnabled()) {
+      _signinPromoViewMediator.reset([[SigninPromoViewMediator alloc] init]);
+      _signinPromoViewMediator.get().consumer = self;
+    }
+    _signinSectionItem = [self signInTextItem];
+  } else {
+    _signinPromoViewMediator.reset(nil);
+    _signinSectionItem = [self accountCellItem];
+  }
+  return _signinSectionItem;
+}
+
 #pragma mark - Model Items
 
 - (CollectionViewItem*)signInTextItem {
@@ -593,6 +604,24 @@
          inSectionWithIdentifier:SectionIdentifierBasics];
 }
 
+- (void)reloadSigninSectionWithAnimation:(BOOL)animation {
+  CollectionViewModel* model = self.collectionViewModel;
+  NSIndexPath* indexPath = [model indexPathForItem:_signinSectionItem
+                           inSectionWithIdentifier:SectionIdentifierSignIn];
+  [model removeItemWithType:_signinSectionItem.type
+      fromSectionWithIdentifier:SectionIdentifierSignIn];
+  [model addItem:[self signinSectionItem]
+      toSectionWithIdentifier:SectionIdentifierSignIn];
+  void (^reloadItemsBlock)(void) = ^{
+    [self.collectionView reloadItemsAtIndexPaths:@[ indexPath ]];
+  };
+  if (animation) {
+    reloadItemsBlock();
+  } else {
+    [UIView performWithoutAnimation:reloadItemsBlock];
+  }
+}
+
 #pragma mark Item Constructors
 
 - (CollectionViewDetailItem*)detailItemWithType:(NSInteger)type
@@ -1022,9 +1051,17 @@
 #pragma mark NotificationBridgeDelegate
 
 - (void)onSignInStateChanged {
-  // Sign in state changes are rare. Just reload the entire collection when this
-  // happens.
-  [self reloadData];
+  // Sign-in section should only be reloaded when
+  // SettingsCollectionViewController is not overlaid.
+  // When clicking on "CONTINUE AS xxx" button, SigninInteractionController
+  // presents a signed-in view and sign-in the user at the same time.
+  // So to avoid having two animations, the sign-in section should only be
+  // reloaded when the settings controler is not overlaid.
+  // The sign-in section will be reloaded when
+  // -[SettingsCollectionViewController viewWillAppear:] is called.
+  if (!self.presentedViewController) {
+    [self reloadSigninSectionWithAnimation:YES];
+  }
 }
 
 #pragma mark SettingsControllerProtocol
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index de52bb0..f3892b0f 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -608,7 +608,6 @@
     "web_state/error_translation_util_unittest.mm",
     "web_state/js/common_js_unittest.mm",
     "web_state/js/context_menu_js_unittest.mm",
-    "web_state/js/core_js_unittest.mm",
     "web_state/js/credential_util_unittest.mm",
     "web_state/js/crw_js_injection_manager_unittest.mm",
     "web_state/js/crw_js_post_request_loader_unittest.mm",
diff --git a/ios/web/web_state/js/core_js_unittest.mm b/ios/web/web_state/js/core_js_unittest.mm
deleted file mode 100644
index 7420220..0000000
--- a/ios/web/web_state/js/core_js_unittest.mm
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2012 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 <vector>
-
-#include "base/macros.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/sys_string_conversions.h"
-#import "ios/web/public/test/web_test_with_web_state.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/gtest_mac.h"
-
-// Unit tests for ios/web/web_state/js/resources/core.js.
-
-namespace web {
-
-// Test fixture to test core.js.
-typedef web::WebTestWithWebState CoreJsTest;
-
-struct TestDataForPasswordFormDetection {
-  NSString* pageContent;
-  NSNumber* containsPassword;
-};
-
-TEST_F(CoreJsTest, HasPasswordField) {
-  TestDataForPasswordFormDetection testData[] = {
-      // Form without a password field.
-      {@"<form><input type='text' name='password'></form>", @NO},
-      // Form with a password field.
-      {@"<form><input type='password' name='password'></form>", @YES}};
-  for (size_t i = 0; i < arraysize(testData); i++) {
-    TestDataForPasswordFormDetection& data = testData[i];
-    LoadHtml(data.pageContent);
-    id result = ExecuteJavaScript(@"__gCrWeb.hasPasswordField()");
-    EXPECT_NSEQ(data.containsPassword, result)
-        << " in test " << i << ": "
-        << base::SysNSStringToUTF8(data.pageContent);
-  }
-}
-
-TEST_F(CoreJsTest, HasPasswordFieldinFrame) {
-  TestDataForPasswordFormDetection data = {
-    // Form with a password field in a nested iframe.
-    @"<iframe name='pf'></iframe>"
-     "<script>"
-     "  var doc = frames['pf'].document.open();"
-     "  doc.write('<form><input type=\\'password\\'></form>');"
-     "  doc.close();"
-     "</script>",
-    @YES
-  };
-  LoadHtml(data.pageContent);
-  id result = ExecuteJavaScript(@"__gCrWeb.hasPasswordField()");
-  EXPECT_NSEQ(data.containsPassword, result)
-      << base::SysNSStringToUTF8(data.pageContent);
-}
-
-}  // namespace web
diff --git a/ios/web/web_state/js/resources/core.js b/ios/web/web_state/js/resources/core.js
index 878481a8..6ec37f02 100644
--- a/ios/web/web_state/js/resources/core.js
+++ b/ios/web/web_state/js/resources/core.js
@@ -40,40 +40,6 @@
         {'command': 'window.error', 'message': event.message.toString()});
   });
 
-
-  // Returns true if the top window or any frames inside contain an input
-  // field of type 'password'.
-  __gCrWeb['hasPasswordField'] = function() {
-    return hasPasswordField_(window);
-  };
-
-
-  // Returns true if the supplied window or any frames inside contain an input
-  // field of type 'password'.
-  // @private
-  var hasPasswordField_ = function(win) {
-    var doc = win.document;
-
-    // We may will not be allowed to read the 'document' property from a frame
-    // that is in a different domain.
-    if (!doc) {
-      return false;
-    }
-
-    if (doc.querySelector('input[type=password]')) {
-      return true;
-    }
-
-    var frames = win.frames;
-    for (var i = 0; i < frames.length; i++) {
-      if (hasPasswordField_(frames[i])) {
-        return true;
-      }
-    }
-
-    return false;
-  };
-
   __gCrWeb['sendFaviconsToHost'] = function() {
     __gCrWeb.message.invokeOnHost({'command': 'document.favicons',
                                    'favicons': __gCrWeb.common.getFavicons()});
diff --git a/ios/web_view/internal/translate/web_view_translate_ranker_factory.cc b/ios/web_view/internal/translate/web_view_translate_ranker_factory.cc
index ca08603..0d0de7e 100644
--- a/ios/web_view/internal/translate/web_view_translate_ranker_factory.cc
+++ b/ios/web_view/internal/translate/web_view_translate_ranker_factory.cc
@@ -44,7 +44,8 @@
       base::MakeUnique<translate::TranslateRankerImpl>(
           translate::TranslateRankerImpl::GetModelPath(
               web_view_browser_state->GetStatePath()),
-          translate::TranslateRankerImpl::GetModelURL());
+          translate::TranslateRankerImpl::GetModelURL(),
+          nullptr /* ukm::UkmService */);
   // WebView has no consumer of translate ranker events, so don't generate them.
   ranker->EnableLogging(false);
 
diff --git a/ipc/README.md b/ipc/README.md
index ba08e075..2b1f9f2 100644
--- a/ipc/README.md
+++ b/ipc/README.md
@@ -50,9 +50,13 @@
 We have a small but growing number of services defined in
 [`//services`](https://cs.chromium.org/chromium/src/services), each of which has
 some set of public interfaces defined in their `public/interfaces` subdirectory.
-In the limit we want all IPC interfaces to be defined by some Mojom in these
-directories, so this is the preferred destination for any new message
-conversions.
+In the limit, this is the preferred destination for any message conversions
+pertaining to foundational system services (more info at
+[https://www.chromium.org/servicification](https://www.chromium.org/servicification).)
+For other code it may make sense to introduce services elsewhere (*e.g.*, in
+`//chrome/services` or `//components/foo/service`), or to simply
+avoid using services altogether for now and instead define some one-off Mojom
+interface alongside the old messages file.
 
 If you need help deciding where a message should live, or if you feel it would
 be appropriate to introduce a new service to implement some feature or large set
diff --git a/mojo/README.md b/mojo/README.md
index e1e7583f..64d129f 100644
--- a/mojo/README.md
+++ b/mojo/README.md
@@ -1,4 +1,4 @@
-# ![Mojo Graphic](https://goo.gl/6CdlbH) Mojo
+# ![Mojo Graphic](https://goo.gl/e0Hpks) Mojo
 This document is a subset of the [Mojo documentation](/mojo).
 
 [TOC]
@@ -29,7 +29,7 @@
 and libraries comprising the system. The basic hierarchy of features is as
 follows:
 
-![Mojo Library Layering: EDK on bottom, different language bindings on top, public system support APIs in the middle](https://docs.google.com/drawings/d/1aNbLfF-fejgzxCxH_b8xAaCVvftW8BGTH_EHD7nvU1w/pub?w=570&h=327)
+![Mojo Library Layering: EDK on bottom, different language bindings on top, public system support APIs in the middle](https://docs.google.com/drawings/d/1RwhzKblXUZw-zhy_KDVobAYprYSqxZzopXTUsbwzDPw/pub?w=570&h=324)
 
 ## Embedder Development Kit (EDK)
 Every process to be interconnected via Mojo IPC is called a **Mojo embedder**
diff --git a/mojo/edk/embedder/README.md b/mojo/edk/embedder/README.md
index fc53bece..6fc2cce 100644
--- a/mojo/edk/embedder/README.md
+++ b/mojo/edk/embedder/README.md
@@ -1,4 +1,4 @@
-# ![Mojo Graphic](https://goo.gl/6CdlbH) Mojo Embedder Development Kit (EDK)
+# ![Mojo Graphic](https://goo.gl/e0Hpks) Mojo Embedder Development Kit (EDK)
 This document is a subset of the [Mojo documentation](/mojo).
 
 [TOC]
diff --git a/mojo/edk/system/node_channel.cc b/mojo/edk/system/node_channel.cc
index b0f770d90..bed3848 100644
--- a/mojo/edk/system/node_channel.cc
+++ b/mojo/edk/system/node_channel.cc
@@ -23,12 +23,6 @@
 
 namespace {
 
-template <typename T>
-T Align(T t) {
-  const auto k = kChannelMessageAlignment;
-  return t + (k - (t % k)) % k;
-}
-
 // NOTE: Please ONLY append messages to the end of this enum.
 enum class MessageType : uint32_t {
   ACCEPT_CHILD,
diff --git a/mojo/public/c/system/README.md b/mojo/public/c/system/README.md
index 2abe80ff..6e4ad3d9 100644
--- a/mojo/public/c/system/README.md
+++ b/mojo/public/c/system/README.md
@@ -1,4 +1,4 @@
-# ![Mojo Graphic](https://goo.gl/6CdlbH) Mojo C System API
+# ![Mojo Graphic](https://goo.gl/e0Hpks) Mojo C System API
 This document is a subset of the [Mojo documentation](/mojo).
 
 [TOC]
diff --git a/mojo/public/cpp/bindings/README.md b/mojo/public/cpp/bindings/README.md
index b37267a..9a8e2b04 100644
--- a/mojo/public/cpp/bindings/README.md
+++ b/mojo/public/cpp/bindings/README.md
@@ -1,4 +1,4 @@
-# ![Mojo Graphic](https://goo.gl/6CdlbH) Mojo C++ Bindings API
+# ![Mojo Graphic](https://goo.gl/e0Hpks) Mojo C++ Bindings API
 This document is a subset of the [Mojo documentation](/mojo).
 
 [TOC]
@@ -143,7 +143,7 @@
 doesn't actually *do* anything other than hold onto a pipe endpoint and carry
 useful compile-time type information.
 
-![Diagram illustrating InterfacePtr and InterfaceRequest on either end of a message pipe](https://docs.google.com/drawings/d/17d5gvErbQ6DthEBMS7I1WhCh9bz0n12pvNjydzuRfTI/pub?w=600&h=100)
+![Diagram illustrating InterfacePtr and InterfaceRequest on either end of a message pipe](https://docs.google.com/drawings/d/1_Ocprq7EGgTKcSE_WlOn_RBfXcr5C3FJyIbWhwzwNX8/pub?w=608&h=100)
 
 So how do we create a strongly-typed message pipe?
 
@@ -210,7 +210,7 @@
 
 This actually writes a `Log` message to the pipe.
 
-![Diagram illustrating a message traveling on a pipe from LoggerPtr to LoggerRequest](https://docs.google.com/a/google.com/drawings/d/1jWEc6jJIP2ed77Gg4JJ3EVC7hvnwcImNqQJywFwpT8g/pub?w=648&h=123)
+![Diagram illustrating a message traveling on a pipe from LoggerPtr to LoggerRequest](https://docs.google.com/drawings/d/11vnOpNP3UBLlWg4KplQuIU3r_e1XqwDFETD-O_bV-2w/pub?w=635&h=112)
 
 But as mentioned above, `InterfaceRequest` *doesn't actually do anything*, so
 that message will just sit on the pipe forever. We need a way to read messages
@@ -277,7 +277,7 @@
 3. The `Log` message is read and deserialized, causing the `Binding` to invoke
    the `Logger::Log` implementation on its bound `LoggerImpl`.
 
-![Diagram illustrating the progression of binding a request, reading a pending message, and dispatching it](https://docs.google.com/drawings/d/1c73-PegT4lmjfHoxhWrHTQXRvzxgb0wdeBa35WBwZ3Q/pub?w=550&h=500)
+![Diagram illustrating the progression of binding a request, reading a pending message, and dispatching it](https://docs.google.com/drawings/d/1F2VvfoOINGuNibomqeEU8KekYCtxYVFC00146CFGGQY/pub?w=550&h=500)
 
 As a result, our implementation will eventually log the client's `"Hello!"`
 message via `LOG(ERROR)`.
diff --git a/mojo/public/cpp/system/README.md b/mojo/public/cpp/system/README.md
index 782744f0..f169411 100644
--- a/mojo/public/cpp/system/README.md
+++ b/mojo/public/cpp/system/README.md
@@ -1,4 +1,4 @@
-# ![Mojo Graphic](https://goo.gl/6CdlbH) Mojo C++ System API
+# ![Mojo Graphic](https://goo.gl/e0Hpks) Mojo C++ System API
 This document is a subset of the [Mojo documentation](/mojo).
 
 [TOC]
@@ -225,7 +225,7 @@
 * `MANUAL` mode requires the user to manually call `Arm` and/or `ArmOrNotify`
   before any notifications will fire regarding the state of the watched handle.
   Every time the notification callback is run, the `SimpleWatcher` must be
-  rearmed again before the next one can fire. See 
+  rearmed again before the next one can fire. See
   [Arming a Watcher](/mojo/public/c/system#Arming-a-Watcher) and the
   documentation in `SimpleWatcher`'s header.
 
@@ -286,7 +286,7 @@
 
 While these API features should be used sparingly, they are sometimes necessary.
 
-See the documentation in 
+See the documentation in
 [wait.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/wait.h)
 and [wait_set.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/wait_set.h)
 for a more detailed API reference.
@@ -335,7 +335,7 @@
 ```
 
 Similar to `mojo::Wait`, `mojo::WaitMany` is primarily useful in testing. When
-waiting on multiple handles in production code, you should almost always instead 
+waiting on multiple handles in production code, you should almost always instead
 use a more efficient and more flexible `mojo::WaitSet` as described in the next
 section.
 
diff --git a/mojo/public/java/bindings/README.md b/mojo/public/java/bindings/README.md
index 821a230e..2831344f 100644
--- a/mojo/public/java/bindings/README.md
+++ b/mojo/public/java/bindings/README.md
@@ -1,4 +1,4 @@
-# ![Mojo Graphic](https://goo.gl/6CdlbH) Mojo Java Bindings API
+# ![Mojo Graphic](https://goo.gl/e0Hpks) Mojo Java Bindings API
 This document is a subset of the [Mojo documentation](/mojo).
 
 [TOC]
diff --git a/mojo/public/java/system/README.md b/mojo/public/java/system/README.md
index 3213e4c..c62f30b 100644
--- a/mojo/public/java/system/README.md
+++ b/mojo/public/java/system/README.md
@@ -1,4 +1,4 @@
-# ![Mojo Graphic](https://goo.gl/6CdlbH) Mojo Java System API
+# ![Mojo Graphic](https://goo.gl/e0Hpks) Mojo Java System API
 This document is a subset of the [Mojo documentation](/mojo).
 
 [TOC]
diff --git a/mojo/public/js/README.md b/mojo/public/js/README.md
index b6eafe9c..f510b03 100644
--- a/mojo/public/js/README.md
+++ b/mojo/public/js/README.md
@@ -1,4 +1,4 @@
-# ![Mojo Graphic](https://goo.gl/6CdlbH) Mojo JavaScript System and Bindings APIs
+# ![Mojo Graphic](https://goo.gl/e0Hpks) Mojo JavaScript System and Bindings APIs
 This document is a subset of the [Mojo documentation](/mojo).
 
 **NOTE:** The JavaScript APIs are currently in flux and will stabilize soon.
diff --git a/mojo/public/tools/bindings/README.md b/mojo/public/tools/bindings/README.md
index 737c7e6..7a62d7e 100644
--- a/mojo/public/tools/bindings/README.md
+++ b/mojo/public/tools/bindings/README.md
@@ -1,4 +1,4 @@
-# ![Mojo Graphic](https://goo.gl/6CdlbH) Mojom IDL and Bindings Generator
+# ![Mojo Graphic](https://goo.gl/e0Hpks) Mojom IDL and Bindings Generator
 This document is a subset of the [Mojo documentation](/mojo).
 
 [TOC]
diff --git a/remoting/client/chromoting_client.cc b/remoting/client/chromoting_client.cc
index 8e8e77db..8029477 100644
--- a/remoting/client/chromoting_client.cc
+++ b/remoting/client/chromoting_client.cc
@@ -233,7 +233,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   if (state == SignalStrategy::CONNECTED) {
-    VLOG(1) << "Connected as: " << signal_strategy_->GetLocalJid();
+    VLOG(1) << "Connected as: " << signal_strategy_->GetLocalAddress().jid();
     // After signaling has been connected we can try connecting to the host.
     if (connection_ &&
         connection_->state() == protocol::ConnectionToHost::INITIALIZING) {
@@ -255,9 +255,10 @@
 void ChromotingClient::StartConnection() {
   DCHECK(thread_checker_.CalledOnValidThread());
   auto session = session_manager_->Connect(
-      host_jid_, base::MakeUnique<protocol::NegotiatingClientAuthenticator>(
-                     NormalizeJid(signal_strategy_->GetLocalJid()), host_jid_,
-                     client_auth_config_));
+      SignalingAddress(host_jid_),
+      base::MakeUnique<protocol::NegotiatingClientAuthenticator>(
+          signal_strategy_->GetLocalAddress().id(), host_jid_,
+          client_auth_config_));
   if (host_experiment_sender_) {
     session->AddPlugin(host_experiment_sender_.get());
   }
diff --git a/remoting/client/plugin/chromoting_instance.cc b/remoting/client/plugin/chromoting_instance.cc
index e586b50ac..8315903 100644
--- a/remoting/client/plugin/chromoting_instance.cc
+++ b/remoting/client/plugin/chromoting_instance.cc
@@ -675,7 +675,7 @@
 
   // Setup the signal strategy.
   signal_strategy_.reset(new DelegatingSignalStrategy(
-      local_jid, plugin_task_runner_,
+      SignalingAddress(local_jid), plugin_task_runner_,
       base::Bind(&ChromotingInstance::SendOutgoingIq,
                  weak_factory_.GetWeakPtr())));
 
diff --git a/remoting/host/gcd_state_updater.cc b/remoting/host/gcd_state_updater.cc
index d7d57c9..a49f7e2 100644
--- a/remoting/host/gcd_state_updater.cc
+++ b/remoting/host/gcd_state_updater.cc
@@ -14,6 +14,7 @@
 #include "base/values.h"
 #include "remoting/base/constants.h"
 #include "remoting/base/logging.h"
+#include "remoting/signaling/signaling_address.h"
 
 namespace remoting {
 
@@ -79,7 +80,7 @@
   }
 
   if (result == GcdRestClient::NETWORK_ERROR ||
-      pending_request_jid_ != signal_strategy_->GetLocalJid()) {
+      pending_request_jid_ != signal_strategy_->GetLocalAddress().jid()) {
     // Continue exponential backoff.
     return;
   }
@@ -115,7 +116,7 @@
   // Construct an update to the remote state.
   std::unique_ptr<base::DictionaryValue> patch(new base::DictionaryValue);
   std::unique_ptr<base::DictionaryValue> base_state(new base::DictionaryValue);
-  pending_request_jid_ = signal_strategy_->GetLocalJid();
+  pending_request_jid_ = signal_strategy_->GetLocalAddress().jid();
   base_state->SetString("_jabberId", pending_request_jid_);
   base_state->SetString("_hostVersion", STRINGIZE(VERSION));
   patch->Set("base", std::move(base_state));
diff --git a/remoting/host/gcd_state_updater_unittest.cc b/remoting/host/gcd_state_updater_unittest.cc
index adcf65d..160bd65 100644
--- a/remoting/host/gcd_state_updater_unittest.cc
+++ b/remoting/host/gcd_state_updater_unittest.cc
@@ -35,12 +35,11 @@
         token_getter_(OAuthTokenGetter::SUCCESS,
                       "<fake_user_email>",
                       "<fake_access_token>"),
-        rest_client_(new GcdRestClient(
-            "http://gcd_base_url",
-            "<gcd_device_id>",
-            nullptr,
-            &token_getter_)),
-        signal_strategy_("local_jid") {
+        rest_client_(new GcdRestClient("http://gcd_base_url",
+                                       "<gcd_device_id>",
+                                       nullptr,
+                                       &token_getter_)),
+        signal_strategy_(SignalingAddress("local_jid")) {
     rest_client_->SetClockForTest(base::WrapUnique(new base::SimpleTestClock));
   }
 
@@ -96,7 +95,7 @@
   task_runner_->RunUntilIdle();
   signal_strategy_.Disconnect();
   task_runner_->RunUntilIdle();
-  signal_strategy_.SetLocalJid("local_jid2");
+  signal_strategy_.SetLocalAddress(SignalingAddress("local_jid2"));
   signal_strategy_.Connect();
   task_runner_->RunUntilIdle();
 
diff --git a/remoting/host/heartbeat_sender.cc b/remoting/host/heartbeat_sender.cc
index e452b5e..8df8890f5 100644
--- a/remoting/host/heartbeat_sender.cc
+++ b/remoting/host/heartbeat_sender.cc
@@ -19,9 +19,9 @@
 #include "remoting/host/host_details.h"
 #include "remoting/host/server_log_entry_host.h"
 #include "remoting/signaling/iq_sender.h"
-#include "remoting/signaling/jid_util.h"
 #include "remoting/signaling/server_log_entry.h"
 #include "remoting/signaling/signal_strategy.h"
+#include "remoting/signaling/signaling_address.h"
 #include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
 #include "third_party/libjingle_xmpp/xmpp/constants.h"
 
@@ -349,7 +349,7 @@
   std::unique_ptr<XmlElement> signature_tag(
       new XmlElement(QName(kChromotingXmlNamespace, kHeartbeatSignatureTag)));
 
-  std::string message = NormalizeJid(signal_strategy_->GetLocalJid()) + ' ' +
+  std::string message = signal_strategy_->GetLocalAddress().jid() + ' ' +
                         base::IntToString(sequence_id_);
   std::string signature(host_key_pair_->SignMessage(message));
   signature_tag->AddText(signature);
diff --git a/remoting/host/heartbeat_sender_unittest.cc b/remoting/host/heartbeat_sender_unittest.cc
index 06fab1c3..18f9126 100644
--- a/remoting/host/heartbeat_sender_unittest.cc
+++ b/remoting/host/heartbeat_sender_unittest.cc
@@ -57,6 +57,8 @@
 class HeartbeatSenderTest
     : public testing::Test {
  protected:
+  HeartbeatSenderTest() : signal_strategy_(SignalingAddress(kTestJid)) {}
+
   void SetUp() override {
     key_pair_ = RsaKeyPair::FromString(kTestRsaKeyPair);
     ASSERT_TRUE(key_pair_.get());
@@ -66,8 +68,6 @@
         .WillRepeatedly(AddListener(&signal_strategy_listeners_));
     EXPECT_CALL(signal_strategy_, RemoveListener(NotNull()))
         .WillRepeatedly(RemoveListener(&signal_strategy_listeners_));
-    EXPECT_CALL(signal_strategy_, GetLocalJid())
-        .WillRepeatedly(Return(kTestJid));
     EXPECT_CALL(mock_unknown_host_id_error_callback_, Run())
         .Times(0);
 
@@ -102,8 +102,6 @@
 // Call Start() followed by Stop(), and make sure a valid heartbeat is sent.
 TEST_F(HeartbeatSenderTest, DoSendStanza) {
   XmlElement* sent_iq = nullptr;
-  EXPECT_CALL(signal_strategy_, GetLocalJid())
-      .WillRepeatedly(Return(kTestJid));
   EXPECT_CALL(signal_strategy_, GetNextId())
       .WillOnce(Return(kStanzaId));
   EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
@@ -126,8 +124,6 @@
 // are sent, with the correct sequence IDs.
 TEST_F(HeartbeatSenderTest, DoSendStanzaTwice) {
   XmlElement* sent_iq = nullptr;
-  EXPECT_CALL(signal_strategy_, GetLocalJid())
-      .WillRepeatedly(Return(kTestJid));
   EXPECT_CALL(signal_strategy_, GetNextId())
       .WillOnce(Return(kStanzaId));
   EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
@@ -145,8 +141,6 @@
   heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::DISCONNECTED);
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_CALL(signal_strategy_, GetLocalJid())
-      .WillRepeatedly(Return(kTestJid));
   EXPECT_CALL(signal_strategy_, GetNextId())
       .WillOnce(Return(kStanzaId + 1));
   EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
@@ -167,8 +161,6 @@
 // are sent, with the correct sequence IDs.
 TEST_F(HeartbeatSenderTest, DoSendStanzaWithExpectedSequenceId) {
   XmlElement* sent_iq = nullptr;
-  EXPECT_CALL(signal_strategy_, GetLocalJid())
-      .WillRepeatedly(Return(kTestJid));
   EXPECT_CALL(signal_strategy_, GetNextId())
       .WillOnce(Return(kStanzaId));
   EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
@@ -184,8 +176,6 @@
   ValidateHeartbeatStanza(stanza.get(), "0", nullptr);
 
   XmlElement* sent_iq2 = nullptr;
-  EXPECT_CALL(signal_strategy_, GetLocalJid())
-      .WillRepeatedly(Return(kTestJid));
   EXPECT_CALL(signal_strategy_, GetNextId())
       .WillOnce(Return(kStanzaId + 1));
   EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
@@ -248,8 +238,6 @@
   XmlElement* sent_iq = nullptr;
   base::MockCallback<base::Callback<void(bool success)>> mock_ack_callback;
 
-  EXPECT_CALL(signal_strategy_, GetLocalJid())
-      .WillRepeatedly(Return(kTestJid));
   EXPECT_CALL(signal_strategy_, GetNextId())
       .WillOnce(Return(kStanzaId));
   EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
@@ -276,8 +264,6 @@
 TEST_F(HeartbeatSenderTest, ProcessHostOfflineResponses) {
   base::MockCallback<base::Callback<void(bool success)>> mock_ack_callback;
 
-  EXPECT_CALL(signal_strategy_, GetLocalJid())
-      .WillRepeatedly(Return(kTestJid));
   EXPECT_CALL(signal_strategy_, GetNextId())
       .WillOnce(Return(kStanzaId));
   EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
@@ -321,8 +307,6 @@
 // The first heartbeat should include host OS information.
 TEST_F(HeartbeatSenderTest, HostOsInfo) {
   XmlElement* sent_iq = nullptr;
-  EXPECT_CALL(signal_strategy_, GetLocalJid())
-      .WillRepeatedly(Return(kTestJid));
   EXPECT_CALL(signal_strategy_, GetNextId())
       .WillOnce(Return(kStanzaId));
   EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
diff --git a/remoting/host/host_change_notification_listener.cc b/remoting/host/host_change_notification_listener.cc
index 3c079f1..c80af519 100644
--- a/remoting/host/host_change_notification_listener.cc
+++ b/remoting/host/host_change_notification_listener.cc
@@ -55,9 +55,13 @@
   const std::string& host_id =
       host_changed_element->Attr(QName(kChromotingXmlNamespace, "hostid"));
   const std::string& from = stanza->Attr(buzz::QN_FROM);
-  const std::string& to = stanza->Attr(buzz::QN_TO);
+
+  std::string to_error;
+  SignalingAddress to =
+      SignalingAddress::Parse(stanza, SignalingAddress::TO, &to_error);
+
   if (host_id == host_id_ && from == directory_bot_jid_ &&
-      to == signal_strategy_->GetLocalJid()) {
+      to == signal_strategy_->GetLocalAddress()) {
     const std::string& operation =
         host_changed_element->Attr(QName(kChromotingXmlNamespace, "operation"));
     if (operation == "delete") {
diff --git a/remoting/host/host_change_notification_listener_unittest.cc b/remoting/host/host_change_notification_listener_unittest.cc
index 7bbf290b..aea4233 100644
--- a/remoting/host/host_change_notification_listener_unittest.cc
+++ b/remoting/host/host_change_notification_listener_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "remoting/base/constants.h"
 #include "remoting/signaling/mock_signal_strategy.h"
+#include "remoting/signaling/signaling_address.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
@@ -45,6 +46,8 @@
 
 class HostChangeNotificationListenerTest : public testing::Test {
  protected:
+  HostChangeNotificationListenerTest()
+      : signal_strategy_(SignalingAddress(kTestJid)) {}
   class MockListener : public HostChangeNotificationListener::Listener {
    public:
     MOCK_METHOD0(OnHostDeleted, void());
@@ -55,8 +58,6 @@
         .WillRepeatedly(AddListener(&signal_strategy_listeners_));
     EXPECT_CALL(signal_strategy_, RemoveListener(NotNull()))
         .WillRepeatedly(RemoveListener(&signal_strategy_listeners_));
-    EXPECT_CALL(signal_strategy_, GetLocalJid())
-        .WillRepeatedly(Return(kTestJid));
 
     host_change_notification_listener_.reset(new HostChangeNotificationListener(
         &mock_listener_, kHostId, &signal_strategy_, kTestBotJid));
diff --git a/remoting/host/host_status_logger_unittest.cc b/remoting/host/host_status_logger_unittest.cc
index fee9ae4..01160ee 100644
--- a/remoting/host/host_status_logger_unittest.cc
+++ b/remoting/host/host_status_logger_unittest.cc
@@ -124,7 +124,7 @@
 
 class HostStatusLoggerTest : public testing::Test {
  public:
-  HostStatusLoggerTest() {}
+  HostStatusLoggerTest() : signal_strategy_(SignalingAddress(kHostJid)) {}
   void SetUp() override {
     EXPECT_CALL(signal_strategy_, AddListener(_));
     host_status_logger_.reset(
@@ -146,8 +146,6 @@
   base::RunLoop run_loop;
   {
     InSequence s;
-    EXPECT_CALL(signal_strategy_, GetLocalJid())
-        .WillRepeatedly(Return(kHostJid));
     EXPECT_CALL(signal_strategy_, AddListener(_));
     EXPECT_CALL(signal_strategy_, GetNextId());
     EXPECT_CALL(signal_strategy_, SendStanzaPtr(IsClientConnected("direct")))
@@ -174,10 +172,9 @@
   host_status_logger_->OnClientRouteChange(kClientJid1, "video", route);
   host_status_logger_->OnClientAuthenticated(kClientJid1);
   host_status_logger_->OnClientConnected(kClientJid1);
+
   {
     InSequence s;
-    EXPECT_CALL(signal_strategy_, GetLocalJid())
-        .WillRepeatedly(Return(kHostJid));
     EXPECT_CALL(signal_strategy_, AddListener(_));
     EXPECT_CALL(signal_strategy_, GetNextId());
     EXPECT_CALL(signal_strategy_, SendStanzaPtr(IsClientConnected("direct")))
@@ -203,10 +200,9 @@
   host_status_logger_->OnClientRouteChange(kClientJid2, "video", route2);
   host_status_logger_->OnClientAuthenticated(kClientJid2);
   host_status_logger_->OnClientConnected(kClientJid2);
+
   {
     InSequence s;
-    EXPECT_CALL(signal_strategy_, GetLocalJid())
-        .WillRepeatedly(Return(kHostJid));
     EXPECT_CALL(signal_strategy_, AddListener(_));
     EXPECT_CALL(signal_strategy_, GetNextId());
     EXPECT_CALL(signal_strategy_,
@@ -223,10 +219,9 @@
 
 TEST_F(HostStatusLoggerTest, HandleRouteChangeInUnusualOrder) {
   base::RunLoop run_loop;
+
   {
     InSequence s;
-    EXPECT_CALL(signal_strategy_, GetLocalJid())
-        .WillRepeatedly(Return(kHostJid));
     EXPECT_CALL(signal_strategy_, AddListener(_));
     EXPECT_CALL(signal_strategy_, GetNextId());
     EXPECT_CALL(signal_strategy_, SendStanzaPtr(IsClientConnected("direct")))
diff --git a/remoting/host/it2me/it2me_host_unittest.cc b/remoting/host/it2me/it2me_host_unittest.cc
index ef11fbd..22a719a 100644
--- a/remoting/host/it2me/it2me_host_unittest.cc
+++ b/remoting/host/it2me/it2me_host_unittest.cc
@@ -188,11 +188,12 @@
   std::unique_ptr<FakeIt2MeDialogFactory> dialog_factory(
       new FakeIt2MeDialogFactory());
   dialog_factory_ = dialog_factory.get();
-  it2me_host_ =
-      new It2MeHost(std::move(host_context), /*policy_watcher=*/nullptr,
-                    std::move(dialog_factory), weak_factory_.GetWeakPtr(),
-                    base::WrapUnique(new FakeSignalStrategy("fake_local_jid")),
-                    "fake_user_name", "fake_bot_jid");
+  it2me_host_ = new It2MeHost(
+      std::move(host_context), /*policy_watcher=*/nullptr,
+      std::move(dialog_factory), weak_factory_.GetWeakPtr(),
+      base::WrapUnique(
+          new FakeSignalStrategy(SignalingAddress("fake_local_jid"))),
+      "fake_user_name", "fake_bot_jid");
 }
 
 void It2MeHostTest::TearDown() {
diff --git a/remoting/host/it2me/it2me_native_messaging_host.cc b/remoting/host/it2me/it2me_native_messaging_host.cc
index 0b3a716..9580d3d5 100644
--- a/remoting/host/it2me/it2me_native_messaging_host.cc
+++ b/remoting/host/it2me/it2me_native_messaging_host.cc
@@ -298,7 +298,7 @@
 
     auto delegating_signal_strategy =
         base::MakeUnique<DelegatingSignalStrategy>(
-            local_jid, host_context_->network_task_runner(),
+            SignalingAddress(local_jid), host_context_->network_task_runner(),
             base::Bind(&It2MeNativeMessagingHost::SendOutgoingIq,
                        weak_factory_.GetWeakPtr()));
     incoming_message_callback_ =
diff --git a/remoting/host/register_support_host_request.cc b/remoting/host/register_support_host_request.cc
index 786c3119..72c2345 100644
--- a/remoting/host/register_support_host_request.cc
+++ b/remoting/host/register_support_host_request.cc
@@ -17,6 +17,7 @@
 #include "remoting/signaling/iq_sender.h"
 #include "remoting/signaling/jid_util.h"
 #include "remoting/signaling/signal_strategy.h"
+#include "remoting/signaling/signaling_address.h"
 #include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
 #include "third_party/libjingle_xmpp/xmpp/constants.h"
 
@@ -65,7 +66,7 @@
 
     request_ = iq_sender_->SendIq(
         buzz::STR_SET, directory_bot_jid_,
-        CreateRegistrationRequest(signal_strategy_->GetLocalJid()),
+        CreateRegistrationRequest(signal_strategy_->GetLocalAddress().jid()),
         base::Bind(&RegisterSupportHostRequest::ProcessResponse,
                    base::Unretained(this)));
   } else if (state == SignalStrategy::DISCONNECTED) {
diff --git a/remoting/host/register_support_host_request_unittest.cc b/remoting/host/register_support_host_request_unittest.cc
index a23a59e..b97c192 100644
--- a/remoting/host/register_support_host_request_unittest.cc
+++ b/remoting/host/register_support_host_request_unittest.cc
@@ -18,6 +18,7 @@
 #include "remoting/base/test_rsa_key_pair.h"
 #include "remoting/signaling/iq_sender.h"
 #include "remoting/signaling/mock_signal_strategy.h"
+#include "remoting/signaling/signaling_address.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
@@ -54,6 +55,9 @@
 class RegisterSupportHostRequestTest : public testing::Test {
  public:
  protected:
+  RegisterSupportHostRequestTest()
+      : signal_strategy_(SignalingAddress(kTestJid)) {}
+
   void SetUp() override {
     key_pair_ = RsaKeyPair::FromString(kTestRsaKeyPair);
     ASSERT_TRUE(key_pair_.get());
@@ -62,8 +66,6 @@
         .WillRepeatedly(AddListener(&signal_strategy_listeners_));
     EXPECT_CALL(signal_strategy_, RemoveListener(NotNull()))
         .WillRepeatedly(RemoveListener(&signal_strategy_listeners_));
-    EXPECT_CALL(signal_strategy_, GetLocalJid())
-        .WillRepeatedly(Return(kTestJid));
   }
 
   base::MessageLoop message_loop_;
diff --git a/remoting/host/signaling_connector.cc b/remoting/host/signaling_connector.cc
index 1309403..1b617f4 100644
--- a/remoting/host/signaling_connector.cc
+++ b/remoting/host/signaling_connector.cc
@@ -14,6 +14,7 @@
 #include "net/url_request/url_request_context_getter.h"
 #include "remoting/base/logging.h"
 #include "remoting/host/dns_blackhole_checker.h"
+#include "remoting/signaling/signaling_address.h"
 
 namespace remoting {
 
@@ -70,7 +71,7 @@
 
   if (state == SignalStrategy::CONNECTED) {
     HOST_LOG << "Signaling connected. New JID: "
-             << signal_strategy_->GetLocalJid();
+             << signal_strategy_->GetLocalAddress().jid();
     reconnect_attempts_ = 0;
   } else if (state == SignalStrategy::DISCONNECTED) {
     HOST_LOG << "Signaling disconnected. error="
diff --git a/remoting/protocol/jingle_session.cc b/remoting/protocol/jingle_session.cc
index 72043a9..14d3350 100644
--- a/remoting/protocol/jingle_session.cc
+++ b/remoting/protocol/jingle_session.cc
@@ -197,13 +197,13 @@
 }
 
 void JingleSession::StartConnection(
-    const std::string& peer_jid,
+    const SignalingAddress& peer_address,
     std::unique_ptr<Authenticator> authenticator) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(authenticator.get());
   DCHECK_EQ(authenticator->state(), Authenticator::MESSAGE_READY);
 
-  peer_address_ = SignalingAddress(peer_jid);
+  peer_address_ = peer_address;
   authenticator_ = std::move(authenticator);
 
   // Generate random session ID. There are usually not more than 1
@@ -792,7 +792,8 @@
   }
   std::unique_ptr<JingleMessage> message(new JingleMessage(
       peer_address_, JingleMessage::SESSION_INITIATE, session_id_));
-  message->initiator = session_manager_->signal_strategy_->GetLocalJid();
+  message->initiator =
+      session_manager_->signal_strategy_->GetLocalAddress().jid();
   message->description.reset(new ContentDescription(
       session_manager_->protocol_config_->Clone(),
       authenticator_->GetNextMessage()));
diff --git a/remoting/protocol/jingle_session.h b/remoting/protocol/jingle_session.h
index 00a7b1e2..3b9c1fe0 100644
--- a/remoting/protocol/jingle_session.h
+++ b/remoting/protocol/jingle_session.h
@@ -53,7 +53,7 @@
   explicit JingleSession(JingleSessionManager* session_manager);
 
   // Start connection by sending session-initiate message.
-  void StartConnection(const std::string& peer_jid,
+  void StartConnection(const SignalingAddress& peer_address,
                        std::unique_ptr<Authenticator> authenticator);
 
   // Called by JingleSessionManager for incoming connections.
diff --git a/remoting/protocol/jingle_session_manager.cc b/remoting/protocol/jingle_session_manager.cc
index 5d564f7..c13cdb09 100644
--- a/remoting/protocol/jingle_session_manager.cc
+++ b/remoting/protocol/jingle_session_manager.cc
@@ -46,10 +46,10 @@
 }
 
 std::unique_ptr<Session> JingleSessionManager::Connect(
-    const std::string& host_jid,
+    const SignalingAddress& peer_address,
     std::unique_ptr<Authenticator> authenticator) {
   std::unique_ptr<JingleSession> session(new JingleSession(this));
-  session->StartConnection(host_jid, std::move(authenticator));
+  session->StartConnection(peer_address, std::move(authenticator));
   sessions_[session->session_id_] = session.get();
   return std::move(session);
 }
@@ -84,7 +84,7 @@
 
     std::unique_ptr<Authenticator> authenticator =
         authenticator_factory_->CreateAuthenticator(
-            signal_strategy_->GetLocalJid(), message->from.id());
+            signal_strategy_->GetLocalAddress().id(), message->from.id());
 
     JingleSession* session = new JingleSession(this);
     session->InitializeIncomingConnection(*message,
diff --git a/remoting/protocol/jingle_session_manager.h b/remoting/protocol/jingle_session_manager.h
index c1d7311..2024a7a 100644
--- a/remoting/protocol/jingle_session_manager.h
+++ b/remoting/protocol/jingle_session_manager.h
@@ -41,7 +41,7 @@
   void set_protocol_config(
       std::unique_ptr<CandidateSessionConfig> config) override;
   std::unique_ptr<Session> Connect(
-      const std::string& host_jid,
+      const SignalingAddress& peer_address,
       std::unique_ptr<Authenticator> authenticator) override;
   void set_authenticator_factory(
       std::unique_ptr<AuthenticatorFactory> authenticator_factory) override;
diff --git a/remoting/protocol/jingle_session_unittest.cc b/remoting/protocol/jingle_session_unittest.cc
index 4575e6e1..39c5ca2a5 100644
--- a/remoting/protocol/jingle_session_unittest.cc
+++ b/remoting/protocol/jingle_session_unittest.cc
@@ -184,10 +184,14 @@
 
   void CreateSessionManagers(int auth_round_trips, int messages_till_start,
                              FakeAuthenticator::Action auth_action) {
-    if (!host_signal_strategy_)
-      host_signal_strategy_.reset(new FakeSignalStrategy(kHostJid));
-    if (!client_signal_strategy_)
-      client_signal_strategy_.reset(new FakeSignalStrategy(kClientJid));
+    if (!host_signal_strategy_) {
+      host_signal_strategy_.reset(
+          new FakeSignalStrategy(SignalingAddress(kHostJid)));
+    }
+    if (!client_signal_strategy_) {
+      client_signal_strategy_.reset(
+          new FakeSignalStrategy(SignalingAddress(kClientJid)));
+    }
     FakeSignalStrategy::Connect(host_signal_strategy_.get(),
                                 client_signal_strategy_.get());
 
@@ -273,8 +277,8 @@
   }
 
   void ConnectClient(std::unique_ptr<Authenticator> authenticator) {
-    client_session_ =
-        client_server_->Connect(host_jid_, std::move(authenticator));
+    client_session_ = client_server_->Connect(SignalingAddress(host_jid_),
+                                              std::move(authenticator));
     client_session_->SetEventHandler(&client_session_event_handler_);
     client_session_->SetTransport(&client_transport_);
     client_session_->AddPlugin(&client_plugin_);
@@ -364,7 +368,8 @@
 
   std::unique_ptr<Authenticator> authenticator(new FakeAuthenticator(
       FakeAuthenticator::CLIENT, 1, FakeAuthenticator::ACCEPT, true));
-  client_session_ = client_server_->Connect(kHostJid, std::move(authenticator));
+  client_session_ = client_server_->Connect(SignalingAddress(kHostJid),
+                                            std::move(authenticator));
   client_session_->SetEventHandler(&client_session_event_handler_);
 
   base::RunLoop().RunUntilIdle();
@@ -388,7 +393,8 @@
 
 TEST_F(JingleSessionTest, MixedCaseHostJid) {
   std::string host_jid = std::string("A") + kHostJid;
-  host_signal_strategy_.reset(new FakeSignalStrategy(host_jid));
+  host_signal_strategy_.reset(
+      new FakeSignalStrategy(SignalingAddress(host_jid)));
 
   // Imitate host JID being lower-cased when stored in the directory.
   host_jid_ = base::ToLowerASCII(host_jid);
@@ -399,7 +405,7 @@
 
 TEST_F(JingleSessionTest, MixedCaseClientJid) {
   client_signal_strategy_.reset(
-      new FakeSignalStrategy(std::string("A") + kClientJid));
+      new FakeSignalStrategy(SignalingAddress(std::string("A") + kClientJid)));
   CreateSessionManagers(1, FakeAuthenticator::ACCEPT);
   InitiateConnection(1, FakeAuthenticator::ACCEPT, false);
 }
@@ -476,7 +482,8 @@
   // Disable all video codecs so the host will reject connection.
   config->mutable_video_configs()->clear();
   client_server_->set_protocol_config(std::move(config));
-  client_session_ = client_server_->Connect(kHostJid, std::move(authenticator));
+  client_session_ = client_server_->Connect(SignalingAddress(kHostJid),
+                                            std::move(authenticator));
   client_session_->SetEventHandler(&client_session_event_handler_);
 
   base::RunLoop().RunUntilIdle();
@@ -502,7 +509,8 @@
       CandidateSessionConfig::CreateDefault();
   config->set_ice_supported(false);
   client_server_->set_protocol_config(std::move(config));
-  client_session_ = client_server_->Connect(kHostJid, std::move(authenticator));
+  client_session_ = client_server_->Connect(SignalingAddress(kHostJid),
+                                            std::move(authenticator));
   client_session_->SetEventHandler(&client_session_event_handler_);
 
   base::RunLoop().RunUntilIdle();
@@ -530,7 +538,8 @@
   std::unique_ptr<Authenticator> authenticator(new FakeAuthenticator(
       FakeAuthenticator::CLIENT, 3, FakeAuthenticator::ACCEPT, true));
 
-  client_session_ = client_server_->Connect(kHostJid, std::move(authenticator));
+  client_session_ = client_server_->Connect(SignalingAddress(kHostJid),
+                                            std::move(authenticator));
 
   base::RunLoop().RunUntilIdle();
 }
@@ -557,7 +566,8 @@
   std::unique_ptr<Authenticator> authenticator(new FakeAuthenticator(
       FakeAuthenticator::CLIENT, 3, FakeAuthenticator::ACCEPT, true));
 
-  client_session_ = client_server_->Connect(kHostJid, std::move(authenticator));
+  client_session_ = client_server_->Connect(SignalingAddress(kHostJid),
+                                            std::move(authenticator));
   base::RunLoop().RunUntilIdle();
 }
 
@@ -638,9 +648,10 @@
 
 TEST_F(JingleSessionTest, ImmediatelyCloseSessionAfterConnect) {
   CreateSessionManagers(3, FakeAuthenticator::ACCEPT);
-  client_session_ = client_server_->Connect(host_jid_,
-      base::MakeUnique<FakeAuthenticator>(
-          FakeAuthenticator::CLIENT, 3, FakeAuthenticator::ACCEPT, true));
+  client_session_ = client_server_->Connect(
+      SignalingAddress(host_jid_),
+      base::MakeUnique<FakeAuthenticator>(FakeAuthenticator::CLIENT, 3,
+                                          FakeAuthenticator::ACCEPT, true));
   client_session_->Close(HOST_OVERLOAD);
   base::RunLoop().RunUntilIdle();
   // We should only send a SESSION_TERMINATE message if the session has been
diff --git a/remoting/protocol/protocol_mock_objects.cc b/remoting/protocol/protocol_mock_objects.cc
index 70581e4..10ffb35 100644
--- a/remoting/protocol/protocol_mock_objects.cc
+++ b/remoting/protocol/protocol_mock_objects.cc
@@ -11,55 +11,43 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "remoting/protocol/session_plugin.h"
 #include "remoting/protocol/video_stream.h"
+#include "remoting/signaling/signaling_address.h"
 
 namespace remoting {
 namespace protocol {
 
 MockAuthenticator::MockAuthenticator() {}
-
 MockAuthenticator::~MockAuthenticator() {}
 
 MockConnectionToClientEventHandler::MockConnectionToClientEventHandler() {}
-
 MockConnectionToClientEventHandler::~MockConnectionToClientEventHandler() {}
 
 MockClipboardStub::MockClipboardStub() {}
-
 MockClipboardStub::~MockClipboardStub() {}
 
 MockInputStub::MockInputStub() {}
-
 MockInputStub::~MockInputStub() {}
 
 MockHostStub::MockHostStub() {}
-
 MockHostStub::~MockHostStub() {}
 
 MockClientStub::MockClientStub() {}
-
 MockClientStub::~MockClientStub() {}
 
 MockCursorShapeStub::MockCursorShapeStub() {}
-
 MockCursorShapeStub::~MockCursorShapeStub() {}
 
 MockVideoStub::MockVideoStub() {}
-
 MockVideoStub::~MockVideoStub() {}
 
 MockSession::MockSession() {}
-
 MockSession::~MockSession() {}
 
 MockSessionManager::MockSessionManager() {}
-
 MockSessionManager::~MockSessionManager() {}
 
-MockPairingRegistryDelegate::MockPairingRegistryDelegate() {
-}
-
-MockPairingRegistryDelegate::~MockPairingRegistryDelegate() {
-}
+MockPairingRegistryDelegate::MockPairingRegistryDelegate() {}
+MockPairingRegistryDelegate::~MockPairingRegistryDelegate() {}
 
 std::unique_ptr<base::ListValue> MockPairingRegistryDelegate::LoadAll() {
   std::unique_ptr<base::ListValue> result(new base::ListValue());
diff --git a/remoting/protocol/protocol_mock_objects.h b/remoting/protocol/protocol_mock_objects.h
index a39d619b..391eb4d 100644
--- a/remoting/protocol/protocol_mock_objects.h
+++ b/remoting/protocol/protocol_mock_objects.h
@@ -30,6 +30,7 @@
 #include "remoting/protocol/session_manager.h"
 #include "remoting/protocol/transport.h"
 #include "remoting/protocol/video_stub.h"
+#include "remoting/signaling/signaling_address.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
 
@@ -206,15 +207,15 @@
   void set_protocol_config(
       std::unique_ptr<CandidateSessionConfig> config) override {}
   MOCK_METHOD2(ConnectPtr,
-               Session*(const std::string& host_jid,
+               Session*(const SignalingAddress& peer_address,
                         Authenticator* authenticator));
   MOCK_METHOD0(Close, void());
   MOCK_METHOD1(set_authenticator_factory_ptr,
                void(AuthenticatorFactory* factory));
   std::unique_ptr<Session> Connect(
-      const std::string& host_jid,
+      const SignalingAddress& peer_address,
       std::unique_ptr<Authenticator> authenticator) override {
-    return base::WrapUnique(ConnectPtr(host_jid, authenticator.get()));
+    return base::WrapUnique(ConnectPtr(peer_address, authenticator.get()));
   }
   void set_authenticator_factory(
       std::unique_ptr<AuthenticatorFactory> authenticator_factory) override {
diff --git a/remoting/protocol/session_manager.h b/remoting/protocol/session_manager.h
index 71c5007..b8cd08f0 100644
--- a/remoting/protocol/session_manager.h
+++ b/remoting/protocol/session_manager.h
@@ -62,6 +62,7 @@
 
 namespace remoting {
 
+class SignalingAddress;
 class SignalStrategy;
 
 namespace protocol {
@@ -108,12 +109,12 @@
   virtual void set_protocol_config(
       std::unique_ptr<CandidateSessionConfig> config) = 0;
 
-  // Tries to create a session to the host |jid|.
+  // Creates a new outgoing session.
   //
-  // |host_jid| is the full jid of the host to connect to.
-  // |authenticator| is a client authenticator for the session.
+  // |peer_address| - full SignalingAddress to connect to.
+  // |authenticator| - client authenticator for the session.
   virtual std::unique_ptr<Session> Connect(
-      const std::string& host_jid,
+      const SignalingAddress& peer_address,
       std::unique_ptr<Authenticator> authenticator) = 0;
 
   // Set authenticator factory that should be used to authenticate
diff --git a/remoting/signaling/delegating_signal_strategy.cc b/remoting/signaling/delegating_signal_strategy.cc
index 0f6dc7d..c4340ac 100644
--- a/remoting/signaling/delegating_signal_strategy.cc
+++ b/remoting/signaling/delegating_signal_strategy.cc
@@ -14,10 +14,10 @@
 namespace remoting {
 
 DelegatingSignalStrategy::DelegatingSignalStrategy(
-    std::string local_jid,
+    const SignalingAddress& local_address,
     scoped_refptr<base::SingleThreadTaskRunner> client_task_runner,
     const IqCallback& send_iq_callback)
-    : local_jid_(local_jid),
+    : local_address_(local_address),
       delegate_task_runner_(base::ThreadTaskRunnerHandle::Get()),
       client_task_runner_(client_task_runner),
       send_iq_callback_(send_iq_callback),
@@ -83,9 +83,9 @@
   return OK;
 }
 
-std::string DelegatingSignalStrategy::GetLocalJid() const {
+const SignalingAddress& DelegatingSignalStrategy::GetLocalAddress() const {
   DCHECK(client_task_runner_->BelongsToCurrentThread());
-  return local_jid_;
+  return local_address_;
 }
 
 void DelegatingSignalStrategy::AddListener(Listener* listener) {
@@ -101,7 +101,7 @@
 bool DelegatingSignalStrategy::SendStanza(
     std::unique_ptr<buzz::XmlElement> stanza) {
   DCHECK(client_task_runner_->BelongsToCurrentThread());
-  stanza->SetAttr(buzz::QN_FROM, GetLocalJid());
+  GetLocalAddress().SetInMessage(stanza.get(), SignalingAddress::FROM);
   delegate_task_runner_->PostTask(FROM_HERE,
                                   base::Bind(send_iq_callback_, stanza->Str()));
   return true;
diff --git a/remoting/signaling/delegating_signal_strategy.h b/remoting/signaling/delegating_signal_strategy.h
index d41de42..7b997877 100644
--- a/remoting/signaling/delegating_signal_strategy.h
+++ b/remoting/signaling/delegating_signal_strategy.h
@@ -10,6 +10,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list_threadsafe.h"
 #include "remoting/signaling/signal_strategy.h"
+#include "remoting/signaling/signaling_address.h"
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -35,7 +36,7 @@
   typedef base::RepeatingCallback<void(const std::string&)> IqCallback;
 
   DelegatingSignalStrategy(
-      std::string local_jid,
+      const SignalingAddress& local_address,
       scoped_refptr<base::SingleThreadTaskRunner> client_task_runner,
       const IqCallback& send_iq_callback);
   ~DelegatingSignalStrategy() override;
@@ -47,7 +48,7 @@
   void Disconnect() override;
   State GetState() const override;
   Error GetError() const override;
-  std::string GetLocalJid() const override;
+  const SignalingAddress& GetLocalAddress() const override;
   void AddListener(Listener* listener) override;
   void RemoveListener(Listener* listener) override;
   bool SendStanza(std::unique_ptr<buzz::XmlElement> stanza) override;
@@ -61,7 +62,7 @@
 
   void OnIncomingMessage(const std::string& message);
 
-  std::string local_jid_;
+  SignalingAddress local_address_;
   scoped_refptr<base::SingleThreadTaskRunner> delegate_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> client_task_runner_;
 
diff --git a/remoting/signaling/fake_signal_strategy.cc b/remoting/signaling/fake_signal_strategy.cc
index fed04e5..dc4db99 100644
--- a/remoting/signaling/fake_signal_strategy.cc
+++ b/remoting/signaling/fake_signal_strategy.cc
@@ -28,9 +28,9 @@
   peer2->ConnectTo(peer1);
 }
 
-FakeSignalStrategy::FakeSignalStrategy(const std::string& jid)
+FakeSignalStrategy::FakeSignalStrategy(const SignalingAddress& address)
     : main_thread_(base::ThreadTaskRunnerHandle::Get()),
-      jid_(jid),
+      address_(address),
       last_id_(0),
       weak_factory_(this) {
   DetachFromThread();
@@ -59,9 +59,9 @@
   }
 }
 
-void FakeSignalStrategy::SetLocalJid(const std::string& jid) {
+void FakeSignalStrategy::SetLocalAddress(const SignalingAddress& address) {
   DCHECK(CalledOnValidThread());
-  jid_ = jid;
+  address_ = address;
 }
 
 void FakeSignalStrategy::SimulateMessageReordering() {
@@ -89,9 +89,9 @@
   return OK;
 }
 
-std::string FakeSignalStrategy::GetLocalJid() const {
+const SignalingAddress& FakeSignalStrategy::GetLocalAddress() const {
   DCHECK(CalledOnValidThread());
-  return jid_;
+  return address_;
 }
 
 void FakeSignalStrategy::AddListener(Listener* listener) {
@@ -107,7 +107,7 @@
 bool FakeSignalStrategy::SendStanza(std::unique_ptr<buzz::XmlElement> stanza) {
   DCHECK(CalledOnValidThread());
 
-  stanza->SetAttr(buzz::QN_FROM, jid_);
+  address_.SetInMessage(stanza.get(), SignalingAddress::FROM);
 
   if (peer_callback_.is_null())
     return false;
@@ -164,10 +164,12 @@
   buzz::XmlElement* stanza_ptr = stanza.get();
   received_messages_.push_back(stanza.release());
 
-  const std::string& to_field = stanza_ptr->Attr(buzz::QN_TO);
-  if (NormalizeJid(to_field) != NormalizeJid(jid_)) {
-    LOG(WARNING) << "Dropping stanza that is addressed to " << to_field
-                 << ". Local jid: " << jid_
+  std::string to_error;
+  SignalingAddress to =
+      SignalingAddress::Parse(stanza_ptr, SignalingAddress::TO, &to_error);
+  if (to != address_) {
+    LOG(WARNING) << "Dropping stanza that is addressed to " << to.id()
+                 << ". Local address: " << address_.id()
                  << ". Message content: " << stanza_ptr->Str();
     return;
   }
diff --git a/remoting/signaling/fake_signal_strategy.h b/remoting/signaling/fake_signal_strategy.h
index 0b5d912..c4da7e4 100644
--- a/remoting/signaling/fake_signal_strategy.h
+++ b/remoting/signaling/fake_signal_strategy.h
@@ -15,6 +15,7 @@
 #include "base/threading/non_thread_safe.h"
 #include "remoting/signaling/iq_sender.h"
 #include "remoting/signaling/signal_strategy.h"
+#include "remoting/signaling/signaling_address.h"
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -29,7 +30,7 @@
   // must belong to the current thread.
   static void Connect(FakeSignalStrategy* peer1, FakeSignalStrategy* peer2);
 
-  FakeSignalStrategy(const std::string& jid);
+  FakeSignalStrategy(const SignalingAddress& address);
   ~FakeSignalStrategy() override;
 
   const std::list<buzz::XmlElement*>& received_messages() {
@@ -43,7 +44,7 @@
   // Connects current FakeSignalStrategy to receive messages from |peer|.
   void ConnectTo(FakeSignalStrategy* peer);
 
-  void SetLocalJid(const std::string& jid);
+  void SetLocalAddress(const SignalingAddress& address);
 
   // Simulate IQ messages re-ordering by swapping the delivery order of
   // next pair of messages.
@@ -54,7 +55,7 @@
   void Disconnect() override;
   State GetState() const override;
   Error GetError() const override;
-  std::string GetLocalJid() const override;
+  const SignalingAddress& GetLocalAddress() const override;
   void AddListener(Listener* listener) override;
   void RemoveListener(Listener* listener) override;
   bool SendStanza(std::unique_ptr<buzz::XmlElement> stanza) override;
@@ -76,7 +77,7 @@
 
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
 
-  std::string jid_;
+  SignalingAddress address_;
   PeerCallback peer_callback_;
   base::ObserverList<Listener, true> listeners_;
 
diff --git a/remoting/signaling/iq_sender_unittest.cc b/remoting/signaling/iq_sender_unittest.cc
index 8ef1041..1b52305 100644
--- a/remoting/signaling/iq_sender_unittest.cc
+++ b/remoting/signaling/iq_sender_unittest.cc
@@ -47,7 +47,7 @@
 
 class IqSenderTest : public testing::Test {
  public:
-  IqSenderTest() {
+  IqSenderTest() : signal_strategy_(SignalingAddress("local_jid@domain.com")) {
     EXPECT_CALL(signal_strategy_, AddListener(NotNull()));
     sender_.reset(new IqSender(&signal_strategy_));
     EXPECT_CALL(signal_strategy_, RemoveListener(
diff --git a/remoting/signaling/log_to_server_unittest.cc b/remoting/signaling/log_to_server_unittest.cc
index 772a33e..856f69c 100644
--- a/remoting/signaling/log_to_server_unittest.cc
+++ b/remoting/signaling/log_to_server_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/run_loop.h"
 #include "remoting/signaling/mock_signal_strategy.h"
 #include "remoting/signaling/server_log_entry_unittest.h"
+#include "remoting/signaling/signaling_address.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -38,7 +39,7 @@
 
 class LogToServerTest : public testing::Test {
  public:
-  LogToServerTest() {}
+  LogToServerTest() : signal_strategy_(SignalingAddress(kClientJid)) {}
   void SetUp() override {
     EXPECT_CALL(signal_strategy_, AddListener(_));
     EXPECT_CALL(signal_strategy_, RemoveListener(_));
@@ -56,8 +57,6 @@
 TEST_F(LogToServerTest, LogWhenConnected) {
   {
     InSequence s;
-    EXPECT_CALL(signal_strategy_, GetLocalJid())
-        .WillRepeatedly(Return(kClientJid));
     EXPECT_CALL(signal_strategy_, AddListener(_));
     EXPECT_CALL(signal_strategy_, GetNextId());
     EXPECT_CALL(signal_strategy_, SendStanzaPtr(IsLogEntry("a", "1")))
@@ -80,8 +79,6 @@
 }
 
 TEST_F(LogToServerTest, DontLogWhenDisconnected) {
-  EXPECT_CALL(signal_strategy_, GetLocalJid())
-      .WillRepeatedly(Return(kClientJid));
   EXPECT_CALL(signal_strategy_, SendStanzaPtr(_)).Times(0);
 
   ServerLogEntry entry;
diff --git a/remoting/signaling/mock_signal_strategy.cc b/remoting/signaling/mock_signal_strategy.cc
index 7133587..2dd562b3 100644
--- a/remoting/signaling/mock_signal_strategy.cc
+++ b/remoting/signaling/mock_signal_strategy.cc
@@ -6,7 +6,12 @@
 
 namespace remoting {
 
-MockSignalStrategy::MockSignalStrategy() { }
-MockSignalStrategy::~MockSignalStrategy() { }
+MockSignalStrategy::MockSignalStrategy(const SignalingAddress& address)
+    : local_address_(address) {}
+MockSignalStrategy::~MockSignalStrategy() {}
+
+const SignalingAddress& MockSignalStrategy::GetLocalAddress() const {
+  return local_address_;
+}
 
 }  // namespace remoting
diff --git a/remoting/signaling/mock_signal_strategy.h b/remoting/signaling/mock_signal_strategy.h
index e4455f4..5fd40f8 100644
--- a/remoting/signaling/mock_signal_strategy.h
+++ b/remoting/signaling/mock_signal_strategy.h
@@ -6,6 +6,7 @@
 
 #include "remoting/signaling/iq_sender.h"
 #include "remoting/signaling/signal_strategy.h"
+#include "remoting/signaling/signaling_address.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
 
@@ -13,14 +14,13 @@
 
 class MockSignalStrategy : public SignalStrategy {
  public:
-  MockSignalStrategy();
+  MockSignalStrategy(const SignalingAddress& address);
   ~MockSignalStrategy() override;
 
   MOCK_METHOD0(Connect, void());
   MOCK_METHOD0(Disconnect, void());
   MOCK_CONST_METHOD0(GetState, State());
   MOCK_CONST_METHOD0(GetError, Error());
-  MOCK_CONST_METHOD0(GetLocalJid, std::string());
   MOCK_METHOD1(AddListener, void(Listener* listener));
   MOCK_METHOD1(RemoveListener, void(Listener* listener));
   MOCK_METHOD0(GetNextId, std::string());
@@ -31,6 +31,11 @@
   bool SendStanza(std::unique_ptr<buzz::XmlElement> stanza) override {
     return SendStanzaPtr(stanza.release());
   }
+
+  const SignalingAddress& GetLocalAddress() const override;
+
+ private:
+  SignalingAddress local_address_;
 };
 
 }  // namespace remoting
diff --git a/remoting/signaling/push_notification_subscriber.cc b/remoting/signaling/push_notification_subscriber.cc
index bbeb02b..734d1de 100644
--- a/remoting/signaling/push_notification_subscriber.cc
+++ b/remoting/signaling/push_notification_subscriber.cc
@@ -10,6 +10,7 @@
 #include "remoting/base/logging.h"
 #include "remoting/signaling/iq_sender.h"
 #include "remoting/signaling/jid_util.h"
+#include "remoting/signaling/signaling_address.h"
 #include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
 
 namespace remoting {
@@ -20,11 +21,8 @@
 
 }  // namespace
 
-PushNotificationSubscriber::Subscription::Subscription() {
-}
-
-PushNotificationSubscriber::Subscription::~Subscription() {
-}
+PushNotificationSubscriber::Subscription::Subscription() {}
+PushNotificationSubscriber::Subscription::~Subscription() {}
 
 PushNotificationSubscriber::PushNotificationSubscriber(
     SignalStrategy* signal_strategy,
@@ -58,7 +56,8 @@
           << subscription.channel << ".";
 
   std::string bare_jid;
-  SplitJidResource(signal_strategy_->GetLocalJid(), &bare_jid, nullptr);
+  SplitJidResource(signal_strategy_->GetLocalAddress().jid(), &bare_jid,
+                   nullptr);
 
   // Build a subscription request.
   buzz::XmlElement* subscribe_element =
diff --git a/remoting/signaling/push_notification_subscriber_unittest.cc b/remoting/signaling/push_notification_subscriber_unittest.cc
index 6536f9c..a7ef2ed 100644
--- a/remoting/signaling/push_notification_subscriber_unittest.cc
+++ b/remoting/signaling/push_notification_subscriber_unittest.cc
@@ -5,6 +5,7 @@
 #include "remoting/signaling/push_notification_subscriber.h"
 
 #include "remoting/signaling/mock_signal_strategy.h"
+#include "remoting/signaling/signaling_address.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 using testing::_;
@@ -16,7 +17,7 @@
 namespace remoting {
 
 TEST(PushNotificationSubscriberTest, Create) {
-  MockSignalStrategy signal_strategy;
+  MockSignalStrategy signal_strategy(SignalingAddress("user@domain/resource"));
   EXPECT_CALL(signal_strategy, AddListener(_));
   EXPECT_CALL(signal_strategy, RemoveListener(_));
 
@@ -25,9 +26,7 @@
 }
 
 TEST(PushNotificationSubscriberTest, Subscribe) {
-  MockSignalStrategy signal_strategy;
-  EXPECT_CALL(signal_strategy, GetLocalJid())
-      .WillRepeatedly(Return("user@domain/resource"));
+  MockSignalStrategy signal_strategy(SignalingAddress("user@domain/resource"));
   EXPECT_CALL(signal_strategy, GetNextId()).WillOnce(Return("next_id"));
   EXPECT_CALL(signal_strategy, AddListener(_)).Times(AtLeast(1));
   EXPECT_CALL(signal_strategy, RemoveListener(_)).Times(AtLeast(1));
diff --git a/remoting/signaling/signal_strategy.h b/remoting/signaling/signal_strategy.h
index 479f8b7..1edeeba 100644
--- a/remoting/signaling/signal_strategy.h
+++ b/remoting/signaling/signal_strategy.h
@@ -16,6 +16,8 @@
 
 namespace remoting {
 
+class SignalingAddress;
+
 class SignalStrategy {
  public:
   enum State {
@@ -75,8 +77,8 @@
   // Returns the last error. Set when state changes to DISCONNECT.
   virtual Error GetError() const = 0;
 
-  // Returns local JID or an empty string when not connected.
-  virtual std::string GetLocalJid() const = 0;
+  // Local address. An empty value is returned when not connected.
+  virtual const SignalingAddress& GetLocalAddress() const = 0;
 
   // Add a |listener| that can listen to all incoming
   // messages. Doesn't take ownership of the |listener|. All listeners
diff --git a/remoting/signaling/xmpp_signal_strategy.cc b/remoting/signaling/xmpp_signal_strategy.cc
index 32ab100..a493ab1 100644
--- a/remoting/signaling/xmpp_signal_strategy.cc
+++ b/remoting/signaling/xmpp_signal_strategy.cc
@@ -30,6 +30,7 @@
 #include "net/url_request/url_request_context_getter.h"
 #include "remoting/base/buffered_socket_writer.h"
 #include "remoting/base/logging.h"
+#include "remoting/signaling/signaling_address.h"
 #include "remoting/signaling/xmpp_login_handler.h"
 #include "remoting/signaling/xmpp_stream_parser.h"
 #include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
@@ -67,7 +68,7 @@
   void Disconnect();
   State GetState() const;
   Error GetError() const;
-  std::string GetLocalJid() const;
+  const SignalingAddress& GetLocalAddress() const;
   void AddListener(Listener* listener);
   void RemoveListener(Listener* listener);
   bool SendStanza(std::unique_ptr<buzz::XmlElement> stanza);
@@ -135,7 +136,7 @@
 
   std::unique_ptr<XmppLoginHandler> login_handler_;
   std::unique_ptr<XmppStreamParser> stream_parser_;
-  std::string jid_;
+  SignalingAddress local_address_;
 
   Error error_ = OK;
 
@@ -226,9 +227,9 @@
   return error_;
 }
 
-std::string XmppSignalStrategy::Core::GetLocalJid() const {
+const SignalingAddress& XmppSignalStrategy::Core::GetLocalAddress() const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  return jid_;
+  return local_address_;
 }
 
 void XmppSignalStrategy::Core::AddListener(Listener* listener) {
@@ -327,7 +328,7 @@
     std::unique_ptr<XmppStreamParser> parser) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  jid_ = jid;
+  local_address_ = SignalingAddress(jid);
   stream_parser_ = std::move(parser);
   stream_parser_->SetCallbacks(
       base::Bind(&Core::OnStanza, base::Unretained(this)),
@@ -532,8 +533,8 @@
   return core_->GetError();
 }
 
-std::string XmppSignalStrategy::GetLocalJid() const {
-  return core_->GetLocalJid();
+const SignalingAddress& XmppSignalStrategy::GetLocalAddress() const {
+  return core_->GetLocalAddress();
 }
 
 void XmppSignalStrategy::AddListener(Listener* listener) {
diff --git a/remoting/signaling/xmpp_signal_strategy.h b/remoting/signaling/xmpp_signal_strategy.h
index c6252be8..fb259fda 100644
--- a/remoting/signaling/xmpp_signal_strategy.h
+++ b/remoting/signaling/xmpp_signal_strategy.h
@@ -50,7 +50,7 @@
   void Disconnect() override;
   State GetState() const override;
   Error GetError() const override;
-  std::string GetLocalJid() const override;
+  const SignalingAddress& GetLocalAddress() const override;
   void AddListener(Listener* listener) override;
   void RemoveListener(Listener* listener) override;
   bool SendStanza(std::unique_ptr<buzz::XmlElement> stanza) override;
diff --git a/remoting/test/protocol_perftest.cc b/remoting/test/protocol_perftest.cc
index 2645690..202b4e3 100644
--- a/remoting/test/protocol_perftest.cc
+++ b/remoting/test/protocol_perftest.cc
@@ -242,7 +242,8 @@
   void StartHostAndClient(bool use_webrtc) {
     fake_network_dispatcher_ =  new FakeNetworkDispatcher();
 
-    client_signaling_.reset(new FakeSignalStrategy(kClientJid));
+    client_signaling_.reset(
+        new FakeSignalStrategy(SignalingAddress(kClientJid)));
 
     jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
 
@@ -261,7 +262,7 @@
 
     jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
 
-    host_signaling_.reset(new FakeSignalStrategy(kHostJid));
+    host_signaling_.reset(new FakeSignalStrategy(SignalingAddress(kHostJid)));
     host_signaling_->set_send_delay(GetParam().signaling_latency);
     host_signaling_->ConnectTo(client_signaling_.get());
 
diff --git a/remoting/test/test_chromoting_client_unittest.cc b/remoting/test/test_chromoting_client_unittest.cc
index 29a242ad..17b35fee 100644
--- a/remoting/test/test_chromoting_client_unittest.cc
+++ b/remoting/test/test_chromoting_client_unittest.cc
@@ -70,7 +70,8 @@
   // remain valid until |test_chromoting_client_| is destroyed.
   fake_connection_to_host_ = new FakeConnectionToHost();
   test_chromoting_client_->SetSignalStrategyForTests(
-      base::MakeUnique<FakeSignalStrategy>("test_user@faux_address.com/123"));
+      base::MakeUnique<FakeSignalStrategy>(
+          SignalingAddress("test_user@faux_address.com/123")));
   test_chromoting_client_->SetConnectionToHostForTests(
       base::WrapUnique(fake_connection_to_host_));
 
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 820fe5b9..66fc0eb 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -216,6 +216,10 @@
 #   define SK_USE_LEGACY_DISTANCE_FIELDS
 #endif
 
+#ifndef SK_SUPPORT_LEGACY_NO_ADDR_PIXELREF
+#define SK_SUPPORT_LEGACY_NO_ADDR_PIXELREF
+#endif
+
 #ifndef SK_DISABLE_DEFERRED_PROXIES
 #define SK_DISABLE_DEFERRED_PROXIES
 #endif
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index 7c54b68..bb2115ca 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -44,3 +44,11 @@
     "//testing/buildbot/filters/site-per-process.interactive_ui_tests.filter",
   ]
 }
+
+source_set("ash_unittests_filters") {
+  testonly = true
+
+  data = [
+    "//testing/buildbot/filters/ash_mus_unittests.filter",
+  ]
+}
diff --git a/third_party/.gitignore b/third_party/.gitignore
index 176819f..c79ea463 100644
--- a/third_party/.gitignore
+++ b/third_party/.gitignore
@@ -49,7 +49,7 @@
 /drmemory/unpacked
 /elfutils/src
 /errorprone/lib
-/espresso/lib/*.jar
+/espresso/lib/*-2.2.1-*.jar
 /eyesfree/src
 /ffmpeg
 /findbugs
diff --git a/third_party/WebKit/LayoutTests/NeverFixTests b/third_party/WebKit/LayoutTests/NeverFixTests
index eaa095a..58e3340 100644
--- a/third_party/WebKit/LayoutTests/NeverFixTests
+++ b/third_party/WebKit/LayoutTests/NeverFixTests
@@ -39,6 +39,7 @@
 
 # This test only applies to overlay scrollbar platforms.
 [ Win Linux ] fast/dom/partial-layout-overlay-scrollbars.html [ WontFix ]
+[ Win Linux ] virtual/sharedarraybuffer/fast/dom/partial-layout-overlay-scrollbars.html [ WontFix ]
 
 # Mac's popup behavior is different.
 [ Mac ] fast/forms/select/menulist-onchange-fired-with-key-up-down.html [ WontFix ]
@@ -101,6 +102,12 @@
 [ Android ] virtual/mojo-loading/http/tests/media/media-source/mediasource-config-change-mp4-v-bitrate.html [ WontFix ]
 [ Android ] virtual/mojo-loading/http/tests/media/media-source/mediasource-config-change-mp4-v-framerate.html [ WontFix ]
 [ Android ] virtual/mojo-loading/http/tests/media/media-source/mediasource-config-change-mp4-v-framesize.html [ WontFix ]
+[ Android ] virtual/sharedarraybuffer/http/tests/media/media-source/mediasource-config-change-mp4-av-audio-bitrate.html [ WontFix ]
+[ Android ] virtual/sharedarraybuffer/http/tests/media/media-source/mediasource-config-change-mp4-av-framesize.html [ WontFix ]
+[ Android ] virtual/sharedarraybuffer/http/tests/media/media-source/mediasource-config-change-mp4-av-video-bitrate.html [ WontFix ]
+[ Android ] virtual/sharedarraybuffer/http/tests/media/media-source/mediasource-config-change-mp4-v-bitrate.html [ WontFix ]
+[ Android ] virtual/sharedarraybuffer/http/tests/media/media-source/mediasource-config-change-mp4-v-framerate.html [ WontFix ]
+[ Android ] virtual/sharedarraybuffer/http/tests/media/media-source/mediasource-config-change-mp4-v-framesize.html [ WontFix ]
 
 # Only run fake-Android tests on Linux
 [ Mac Win ] virtual/android [ WontFix ]
@@ -215,6 +222,8 @@
 # these tests can still be run manually with --skiped=ignore.
 webaudio/codec-tests/mp3 [ WontFix ]
 webaudio/codec-tests/aac [ WontFix ]
+virtual/sharedarraybuffer/webaudio/codec-tests/mp3 [ WontFix ]
+virtual/sharedarraybuffer/webaudio/codec-tests/aac [ WontFix ]
 
 # WPT subdirectories without owners.
 external/wpt/accelerometer [ WontFix ]
diff --git a/third_party/WebKit/LayoutTests/SlowTests b/third_party/WebKit/LayoutTests/SlowTests
index 4abba26..32b60bb 100644
--- a/third_party/WebKit/LayoutTests/SlowTests
+++ b/third_party/WebKit/LayoutTests/SlowTests
@@ -24,6 +24,7 @@
 crbug.com/24182 editing/selection/modify_move/move-by-word-visually-multi-space.html [ Slow ]
 crbug.com/24182 editing/selection/modify_move/move-by-word-visually-crash-test-5.html [ Slow ]
 crbug.com/24182 fast/dom/SelectorAPI/resig-SelectorsAPI-test.xhtml [ Slow ]
+crbug.com/24182 virtual/sharedarraybuffer/fast/dom/SelectorAPI/resig-SelectorsAPI-test.xhtml [ Slow ]
 crbug.com/24182 fast/frames/cached-frame-counter.html [ Slow ]
 crbug.com/24182 fast/frames/frame-limit.html [ Slow ]
 crbug.com/24182 fast/overflow/lots-of-sibling-inline-boxes.html [ Slow ] # Particularly slow in Debug: >12x slower!
@@ -35,6 +36,7 @@
 crbug.com/24182 virtual/mojo-loading/http/tests/cache/subresource-expiration-2.html [ Slow ]
 crbug.com/24182 [ Release Win7 ] http/tests/media/media-source/mediasource-addsourcebuffer.html [ Slow ]
 crbug.com/24182 [ Release Win7 ] virtual/mojo-loading/http/tests/media/media-source/mediasource-addsourcebuffer.html [ Slow ]
+crbug.com/24182 [ Release Win7 ] virtual/sharedarraybuffer/http/tests/media/media-source/mediasource-addsourcebuffer.html [ Slow ]
 crbug.com/24182 http/tests/misc/acid3.html [ Slow ]
 crbug.com/24182 virtual/mojo-loading/http/tests/misc/acid3.html [ Slow ]
 crbug.com/24182 http/tests/misc/object-embedding-svg-delayed-size-negotiation-2.htm [ Slow ]
@@ -81,10 +83,13 @@
 crbug.com/24182 virtual/gpu/fast/canvas/canvas-strokeRect-gradient-shadow.html [ Slow ]
 crbug.com/24182 virtual/gpu/fast/canvas/canvas-toDataURL-jpeg-crash.html [ Slow ]
 crbug.com/24182 fast/dom/timer-throttling-background-page-near-alignment-interval.html [ Slow ]
+crbug.com/24182 virtual/sharedarraybuffer/fast/dom/timer-throttling-background-page-near-alignment-interval.html [ Slow ]
 crbug.com/24182 http/tests/perf/large-inlined-script.html [ Slow ]
 crbug.com/24182 virtual/mojo-loading/http/tests/perf/large-inlined-script.html [ Slow ]
 crbug.com/24182 fast/css/should-not-insert-stylesheet-into-detached-document.html [ Slow ]
+crbug.com/24182 virtual/sharedarraybuffer/fast/css/should-not-insert-stylesheet-into-detached-document.html [ Slow ]
 crbug.com/24182 fast/dom/shadow/svg-style-in-shadow-tree-crash.html [ Slow ]
+crbug.com/24182 virtual/sharedarraybuffer/fast/dom/shadow/svg-style-in-shadow-tree-crash.html [ Slow ]
 crbug.com/24182 fast/encoding/char-encoding.html [ Slow ]
 crbug.com/24182 fast/frames/sandboxed-iframe-navigation-targetlink.html [ Slow ]
 crbug.com/24182 html/marquee/marquee-destroyed-without-removed-from-crash.html [ Slow ]
@@ -95,6 +100,7 @@
 crbug.com/24182 svg/hixie/perf/005.xml [ Slow ]
 crbug.com/24182 svg/hixie/perf/006.xml [ Slow ]
 crbug.com/451577 [ Debug ] fast/dom/gc-treescope.html  [ Slow ]
+crbug.com/451577 [ Debug ] virtual/sharedarraybuffer/fast/dom/gc-treescope.html  [ Slow ]
 crbug.com/451577 [ Debug ] fast/frames/calculate-round.html [ Slow ]
 crbug.com/451577 external/wpt/IndexedDB/idbindex-multientry-big.htm [ Slow ]
 crbug.com/451577 [ Mac ] inspector/extensions/extensions-reload.html [ Slow ]
@@ -220,6 +226,7 @@
 crbug.com/24182 http/tests/xmlhttprequest/simple-cross-origin-progress-events.html [ Slow ]
 crbug.com/24182 virtual/mojo-loading/http/tests/xmlhttprequest/simple-cross-origin-progress-events.html [ Slow ]
 crbug.com/237270 [ Win7 ] fast/css/custom-font-xheight.html [ Slow ]
+crbug.com/237270 [ Win7 ] virtual/sharedarraybuffer/fast/css/custom-font-xheight.html [ Slow ]
 crbug.com/241576 [ Win ] http/tests/appcache/404-manifest.html [ Slow ]
 crbug.com/241576 [ Win ] virtual/mojo-loading/http/tests/appcache/404-manifest.html [ Slow ]
 crbug.com/241869 [ Debug ] css3/flexbox/multiline-justify-content.html [ Slow ]
@@ -229,6 +236,7 @@
 # This test takes 5+ seconds as intended because it tests connection throttling.
 crbug.com/459377 http/tests/websocket/multiple-connections-throttled.html [ Slow ]
 crbug.com/459377 virtual/mojo-loading/http/tests/websocket/multiple-connections-throttled.html [ Slow ]
+crbug.com/459377 virtual/sharedarraybuffer/http/tests/websocket/multiple-connections-throttled.html [ Slow ]
 
 # These tests are slow to measure bounding box of every single Unicode character.
 #crbug.com/492664 external/csswg-test/css-writing-modes-3/text-orientation-script-001a.html [ Slow ]
@@ -250,6 +258,7 @@
 crbug.com/336481 inspector/jump-to-previous-editing-location.html [ Slow ]
 crbug.com/346259 http/tests/websocket/no-crash-on-cookie-flood.html [ Slow ]
 crbug.com/346259 virtual/mojo-loading/http/tests/websocket/no-crash-on-cookie-flood.html [ Slow ]
+crbug.com/346259 virtual/sharedarraybuffer/http/tests/websocket/no-crash-on-cookie-flood.html [ Slow ]
 
 crbug.com/522646 http/tests/media/encrypted-media/encrypted-media-encrypted-event-different-origin.html [ Slow ]
 crbug.com/522646 virtual/mojo-loading/http/tests/media/encrypted-media/encrypted-media-encrypted-event-different-origin.html [ Slow ]
@@ -277,6 +286,7 @@
 crbug.com/548765 virtual/mojo-loading/http/tests/inspector/console-fetch-logging.html [ Slow ]
 
 crbug.com/419993 [ Debug ] fast/css/giant-stylesheet-crash.html [ Slow ]
+crbug.com/419993 [ Debug ] virtual/sharedarraybuffer/fast/css/giant-stylesheet-crash.html [ Slow ]
 
 crbug.com/331186 [ Debug ] css3/flexbox/position-absolute-child.html [ Slow ]
 crbug.com/362509 [ Debug ] fast/parser/xml-error-adopted.xml [ Slow ]
@@ -285,6 +295,7 @@
 crbug.com/364225 virtual/gpu/fast/canvas/canvas-composite-text-alpha.html [ Slow ]
 
 crbug.com/372424 fast/dom/DOMImplementation/createDocument-with-used-doctype.html [ Slow ]
+crbug.com/372424 virtual/sharedarraybuffer/fast/dom/DOMImplementation/createDocument-with-used-doctype.html [ Slow ]
 
 crbug.com/372424 http/tests/serviceworker/chromium/registration-stress.html [ Slow ]
 crbug.com/372424 virtual/mojo-loading/http/tests/serviceworker/chromium/registration-stress.html [ Slow ]
@@ -295,6 +306,7 @@
 
 # Most crypto/subtle tests are slow some or most of the time.
 crbug.com/459009 crypto/subtle/ [ Slow ]
+crbug.com/459009 virtual/sharedarraybuffer/crypto/subtle/ [ Slow ]
 
 crbug.com/24182 [ Debug ] animations/interpolation/transform-interpolation.html [ Slow ]
 crbug.com/24182 [ Debug ] animations/interpolation/webkit-transform-interpolation.html [ Slow ]
@@ -312,6 +324,7 @@
 crbug.com/658211 [ Win7 Debug ] fast/text/line-break-ascii.html [ Slow ]
 
 crbug.com/445194 fast/dom/shadow/focus-controller-recursion-crash.html [ Slow ]
+crbug.com/445194 virtual/sharedarraybuffer/fast/dom/shadow/focus-controller-recursion-crash.html [ Slow ]
 crbug.com/697735 external/wpt/editing/run/backcolor.html [ Slow ]
 crbug.com/697735 external/wpt/editing/run/bold.html [ Slow ]
 crbug.com/697735 external/wpt/editing/run/createlink.html [ Slow ]
@@ -359,6 +372,7 @@
 crbug.com/623798 virtual/disable-spinvalidation/paint/images/animated-gif-last-frame-crash.html [ Slow ]
 
 crbug.com/606649 fast/dom/gc-dom-tree-lifetime.html [ Slow ]
+crbug.com/606649 virtual/sharedarraybuffer/fast/dom/gc-dom-tree-lifetime.html [ Slow ]
 
 # Slow on Win Debug in part due to incremental linking.
 crbug.com/647192 [ Win Debug ] fast/css3-text/css3-word-break/word-break-all-ascii.html [ Slow ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 76e9613..850be5a 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -737,6 +737,7 @@
 
 crbug.com/520739 [ Mac ] http/tests/websocket/close-code-and-reason.html [ Failure Pass Timeout ]
 crbug.com/520739 [ Mac ] virtual/mojo-loading/http/tests/websocket/close-code-and-reason.html [ Failure Pass Timeout ]
+crbug.com/520739 [ Mac ] virtual/sharedarraybuffer/http/tests/websocket/close-code-and-reason.html [ Failure Pass Timeout ]
 #crbug.com/520737 [ Mac ] external/wpt/css/css-writing-modes-3/writing-mode-vertical-rl-001.xht [ Failure Pass Timeout ]
 crbug.com/520736 [ Win7 ] media/W3C/video/networkState/networkState_during_progress.html [ Failure Pass ]
 
@@ -812,10 +813,12 @@
 crbug.com/389648 crbug.com/517123 crbug.com/410145 fast/text-autosizing/table-inflation-crash.html [ Pass Crash Timeout ]
 
 crbug.com/636207 [ Win Debug ] fast/dom/HTMLImageElement/image-srcset-w-onerror.html [ Failure Pass ]
+crbug.com/636207 [ Win Debug ] virtual/sharedarraybuffer/fast/dom/HTMLImageElement/image-srcset-w-onerror.html [ Failure Pass ]
 
 crbug.com/569139 fast/js/string-replace-2.html [ Failure ]
 crbug.com/569139 fast/js/regexp-caching.html [ Failure ]
 crbug.com/597221 fast/dom/Window/window-postmessage-clone-deep-array.html [ Failure ]
+crbug.com/597221 virtual/sharedarraybuffer/fast/dom/Window/window-postmessage-clone-deep-array.html [ Failure ]
 crbug.com/498539 [ Win ] inspector/tracing/decode-resize.html [ Failure Timeout ]
 crbug.com/498539 inspector/tracing/timeline-misc/timeline-bound-function.html [ Pass Failure ]
 crbug.com/498539 virtual/threaded/inspector/tracing/timeline-misc/timeline-bound-function.html [ Pass Failure ]
@@ -870,6 +873,7 @@
 crbug.com/688613 external/wpt/mediacapture-streams/MediaStreamTrack-MediaElement-disabled-audio-is-silence.https.html [ Skip ]
 
 crbug.com/542660 fast/css/absolute-inline-alignment-2.html [ Failure ]
+crbug.com/542660 virtual/sharedarraybuffer/fast/css/absolute-inline-alignment-2.html [ Failure ]
 
 # Ref tests that needs investigation.
 crbug.com/404597 [ Mac ] fast/css3-text/css3-text-justify/text-justify-crash.html [ Failure ]
@@ -1087,6 +1091,7 @@
 #crbug.com/571531 external/wpt/css/css-flexbox-1/css-flexbox-height-animation-stretch.html [ Pass Failure ]
 
 crbug.com/637055 fast/css/outline-offset-large.html [ Skip ]
+crbug.com/637055 virtual/sharedarraybuffer/fast/css/outline-offset-large.html [ Skip ]
 
 # Either "combo" or split should run: http://testthewebforward.org/docs/css-naming.html
 #crbug.com/410320 external/wpt/css/css-writing-modes-3/orthogonal-parent-shrink-to-fit-001.html [ Skip ]
@@ -1174,6 +1179,7 @@
 
 crbug.com/381684 [ Mac Win ] fonts/family-fallback-gardiner.html [ Skip ]
 crbug.com/467635 fast/dom/HTMLImageElement/image-sizes-meta-viewport.html [ Skip ]
+crbug.com/467635 virtual/sharedarraybuffer/fast/dom/HTMLImageElement/image-sizes-meta-viewport.html [ Skip ]
 
 
 crbug.com/636239 [ Win7 ] media/video-zoom-controls.html [ Failure ]
@@ -1217,12 +1223,14 @@
 
 crbug.com/524160 [ Debug ] http/tests/media/media-source/stream_memory_tests/mediasource-appendbuffer-quota-exceeded-default-buffers.html [ Timeout ]
 crbug.com/524160 [ Debug ] virtual/mojo-loading/http/tests/media/media-source/stream_memory_tests/mediasource-appendbuffer-quota-exceeded-default-buffers.html [ Timeout ]
+crbug.com/524160 [ Debug ] virtual/sharedarraybuffer/http/tests/media/media-source/stream_memory_tests/mediasource-appendbuffer-quota-exceeded-default-buffers.html [ Timeout ]
 
 # Run the tests with the default MSE buffer sizes in the main test suite and the tests for 1MB buffers set via command line in a virtual test suite
 # with the --mse-audio-buffer-size-limit=1048576 and --mse-video-buffer-size-limit=1048576 command-line parameters.
 crbug.com/630342 virtual/mse-1mb-buffers/http/tests/media/media-source/stream_memory_tests/mediasource-appendbuffer-quota-exceeded-default-buffers.html [ Skip ]
 crbug.com/630342 http/tests/media/media-source/stream_memory_tests/mediasource-appendbuffer-quota-exceeded-1mb-buffers.html [ Skip ]
 crbug.com/630342 virtual/mojo-loading/http/tests/media/media-source/stream_memory_tests/mediasource-appendbuffer-quota-exceeded-1mb-buffers.html [ Skip ]
+crbug.com/630342 virtual/sharedarraybuffer/http/tests/media/media-source/stream_memory_tests/mediasource-appendbuffer-quota-exceeded-1mb-buffers.html [ Skip ]
 
 # On these platforms (all but Android) media tests don't currently use gpu-accelerated (proprietary) codecs, so no
 # benefit to running them again with gpu acceleration enabled.
@@ -1486,6 +1494,7 @@
 
 # These need a rebaseline due crbug.com/504745 on Windows when they are activated again.
 crbug.com/521124 crbug.com/410145 [ Win7 ] fast/css/font-weight-1.html [ Pass Failure ]
+crbug.com/521124 crbug.com/410145 [ Win7 ] virtual/sharedarraybuffer/fast/css/font-weight-1.html [ Pass Failure ]
 
 # Temporary, until we stop use_system_harfbuzz on Linux including non-official builds
 crbug.com/462689 [ Linux ] fast/text/unicode-variation-selector.html [ Failure ]
@@ -1514,6 +1523,7 @@
 # Ref tests that fail due to differences in inline box structure, even though they contain the same text.
 # This happens because inline box layout uses fixed-point measurements, which can cause rounding differences.
 crbug.com/321237 [ Mac ] fast/dom/shadow/shadow-insertion-point-rendering-multiple-shadow-roots.html [ Failure ]
+crbug.com/321237 [ Mac ] virtual/sharedarraybuffer/fast/dom/shadow/shadow-insertion-point-rendering-multiple-shadow-roots.html [ Failure ]
 crbug.com/321237 [ Mac ] fast/selectors/007a.html [ Failure ]
 crbug.com/321237 [ Mac Win ] fast/text-autosizing/inherited-multiplier.html [ Failure ]
 crbug.com/321237 [ Mac ] virtual/stable/fast/css3-text/css3-text-decoration/stable/first-letter-text-decoration.html [ Failure ]
@@ -1525,6 +1535,7 @@
 crbug.com/501659 http/tests/security/xss-DENIED-xml-external-entity.xhtml [ Failure ]
 crbug.com/501659 virtual/mojo-loading/http/tests/security/xss-DENIED-xml-external-entity.xhtml [ Failure ]
 crbug.com/501659 fast/css/stylesheet-candidate-nodes-crash.xhtml [ Failure ]
+crbug.com/501659 virtual/sharedarraybuffer/fast/css/stylesheet-candidate-nodes-crash.xhtml [ Failure ]
 
 crbug.com/686470 http/tests/security/xss-DENIED-window-name-navigator.html [ Pass Failure ]
 crbug.com/686470 virtual/mojo-loading/http/tests/security/xss-DENIED-window-name-navigator.html [ Pass Failure ]
@@ -1534,6 +1545,7 @@
 # If you see wider INPUT elements or narrower TEXTAREA elements, you may do just
 # rebaseline. See crbug.com/508768#c6
 crbug.com/509025 [ Mac10.10 ] fast/css/css2-system-fonts.html [ Failure ]
+crbug.com/509025 [ Mac10.10 ] virtual/sharedarraybuffer/fast/css/css2-system-fonts.html [ Failure ]
 crbug.com/509025 [ Mac10.10 ] fast/forms/select/hidden-listbox.html [ Failure ]
 crbug.com/509025 [ Mac10.10 ] fast/forms/textarea/textarea-newline.html [ Failure ]
 
@@ -1605,6 +1617,7 @@
 crbug.com/459056 [ Win7 ] fast/text/font-fallback-win.html [ Failure ]
 
 crbug.com/525296 fast/css/font-load-while-styleresolver-missing.html [ Crash Failure Pass ]
+crbug.com/525296 virtual/sharedarraybuffer/fast/css/font-load-while-styleresolver-missing.html [ Crash Failure Pass ]
 
 crbug.com/240576 external/wpt/fullscreen/api/element-ready-check-containing-iframe-manual.html [ Timeout Failure Pass ]
 
@@ -1618,10 +1631,12 @@
 
 crbug.com/457273 [ Mac ] http/tests/websocket/close.html [ Pass Timeout ]
 crbug.com/457273 [ Mac ] virtual/mojo-loading/http/tests/websocket/close.html [ Pass Timeout ]
+crbug.com/457273 [ Mac ] virtual/sharedarraybuffer/http/tests/websocket/close.html [ Pass Timeout ]
 
 crbug.com/596752 virtual/threaded/inspector/tracing/decode-resize.html [ Pass Failure ]
 
 crbug.com/524646 [ Mac10.10 ] fast/dom/shadow/shadowdom-for-button.html [ Failure ]
+crbug.com/524646 [ Mac10.10 ] virtual/sharedarraybuffer/fast/dom/shadow/shadowdom-for-button.html [ Failure ]
 
 crbug.com/538717 [ Win Mac Linux ] http/tests/permissions/chromium/test-request-multiple-window.html [ Failure Pass Timeout ]
 crbug.com/538717 [ Win Mac Linux ] http/tests/permissions/chromium/test-request-multiple-worker.html [ Failure Pass Timeout ]
@@ -1732,6 +1747,7 @@
 
 # Note: this test was previously marked as slow on Debug builds. Skipping until crash is fixed
 crbug.com/619978 fast/css/giant-stylesheet-crash.html [ Skip ]
+crbug.com/619978 virtual/sharedarraybuffer/fast/css/giant-stylesheet-crash.html [ Skip ]
 
 crbug.com/624430 [ Win10 ] fast/text/font-features/caps-casemapping.html [ Failure ]
 
@@ -2536,6 +2552,7 @@
 crbug.com/642376 virtual/gpu/fast/canvas/canvas-incremental-repaint.html [ NeedsManualRebaseline ]
 crbug.com/706375 virtual/gpu/fast/canvas/canvas-lost-gpu-context.html [ Failure ]
 crbug.com/706375 virtual/display_list_2d_canvas/fast/canvas/canvas-lost-gpu-context.html [ Failure ]
+crbug.com/706375 virtual/sharedarraybuffer/fast/canvas/canvas-lost-gpu-context.html [ Failure ]
 
 # Added 2016-12-12
 crbug.com/610835 http/tests/security/XFrameOptions/x-frame-options-deny-multiple-clients.html [ Failure Pass ]
@@ -2549,6 +2566,7 @@
 
 # Added 2016-12-22
 crbug.com/676537 fast/css/imageTileOpacity.html [ Pass Failure ]
+crbug.com/676537 virtual/sharedarraybuffer/fast/css/imageTileOpacity.html [ Pass Failure ]
 
 # ====== Random order flaky tests from here ======
 # These tests are flaky when run in random order, which is the default on Linux & Mac since since 2016-12-16.
@@ -2615,6 +2633,7 @@
 crbug.com/676229 [ Linux ] plugins/mouse-click-plugin-clears-selection.html [ Failure Pass ]
 
 crbug.com/678346 [ Win7 Debug ] fast/dom/shadow/selections-in-shadow.html [ Pass Timeout ]
+crbug.com/678346 [ Win7 Debug ] virtual/sharedarraybuffer/fast/dom/shadow/selections-in-shadow.html [ Pass Timeout ]
 crbug.com/678346 [ Win7 Debug ] storage/indexeddb/index-cursor.html [ Pass Timeout ]
 crbug.com/678346 [ Win7 Debug ] storage/indexeddb/mozilla/test_objectStore_openKeyCursor.html [ Pass Timeout ]
 crbug.com/678346 [ Win7 Debug ] storage/indexeddb/structured-clone.html [ Pass Timeout ]
@@ -2633,6 +2652,7 @@
 
 crbug.com/689781 http/tests/media/media-source/mediasource-duration.html [ Failure Pass ]
 crbug.com/689781 virtual/mojo-loading/http/tests/media/media-source/mediasource-duration.html [ Failure Pass ]
+crbug.com/689781 virtual/sharedarraybuffer/http/tests/media/media-source/mediasource-duration.html [ Failure Pass ]
 
 crbug.com/701445 external/wpt/dom/events/EventListener-invoke-legacy.html [ Timeout Pass ]
 
@@ -2682,6 +2702,7 @@
 
 crbug.com/697342 http/tests/push_messaging/permission-state-granted-in-document.html [ Pass Failure ]
 crbug.com/697342 virtual/mojo-loading/http/tests/push_messaging/permission-state-granted-in-document.html [ Pass Failure ]
+crbug.com/697342 virtual/sharedarraybuffer/http/tests/push_messaging/permission-state-granted-in-document.html [ Pass Failure ]
 
 crbug.com/698520 external/wpt/preload/fetch-destination.https.html [ Failure Pass ]
 
diff --git a/third_party/WebKit/LayoutTests/VirtualTestSuites b/third_party/WebKit/LayoutTests/VirtualTestSuites
index c782e8c..c6c6d22 100644
--- a/third_party/WebKit/LayoutTests/VirtualTestSuites
+++ b/third_party/WebKit/LayoutTests/VirtualTestSuites
@@ -216,11 +216,101 @@
   },
   {
     "prefix": "sharedarraybuffer",
+    "base": "fast/beacon",
+    "args": ["--js-flags=--harmony-sharedarraybuffer",
+             "--enable-blink-features=SharedArrayBuffer"]
+  },
+  {
+    "prefix": "sharedarraybuffer",
+    "base": "fast/canvas",
+    "args": ["--js-flags=--harmony-sharedarraybuffer",
+             "--enable-blink-features=SharedArrayBuffer"]
+  },
+  {
+    "prefix": "sharedarraybuffer",
+    "base": "fast/css",
+    "args": ["--js-flags=--harmony-sharedarraybuffer",
+             "--enable-blink-features=SharedArrayBuffer"]
+  },
+  {
+    "prefix": "sharedarraybuffer",
+    "base": "fast/dom",
+    "args": ["--js-flags=--harmony-sharedarraybuffer",
+             "--enable-blink-features=SharedArrayBuffer"]
+  },
+  {
+    "prefix": "sharedarraybuffer",
+    "base": "fast/encoding/api",
+    "args": ["--js-flags=--harmony-sharedarraybuffer",
+             "--enable-blink-features=SharedArrayBuffer"]
+  },
+  {
+    "prefix": "sharedarraybuffer",
+    "base": "fast/events/constructors",
+    "args": ["--js-flags=--harmony-sharedarraybuffer",
+             "--enable-blink-features=SharedArrayBuffer"]
+  },
+  {
+    "prefix": "sharedarraybuffer",
+    "base": "fast/files",
+    "args": ["--js-flags=--harmony-sharedarraybuffer",
+             "--enable-blink-features=SharedArrayBuffer"]
+  },
+  {
+    "prefix": "sharedarraybuffer",
+    "base": "fast/peerconnection",
+    "args": ["--js-flags=--harmony-sharedarraybuffer",
+             "--enable-blink-features=SharedArrayBuffer"]
+  },
+  {
+    "prefix": "sharedarraybuffer",
     "base": "fast/workers",
     "args": ["--js-flags=--harmony-sharedarraybuffer",
              "--enable-blink-features=SharedArrayBuffer"]
   },
   {
+    "prefix": "sharedarraybuffer",
+    "base": "crypto",
+    "args": ["--js-flags=--harmony-sharedarraybuffer",
+             "--enable-blink-features=SharedArrayBuffer"]
+  },
+  {
+    "prefix": "sharedarraybuffer",
+    "base": "http/tests/media/media-source",
+    "args": ["--js-flags=--harmony-sharedarraybuffer",
+             "--enable-blink-features=SharedArrayBuffer"]
+  },
+  {
+    "prefix": "sharedarraybuffer",
+    "base": "http/tests/push_messaging",
+    "args": ["--js-flags=--harmony-sharedarraybuffer",
+             "--enable-blink-features=SharedArrayBuffer"]
+  },
+  {
+    "prefix": "sharedarraybuffer",
+    "base": "http/tests/websocket",
+    "args": ["--js-flags=--harmony-sharedarraybuffer",
+             "--enable-blink-features=SharedArrayBuffer"]
+  },
+  {
+    "prefix": "sharedarraybuffer",
+    "base": "sensor",
+    "args": ["--js-flags=--harmony-sharedarraybuffer",
+             "--enable-blink-features=SharedArrayBuffer"]
+  },
+  {
+    "prefix": "sharedarraybuffer",
+    "base": "webaudio",
+    "args": ["--js-flags=--harmony-sharedarraybuffer",
+             "--enable-blink-features=SharedArrayBuffer"]
+  },
+  {
+    "prefix": "sharedarraybuffer",
+    "base": "webmidi",
+    "args": ["--js-flags=--harmony-sharedarraybuffer",
+             "--enable-blink-features=SharedArrayBuffer"]
+  },
+  {
     "prefix": "threaded",
     "base": "fast/idle-callback",
     "args": ["--enable-threaded-compositing"]
diff --git a/third_party/WebKit/LayoutTests/crypto/random-values.js b/third_party/WebKit/LayoutTests/crypto/random-values.js
index 0fb26bd..f87e1ca 100644
--- a/third_party/WebKit/LayoutTests/crypto/random-values.js
+++ b/third_party/WebKit/LayoutTests/crypto/random-values.js
@@ -37,4 +37,8 @@
     debug(ex);
 }
 
+if (self.SharedArrayBuffer) {
+    shouldThrow("crypto.getRandomValues(new Uint8Array(new SharedArrayBuffer(100)))");
+}
+
 finishJSTest();
diff --git a/third_party/WebKit/LayoutTests/crypto/subtle/importKey-badParameters-expected.txt b/third_party/WebKit/LayoutTests/crypto/subtle/importKey-badParameters-expected.txt
index de9526d..73949bf 100644
--- a/third_party/WebKit/LayoutTests/crypto/subtle/importKey-badParameters-expected.txt
+++ b/third_party/WebKit/LayoutTests/crypto/subtle/importKey-badParameters-expected.txt
@@ -11,6 +11,7 @@
 error is: TypeError: Invalid keyFormat argument
 error is: TypeError: HmacImportParams: hash: Missing or not an AlgorithmIdentifier
 error is: NotSupportedError: SHA-1: Unsupported operation: importKey
+error is: SharedArrayBuffers not enabled.
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/crypto/subtle/importKey-badParameters.html b/third_party/WebKit/LayoutTests/crypto/subtle/importKey-badParameters.html
index 9052c10..303f4e2 100644
--- a/third_party/WebKit/LayoutTests/crypto/subtle/importKey-badParameters.html
+++ b/third_party/WebKit/LayoutTests/crypto/subtle/importKey-badParameters.html
@@ -62,6 +62,16 @@
     return crypto.subtle.importKey('raw', new Uint8Array(20), {name: 'sha-1'}, extractable, ['sign']);
 }).then(failAndFinishJSTest, function(result) {
     logError(result);
+
+    // SharedArrayBuffer-backed view is not allowed.
+    if (window.SharedArrayBuffer) {
+        var bytes = new Uint8Array(new SharedArrayBuffer(16));
+        return crypto.subtle.importKey('raw', bytes, aesCbc, extractable, ['encrypt']);
+    } else {
+        return Promise.reject('SharedArrayBuffers not enabled.');
+    }
+}).then(failAndFinishJSTest, function(result) {
+    logError(result);
 }).then(finishJSTest, failAndFinishJSTest);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/fast/beacon/beacon-basic.html b/third_party/WebKit/LayoutTests/fast/beacon/beacon-basic.html
index 6450c6ef..faa5fa2 100644
--- a/third_party/WebKit/LayoutTests/fast/beacon/beacon-basic.html
+++ b/third_party/WebKit/LayoutTests/fast/beacon/beacon-basic.html
@@ -8,4 +8,9 @@
 shouldThrow("navigator.sendBeacon()");
 shouldThrow("navigator.sendBeacon('http:')");
 shouldThrow("navigator.sendBeacon('javascript:alert(1);')");
+
+if (window.SharedArrayBuffer) {
+  shouldThrow("navigator.sendBeacon('https:', new Uint8Array(new SharedArrayBuffer(10)))");
+}
+
 </script>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-ImageData-constructor.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-ImageData-constructor.html
index 16c788d6..c5b0e58 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-ImageData-constructor.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-ImageData-constructor.html
@@ -74,6 +74,10 @@
     assert_array_equals(getRGBA(imageDataFromData.data, 2), getRGBA(data, 2));
     setRGBA(imageDataFromData.data, 2, testColor);
     assert_array_equals(getRGBA(imageDataFromData.data, 2), getRGBA(data, 2));
+
+    if (window.SharedArrayBuffer) {
+        assert_throws(null, function() {new ImageData(new Uint16Array(new SharedArrayBuffer(32)), 4, 2)});
+    }
         
 }, 'Test ImageData constructor');
 </script>
diff --git a/third_party/WebKit/LayoutTests/fast/css/fontface-arraybuffer.html b/third_party/WebKit/LayoutTests/fast/css/fontface-arraybuffer.html
index 1bbcd1ca..31457b6 100644
--- a/third_party/WebKit/LayoutTests/fast/css/fontface-arraybuffer.html
+++ b/third_party/WebKit/LayoutTests/fast/css/fontface-arraybuffer.html
@@ -23,6 +23,16 @@
 shouldBeEqualToString('face2.status', 'loaded');
 document.fonts.add(face2);
 
+if (window.SharedArrayBuffer) {
+    try {
+        new FontFace('FontFromSharedArrayBufferView',
+                     new Uint8Array(new SharedArrayBuffer(4)));
+    } catch (e) {
+        rejectionValue = e;
+        shouldBeEqualToString('rejectionValue.name', 'TypeError');
+    }
+}
+
 var face3 = new FontFace('FontFromEmptyArrayBuffer', new ArrayBuffer(0));
 shouldBeEqualToString('face3.status', 'error');
 face3.load().catch(function(v) {
diff --git a/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-matrix-readonly.html b/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-matrix-readonly.html
index 72c1c8fb..3aa6eb3 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-matrix-readonly.html
+++ b/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-matrix-readonly.html
@@ -177,6 +177,15 @@
     "fromFloat64Array function only accepts 1 Float64Array with 6 or 16 elements");
 }, "DOMMatrixReadOnly fromFloat*Array - invalid array size");
 
+if (window.SharedArrayBuffer) {
+  test(() => {
+    assert_throws(new TypeError(), () => { DOMMatrixReadOnly.fromFloat32Array(new Float32Array(new SharedArrayBuffer(24))); },
+      "");
+    assert_throws(new TypeError(), () => { DOMMatrixReadOnly.fromFloat64Array(new Float64Array(new SharedArrayBuffer(32))); },
+      "");
+  }, "DOMMatrixReadOnly fromFloat*Array - can't use SharedArrayBuffer view");
+}
+
 test(() => {
   var matrix = DOMMatrixReadOnly.fromMatrix();
   assert_identity_2d_matrix(matrix);
diff --git a/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-matrix.html b/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-matrix.html
index 998c3da..525425f 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-matrix.html
+++ b/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-matrix.html
@@ -120,6 +120,15 @@
     "fromFloat64Array function only accepts 1 Float64Array with 6 or 16 elements");
 }, "DOMMatrix fromFloat*Array - invalid array size");
 
+if (window.SharedArrayBuffer) {
+  test(() => {
+    assert_throws(new TypeError(), () => { DOMMatrix.fromFloat32Array(new Float32Array(new SharedArrayBuffer(24))); },
+      "");
+    assert_throws(new TypeError(), () => { DOMMatrix.fromFloat64Array(new Float64Array(new SharedArrayBuffer(32))); },
+      "");
+  }, "DOMMatrix fromFloat*Array - can't use SharedArrayBuffer view");
+}
+
 test(() => {
   assert_identity_2d_matrix(DOMMatrix.fromMatrix());
 }, "DOMMatrix.fromMatrix() with no parameter");
diff --git a/third_party/WebKit/LayoutTests/fast/encoding/api/sharedarraybuffer.html b/third_party/WebKit/LayoutTests/fast/encoding/api/sharedarraybuffer.html
new file mode 100644
index 0000000..161b546
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/encoding/api/sharedarraybuffer.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script>
+
+if (window.SharedArrayBuffer) {
+  test(() => {
+    const decoder = new TextDecoder('utf-8');
+    assert_throws(null, () => {
+      decoder.decode(new Uint8Array(new SharedArrayBuffer(4)));
+    }, 'constructing TextDecoder with SharedArrayBuffer view should throw');
+  }, 'decoding SharedArrayBuffer');
+}
+done();
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/events/constructors/midi-message-event-constructor.html b/third_party/WebKit/LayoutTests/fast/events/constructors/midi-message-event-constructor.html
index 0631a39..46e2da7 100644
--- a/third_party/WebKit/LayoutTests/fast/events/constructors/midi-message-event-constructor.html
+++ b/third_party/WebKit/LayoutTests/fast/events/constructors/midi-message-event-constructor.html
@@ -31,6 +31,11 @@
 shouldBe("new MIDIMessageEvent('eventType', { bubbles: true, cancelable: true, data: data }).cancelable", "true");
 shouldEvaluateTo("new MIDIMessageEvent('eventType', { bubbles: true, cancelable: true, data: data }).data", data);
 
+if (window.SharedArrayBuffer) {
+    data = new Uint8Array(new SharedArrayBuffer(3));
+    shouldThrow("new MIDIMessageEvent('eventType', { data: data })");
+}
+
 </script>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/fast/files/blob-constructor.html b/third_party/WebKit/LayoutTests/fast/files/blob-constructor.html
index 5261d19..df34b9c 100644
--- a/third_party/WebKit/LayoutTests/fast/files/blob-constructor.html
+++ b/third_party/WebKit/LayoutTests/fast/files/blob-constructor.html
@@ -123,6 +123,11 @@
 shouldBe("new Blob([(new Float64Array(100)).buffer, (new Int32Array(100)).buffer, (new Uint8Array(100)).buffer, (new DataView(new ArrayBuffer(100))).buffer]).size", "1400");
 shouldBe("new Blob([new Blob([(new Int32Array(100)).buffer]), (new Uint8Array(100)).buffer, (new Float32Array(100)).buffer, (new DataView(new ArrayBuffer(100))).buffer]).size", "1000");
 
+if (window.SharedArrayBuffer) {
+  // Test SharedArrayBuffer parameters.
+  shouldThrow("new Blob([new Uint8Array(new SharedArrayBuffer(4))])", '"TypeError: Failed to construct \'Blob\': The provided ArrayBufferView value must not be shared."');
+}
+
 // Test passing blob parts in objects with indexed properties.
 // (This depends on the bindings code handling of sequence<T>)
 shouldBe("new Blob({length: 0}).size", "0");
diff --git a/third_party/WebKit/LayoutTests/fast/peerconnection/RTCPeerConnection-datachannel.html b/third_party/WebKit/LayoutTests/fast/peerconnection/RTCPeerConnection-datachannel.html
index 21e7dc6..260f377 100644
--- a/third_party/WebKit/LayoutTests/fast/peerconnection/RTCPeerConnection-datachannel.html
+++ b/third_party/WebKit/LayoutTests/fast/peerconnection/RTCPeerConnection-datachannel.html
@@ -19,6 +19,15 @@
     finishJSTest();
 }
 
+function dc_onmessage_sharedarraybuffer_view() {
+    if (window.SharedArrayBuffer) {
+        shouldThrow("dc.send(new Uint8Array(new SharedArrayBuffer(16)));");
+    }
+
+    dc.onclose = dc_onclose;
+    dc.close();
+}
+
 function dc_onmessage_dataview(e) {
     testPassed("dc_onmessage_dataview was called");
     data = e.data;
@@ -27,8 +36,7 @@
     shouldBe("array[0]", "1");
     shouldBe("array[9]", "10");
 
-    dc.onclose = dc_onclose;
-    dc.close();
+    dc_onmessage_sharedarraybuffer_view();
 }
 
 function dc_onmessage_arraybuffer(e) {
diff --git a/third_party/WebKit/LayoutTests/fast/xmlhttprequest/xmlhttprequest-send-sharedarraybuffer.html b/third_party/WebKit/LayoutTests/fast/xmlhttprequest/xmlhttprequest-send-sharedarraybuffer.html
new file mode 100644
index 0000000..9f02118
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/xmlhttprequest/xmlhttprequest-send-sharedarraybuffer.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+
+if (window.SharedArrayBuffer) {
+  test(() => {
+    const xhr = new XMLHttpRequest();
+    xhr.open('POST', '/foo.html');
+
+    assert_throws(null, () => {
+      xhr.send(new Uint8Array(new SharedArrayBuffer(32)));
+    }, 'send() of SharedArrayBuffer view should throw');
+  }, 'sending SharedArrayBuffer');
+}
+done();
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/geolocation-api/position-string-expected.txt b/third_party/WebKit/LayoutTests/geolocation-api/position-string-expected.txt
index 045f496..f1a1d78 100644
--- a/third_party/WebKit/LayoutTests/geolocation-api/position-string-expected.txt
+++ b/third_party/WebKit/LayoutTests/geolocation-api/position-string-expected.txt
@@ -6,7 +6,7 @@
 PASS position.coords.latitude is mockLatitude
 PASS position.coords.longitude is mockLongitude
 PASS position.coords.accuracy is mockAccuracy
-PASS position.toString() is "[object Geoposition]"
+PASS position.toString() is "[object Position]"
 PASS position.coords.toString() is "[object Coordinates]"
 PASS successfullyParsed is true
 
diff --git a/third_party/WebKit/LayoutTests/geolocation-api/position-string.html b/third_party/WebKit/LayoutTests/geolocation-api/position-string.html
index d5298dfc..665d4f1a 100644
--- a/third_party/WebKit/LayoutTests/geolocation-api/position-string.html
+++ b/third_party/WebKit/LayoutTests/geolocation-api/position-string.html
@@ -27,7 +27,7 @@
         shouldBe('position.coords.latitude', 'mockLatitude');
         shouldBe('position.coords.longitude', 'mockLongitude');
         shouldBe('position.coords.accuracy', 'mockAccuracy');
-        shouldBe('position.toString()', '"[object Geoposition]"');
+        shouldBe('position.toString()', '"[object Position]"');
         shouldBe('position.coords.toString()', '"[object Coordinates]"');
         finishJSTest();
     }, function(e) {
diff --git a/third_party/WebKit/LayoutTests/http/tests/media/media-source/mediasource-append-buffer.html b/third_party/WebKit/LayoutTests/http/tests/media/media-source/mediasource-append-buffer.html
index ce60447..1d25402 100644
--- a/third_party/WebKit/LayoutTests/http/tests/media/media-source/mediasource-append-buffer.html
+++ b/third_party/WebKit/LayoutTests/http/tests/media/media-source/mediasource-append-buffer.html
@@ -550,6 +550,22 @@
               test.done();
           }, 'Test appending null.');
 
+          if (window.SharedArrayBuffer) {
+              mediasource_test(function(test, mediaElement, mediaSource)
+              {
+                  var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE);
+
+                  test.expectEvent(sourceBuffer, 'updatestart', 'Append started.');
+                  test.expectEvent(sourceBuffer, 'update', 'Append success.');
+                  test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');
+
+                  assert_throws( { name: 'TypeError'} ,
+                      function() { sourceBuffer.appendBuffer(new Uint8Array(new SharedArrayBuffer(16))); },
+                      'appendBuffer() of SharedArrayBuffer view throws an exception.');
+                  test.done();
+              }, 'Test appending SharedArrayBuffer view.');
+          }
+
           mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
           {
               mediaSource.removeSourceBuffer(sourceBuffer);
diff --git a/third_party/WebKit/LayoutTests/http/tests/push_messaging/resources/pushmessagedata-worker.js b/third_party/WebKit/LayoutTests/http/tests/push_messaging/resources/pushmessagedata-worker.js
index 36fa4437..6b9a49f 100644
--- a/third_party/WebKit/LayoutTests/http/tests/push_messaging/resources/pushmessagedata-worker.js
+++ b/third_party/WebKit/LayoutTests/http/tests/push_messaging/resources/pushmessagedata-worker.js
@@ -108,3 +108,11 @@
     assert_equals(data.text(), s, 'String should not be NFC-normalized.');
 
 }, 'PushEventInit data is not normalized');
+
+if (self.SharedArrayBuffer) {
+    test(function() {
+        assert_throws(null, () => {
+            createPushMessageData(new Uint8Array(new SharedArrayBuffer(16)));
+        });
+    }, 'PushMessageData throws when passed SharedArrayBuffer view.');
+}
diff --git a/third_party/WebKit/LayoutTests/http/tests/websocket/send-arraybufferview.html b/third_party/WebKit/LayoutTests/http/tests/websocket/send-arraybufferview.html
index b85476f8f..a25a2468 100644
--- a/third_party/WebKit/LayoutTests/http/tests/websocket/send-arraybufferview.html
+++ b/third_party/WebKit/LayoutTests/http/tests/websocket/send-arraybufferview.html
@@ -40,6 +40,11 @@
     return array;
 }
 
+function createSharedArrayBufferView()
+{
+    return new Uint8Array(new SharedArrayBuffer(16));
+}
+
 var url = "ws://127.0.0.1:8880/check-binary-messages";
 var ws = new WebSocket(url);
 var closeEvent;
@@ -49,6 +54,10 @@
     ws.send(createArrayBufferViewContainingHelloWorld());
     ws.send(createEmptyArrayBufferView());
     ws.send(createArrayBufferViewContainingAllDistinctBytes());
+
+    if (window.SharedArrayBuffer) {
+        shouldThrow("ws.send(createSharedArrayBufferView())");
+    }
 };
 
 ws.onmessage = function(event)
diff --git a/third_party/WebKit/LayoutTests/sensor/absolute-orientation-sensor.html b/third_party/WebKit/LayoutTests/sensor/absolute-orientation-sensor.html
index 8e563b65..f05c52a 100644
--- a/third_party/WebKit/LayoutTests/sensor/absolute-orientation-sensor.html
+++ b/third_party/WebKit/LayoutTests/sensor/absolute-orientation-sensor.html
@@ -48,6 +48,11 @@
   // Throws if no orientation data available.
   assert_throws({ name: 'NotReadableError' }, () => sensorObject.populateMatrix(new Float32Array(16)));
 
+  if (window.SharedArrayBuffer) {
+    // Throws if passed SharedArrayBuffer view.
+    assert_throws({ name: 'TypeError' }, () => sensorObject.populateMatrix(new Float32Array(new SharedArrayBuffer(16))));
+  }
+
   sensorObject.start();
 
   return sensor.mockSensorProvider.getCreatedSensor()
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/crypto/README.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/crypto/README.txt
new file mode 100644
index 0000000..51001f0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/crypto/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in with --js-flags=--harmony-sharedarraybuffer
+# This enables the SharedArrayBuffer language feature in V8.
+# See https://github.com/tc39/ecmascript_sharedmem for more information.
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/crypto/random-values-expected.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/crypto/random-values-expected.txt
new file mode 100644
index 0000000..1334f1f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/crypto/random-values-expected.txt
@@ -0,0 +1,14 @@
+Tests crypto.randomValues.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS 'crypto' in self is true
+PASS 'getRandomValues' in self.crypto is true
+PASS self.crypto.__proto__.hasOwnProperty('getRandomValues') is true
+PASS matchingBytes < 100 is true
+PASS crypto.getRandomValues(new Uint8Array(new SharedArrayBuffer(100))) threw exception TypeError: Failed to execute 'getRandomValues' on 'Crypto': The provided ArrayBufferView value must not be shared..
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/crypto/subtle/importKey-badParameters-expected.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/crypto/subtle/importKey-badParameters-expected.txt
new file mode 100644
index 0000000..f3e09a7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/crypto/subtle/importKey-badParameters-expected.txt
@@ -0,0 +1,18 @@
+Tests calling cypto.subtle.importKey with bad parameters
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+error is: TypeError: Key data must be a BufferSource for non-JWK formats
+error is: TypeError: Key data must be a BufferSource for non-JWK formats
+error is: TypeError: Algorithm: Not an object
+error is: TypeError: Invalid keyFormat argument
+error is: TypeError: Invalid keyUsages argument
+error is: TypeError: Invalid keyFormat argument
+error is: TypeError: HmacImportParams: hash: Missing or not an AlgorithmIdentifier
+error is: NotSupportedError: SHA-1: Unsupported operation: importKey
+error is: TypeError: Failed to execute 'importKey' on 'SubtleCrypto': The provided ArrayBufferView value must not be shared.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/crypto/worker-random-values-expected.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/crypto/worker-random-values-expected.txt
new file mode 100644
index 0000000..47f33e1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/crypto/worker-random-values-expected.txt
@@ -0,0 +1,15 @@
+[Worker] Tests crypto.randomValues.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Starting worker: random-values.js
+PASS [Worker] 'crypto' in self is true
+PASS [Worker] 'getRandomValues' in self.crypto is true
+PASS [Worker] self.crypto.__proto__.hasOwnProperty('getRandomValues') is true
+PASS [Worker] matchingBytes < 100 is true
+PASS [Worker] crypto.getRandomValues(new Uint8Array(new SharedArrayBuffer(100))) threw exception TypeError: Failed to execute 'getRandomValues' on 'Crypto': The provided ArrayBufferView value must not be shared..
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/beacon/README.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/beacon/README.txt
new file mode 100644
index 0000000..de49eb0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/beacon/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in --js-flags=--harmony-sharedarraybuffer
+# This enables the SharedArrayBuffer language feature in V8.
+# See https://github.com/tc39/ecmascript_sharedmem for more information.
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/beacon/beacon-basic-expected.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/beacon/beacon-basic-expected.txt
new file mode 100644
index 0000000..4353a79b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/beacon/beacon-basic-expected.txt
@@ -0,0 +1,15 @@
+Exercising the Beacon API
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS Object.getPrototypeOf(navigator).hasOwnProperty('sendBeacon') is true
+PASS typeof navigator.sendBeacon is "function"
+PASS navigator.sendBeacon() threw exception TypeError: Failed to execute 'sendBeacon' on 'Navigator': 1 argument required, but only 0 present..
+PASS navigator.sendBeacon('http:') threw exception SyntaxError: Failed to execute 'sendBeacon' on 'Navigator': The URL argument is ill-formed or unsupported..
+PASS navigator.sendBeacon('javascript:alert(1);') threw exception SyntaxError: Failed to execute 'sendBeacon' on 'Navigator': Beacons are only supported over HTTP(S)..
+PASS navigator.sendBeacon('https:', new Uint8Array(new SharedArrayBuffer(10))) threw exception TypeError: Failed to execute 'sendBeacon' on 'Navigator': The provided ArrayBufferView value must not be shared..
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/canvas/README.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/canvas/README.txt
new file mode 100644
index 0000000..de49eb0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/canvas/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in --js-flags=--harmony-sharedarraybuffer
+# This enables the SharedArrayBuffer language feature in V8.
+# See https://github.com/tc39/ecmascript_sharedmem for more information.
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/canvas/canvas-lost-gpu-context-expected.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/canvas/canvas-lost-gpu-context-expected.txt
new file mode 100644
index 0000000..83502ff
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/canvas/canvas-lost-gpu-context-expected.txt
@@ -0,0 +1,13 @@
+Test the behavior of canvas recovery after a gpu context loss
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS contextLostTest is false
+PASS ctx.isContextLost() is false
+PASS ctx.isContextLost() is false
+Aborting test: Graphics context loss did not destroy canvas contents. This is expected if canvas is not accelerated.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/css/README.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/css/README.txt
new file mode 100644
index 0000000..de49eb0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/css/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in --js-flags=--harmony-sharedarraybuffer
+# This enables the SharedArrayBuffer language feature in V8.
+# See https://github.com/tc39/ecmascript_sharedmem for more information.
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/css/fontface-arraybuffer-expected.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/css/fontface-arraybuffer-expected.txt
new file mode 100644
index 0000000..26e246e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/css/fontface-arraybuffer-expected.txt
@@ -0,0 +1,18 @@
+Tests ArrayBuffer / ArrayBufferView constructors of FontFace.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS face1.status is "loaded"
+PASS face2.status is "loaded"
+PASS rejectionValue.name is "TypeError"
+PASS face3.status is "error"
+PASS rejectionValue.name is "SyntaxError"
+PASS document.getElementById('FontFromArrayBuffer').offsetWidth is document.getElementById('ref').offsetWidth
+PASS document.getElementById('FontFromArrayBufferView').offsetWidth is document.getElementById('ref').offsetWidth
+PASS successfullyParsed is true
+
+TEST COMPLETE
+abc
+abc
+abc
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/dom/README.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/dom/README.txt
new file mode 100644
index 0000000..de49eb0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/dom/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in --js-flags=--harmony-sharedarraybuffer
+# This enables the SharedArrayBuffer language feature in V8.
+# See https://github.com/tc39/ecmascript_sharedmem for more information.
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/dom/geometry-interfaces-dom-matrix-expected.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/dom/geometry-interfaces-dom-matrix-expected.txt
new file mode 100644
index 0000000..9b8652c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/dom/geometry-interfaces-dom-matrix-expected.txt
@@ -0,0 +1,31 @@
+This is a testharness.js-based test.
+Harness Error. harness_status.status = 1 , harness_status.message = 1 duplicate test name: "DOMMatrix(numberSequence) constructor"
+PASS DOMMatrix() constructor 
+PASS DOMMatrix fromFloat32Array - 2D matrix 
+PASS DOMMatrix fromFloat64Array - 2D matrix 
+PASS DOMMatrix fromFloat32Array - 3D matrix 
+PASS DOMMatrix fromFloat64Array - 3D matrix 
+PASS DOMMatrix(transformList) - emptyString 
+PASS DOMMatrix(transformList) - transformList 
+PASS DOMMatrix(numberSequence) constructor 
+PASS DOMMatrix(numberSequence) constructor 
+PASS DOMMatrix attributes 
+PASS DOMMatrix.is2D can never be set to 'true' when it was set to 'false' before calling setMatrixValue() 
+PASS DOMMatrix fromFloat*Array - invalid array size of nearby 6 
+PASS DOMMatrix fromFloat*Array - invalid array size of nearby 16 
+PASS DOMMatrix fromFloat*Array - invalid array size 
+PASS DOMMatrix fromFloat*Array - can't use SharedArrayBuffer view 
+PASS DOMMatrix.fromMatrix() with no parameter 
+PASS DOMMatrix.fromMatrix() with null 
+PASS DOMMatrix.fromMatrix() with undefined 
+PASS DOMMatrix.fromMatrix() with empty object 
+PASS DOMMatrix.fromMatrix({a: 1, b: 2, c: 3, d: 4, e: 5, f: 6}) should create a 2D DOMMatrix 
+PASS DOMMatrix.fromMatrix({m11: 1, m22: 2, m33: 3, m44: 4, m23: 5, m43: 6}) should create a 3D DOMMatrix 
+PASS If 2d related properties don't be set, should set to fallback 
+PASS DOMMatrix.fromMatrix(): NaN test 
+PASS DOMMatrix toJSON() - identity matrix 
+PASS DOMMatrix toJSON() - 2D matrix 
+PASS DOMMatrix toJSON() - 3D matrix 
+PASS DOMMatrix.fromMatrix(): Exception test 
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/encoding/api/README.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/encoding/api/README.txt
new file mode 100644
index 0000000..de49eb0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/encoding/api/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in --js-flags=--harmony-sharedarraybuffer
+# This enables the SharedArrayBuffer language feature in V8.
+# See https://github.com/tc39/ecmascript_sharedmem for more information.
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/events/constructors/README.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/events/constructors/README.txt
new file mode 100644
index 0000000..de49eb0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/events/constructors/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in --js-flags=--harmony-sharedarraybuffer
+# This enables the SharedArrayBuffer language feature in V8.
+# See https://github.com/tc39/ecmascript_sharedmem for more information.
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/events/constructors/midi-message-event-constructor-expected.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/events/constructors/midi-message-event-constructor-expected.txt
new file mode 100644
index 0000000..14b8b2b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/events/constructors/midi-message-event-constructor-expected.txt
@@ -0,0 +1,21 @@
+This tests the constructor for the MIDIMessageEvent DOM class.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS new MIDIMessageEvent('eventType').bubbles is false
+PASS new MIDIMessageEvent('eventType').cancelable is false
+PASS new MIDIMessageEvent('eventType').data is null
+PASS new MIDIMessageEvent('eventType', { bubbles: false }).bubbles is false
+PASS new MIDIMessageEvent('eventType', { bubbles: true }).bubbles is true
+PASS new MIDIMessageEvent('eventType', { cancelable: false }).cancelable is false
+PASS new MIDIMessageEvent('eventType', { cancelable: true }).cancelable is true
+PASS new MIDIMessageEvent('eventType', { data: data }).data == '0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0' is true
+PASS new MIDIMessageEvent('eventType', { bubbles: true, cancelable: true, data: data }).bubbles is true
+PASS new MIDIMessageEvent('eventType', { bubbles: true, cancelable: true, data: data }).cancelable is true
+PASS new MIDIMessageEvent('eventType', { bubbles: true, cancelable: true, data: data }).data == '0,0,0' is true
+PASS new MIDIMessageEvent('eventType', { data: data }) threw exception TypeError: Failed to construct 'MIDIMessageEvent': The provided ArrayBufferView value must not be shared..
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/files/README.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/files/README.txt
new file mode 100644
index 0000000..de49eb0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/files/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in --js-flags=--harmony-sharedarraybuffer
+# This enables the SharedArrayBuffer language feature in V8.
+# See https://github.com/tc39/ecmascript_sharedmem for more information.
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/files/blob-constructor-expected.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/files/blob-constructor-expected.txt
new file mode 100644
index 0000000..195ef38
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/files/blob-constructor-expected.txt
@@ -0,0 +1,96 @@
+Test the Blob constructor.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS (new Blob()) instanceof window.Blob is true
+PASS (new Blob(undefined)) instanceof window.Blob is true
+PASS (new Blob([])) instanceof window.Blob is true
+PASS (new Blob(['hello'])) instanceof window.Blob is true
+PASS (new Blob(['hello'], {})) instanceof window.Blob is true
+PASS (new Blob(['hello'], {type:'text/html'})) instanceof window.Blob is true
+PASS (new Blob(['hello'], {type:'text/html', endings:'native'})) instanceof window.Blob is true
+PASS (new Blob(['hello'], {type:'text/html', endings:'transparent'})) instanceof window.Blob is true
+PASS (new Blob()).size is 0
+PASS (new Blob(undefined)).size is 0
+PASS (new Blob()).type is ""
+PASS (new Blob(undefined)).type is ""
+PASS new Blob('hello') threw exception TypeError: Failed to construct 'Blob': The 1st argument is neither an array, nor does it have indexed properties..
+PASS new Blob(0) threw exception TypeError: Failed to construct 'Blob': The 1st argument is neither an array, nor does it have indexed properties..
+PASS new Blob(null) threw exception TypeError: Failed to construct 'Blob': The 1st argument is neither an array, nor does it have indexed properties..
+PASS (new Blob([])) instanceof window.Blob is true
+PASS (new Blob(['stringPrimitive'])) instanceof window.Blob is true
+PASS (new Blob([String('stringObject')])) instanceof window.Blob is true
+PASS (new Blob([new Blob])) instanceof window.Blob is true
+PASS (new Blob([new Blob([new Blob])])) instanceof window.Blob is true
+PASS (new Blob([12])).size is 2
+PASS (new Blob([[]])).size is 0
+PASS (new Blob([{}])).size is 15
+PASS (new Blob([document])).size is 21
+PASS (new Blob([toStringingObj])).size is 8
+PASS new Blob([throwingObj]) threw exception Error.
+PASS (new Blob([], {unknownKey:'value'})) instanceof window.Blob is true
+PASS new Blob([], {endings:'illegalValue'}) threw exception TypeError: Failed to construct 'Blob': The provided value 'illegalValue' is not a valid enum value of type NormalizeLineEndings..
+PASS new Blob([], {endings:throwingObj}) threw exception Error.
+PASS new Blob([], {type:throwingObj}) threw exception Error.
+PASS new Blob([], {endings:throwingObj1, type:throwingObj2}) threw exception Error 1.
+PASS new Blob([], {type:throwingObj2, endings:throwingObj1}) threw exception Error 1.
+PASS new Blob([], {type:throwingObj2, endings:'illegal'}) threw exception TypeError: Failed to construct 'Blob': The provided value 'illegal' is not a valid enum value of type NormalizeLineEndings..
+PASS new Blob([], {type:'hello\u00EE'}).type is ""
+PASS new Blob([], {type:'hello\u001F'}).type is ""
+PASS new Blob([], {type:'hello\u007F'}).type is ""
+PASS new Blob([], {type:'ABC/abc'}).type is "abc/abc"
+PASS new Blob([], {type:'123ABCabc'}).type is "123abcabc"
+PASS (new Blob([], null)) instanceof window.Blob is true
+PASS (new Blob([], undefined)) instanceof window.Blob is true
+PASS (new Blob([], 123)) instanceof window.Blob threw exception TypeError: Failed to construct 'Blob': parameter 2 ('options') is not an object..
+PASS (new Blob([], 123.4)) instanceof window.Blob threw exception TypeError: Failed to construct 'Blob': parameter 2 ('options') is not an object..
+PASS (new Blob([], true)) instanceof window.Blob threw exception TypeError: Failed to construct 'Blob': parameter 2 ('options') is not an object..
+PASS (new Blob([], 'abc')) instanceof window.Blob threw exception TypeError: Failed to construct 'Blob': parameter 2 ('options') is not an object..
+PASS (new Blob([], [])) instanceof window.Blob is true
+PASS (new Blob([], /abc/)) instanceof window.Blob is true
+PASS (new Blob([], function () {})) instanceof window.Blob is true
+PASS (new Blob([], {type:'text/html'})).type is 'text/html'
+PASS (new Blob([], {type:'text/html'})).size is 0
+PASS (new Blob([], {type:'text/plain;charset=UTF-8'})).type is 'text/plain;charset=utf-8'
+PASS (new Blob([])).lastModified is undefined
+PASS (new Blob([], {})).lastModified is undefined
+PASS (new Blob([], {lastModified: new Date()})).lastModified is undefined
+PASS window.Blob.length is 0
+PASS new Blob([new DataView(new ArrayBuffer(100))]).size is 100
+PASS new Blob([new Uint8Array(100)]).size is 100
+PASS new Blob([new Uint8ClampedArray(100)]).size is 100
+PASS new Blob([new Uint16Array(100)]).size is 200
+PASS new Blob([new Uint32Array(100)]).size is 400
+PASS new Blob([new Int8Array(100)]).size is 100
+PASS new Blob([new Int16Array(100)]).size is 200
+PASS new Blob([new Int32Array(100)]).size is 400
+PASS new Blob([new Float32Array(100)]).size is 400
+PASS new Blob([new Float64Array(100)]).size is 800
+PASS new Blob([new Float64Array(100), new Int32Array(100), new Uint8Array(100), new DataView(new ArrayBuffer(100))]).size is 1400
+PASS new Blob([new Blob([new Int32Array(100)]), new Uint8Array(100), new Float32Array(100), new DataView(new ArrayBuffer(100))]).size is 1000
+PASS new Blob([(new DataView(new ArrayBuffer(100))).buffer]).size is 100
+PASS new Blob([(new Uint8Array(100)).buffer]).size is 100
+PASS new Blob([(new Uint8ClampedArray(100)).buffer]).size is 100
+PASS new Blob([(new Uint16Array(100)).buffer]).size is 200
+PASS new Blob([(new Uint32Array(100)).buffer]).size is 400
+PASS new Blob([(new Int8Array(100)).buffer]).size is 100
+PASS new Blob([(new Int16Array(100)).buffer]).size is 200
+PASS new Blob([(new Int32Array(100)).buffer]).size is 400
+PASS new Blob([(new Float32Array(100)).buffer]).size is 400
+PASS new Blob([(new Float64Array(100)).buffer]).size is 800
+PASS new Blob([(new Float64Array(100)).buffer, (new Int32Array(100)).buffer, (new Uint8Array(100)).buffer, (new DataView(new ArrayBuffer(100))).buffer]).size is 1400
+PASS new Blob([new Blob([(new Int32Array(100)).buffer]), (new Uint8Array(100)).buffer, (new Float32Array(100)).buffer, (new DataView(new ArrayBuffer(100))).buffer]).size is 1000
+PASS new Blob([new Uint8Array(new SharedArrayBuffer(4))]) threw exception TypeError: Failed to construct 'Blob': The provided ArrayBufferView value must not be shared..
+PASS new Blob({length: 0}).size is 0
+PASS new Blob({length: 1, 0: 'string'}).size is 6
+PASS OMICRON_WITH_OXIA.charCodeAt(0) is 0x1F79
+PASS reader.result.charCodeAt(0) is 0x1F79
+PASS CONTAINS_UNPAIRED_SURROGATES.charCodeAt(3) is 0xDC00
+PASS CONTAINS_UNPAIRED_SURROGATES.charCodeAt(7) is 0xD800
+PASS reader.result.charCodeAt(3) is 0xFFFD
+PASS reader.result.charCodeAt(7) is 0xFFFD
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/peerconnection/README.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/peerconnection/README.txt
new file mode 100644
index 0000000..de49eb0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/peerconnection/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in --js-flags=--harmony-sharedarraybuffer
+# This enables the SharedArrayBuffer language feature in V8.
+# See https://github.com/tc39/ecmascript_sharedmem for more information.
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/peerconnection/RTCPeerConnection-datachannel-expected.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/peerconnection/RTCPeerConnection-datachannel-expected.txt
new file mode 100644
index 0000000..a625818
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/peerconnection/RTCPeerConnection-datachannel-expected.txt
@@ -0,0 +1,45 @@
+Tests RTCDataChannel.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS dc = pc.createDataChannel("label1"); did not throw exception.
+PASS dc.reliable is true
+PASS dc = pc.createDataChannel("label2", {}); did not throw exception.
+PASS dc.reliable is true
+PASS dc = pc.createDataChannel("label3", {ordered:true}); did not throw exception.
+PASS dc.reliable is true
+PASS dc = pc.createDataChannel("label3", {ordered:false}); did not throw exception.
+PASS dc.reliable is false
+PASS dc = pc.createDataChannel("label3", {maxRetransmits:0}); did not throw exception.
+PASS dc.reliable is false
+PASS dc = pc.createDataChannel("label3", {maxRetransmitTime:0}); did not throw exception.
+PASS dc.reliable is false
+PASS pc is connected
+PASS dc = pc.createDataChannel("label"); did not throw exception.
+PASS dc.readyState is 'connecting'
+PASS pc_ondatachannel was called
+PASS dc_onopen was called
+PASS dc.readyState is 'open'
+PASS dc.label is 'label'
+PASS dc.send('xyzzy'); did not throw exception.
+PASS dc_onmessage_string was called
+PASS data is 'xyzzy'
+PASS dc.send(buffer); did not throw exception.
+PASS dc_onmessage_arraybuffer was called
+PASS data.byteLength is 2
+PASS array[0] is 17
+PASS array[1] is 19
+PASS data.byteLength is 12
+PASS dc.send(shrunkView); did not throw exception.
+PASS dc_onmessage_dataview was called
+PASS data.byteLength is 10
+PASS array[0] is 1
+PASS array[9] is 10
+PASS dc.send(new Uint8Array(new SharedArrayBuffer(16))); threw exception TypeError: Failed to execute 'send' on 'RTCDataChannel': The provided ArrayBufferView value must not be shared..
+PASS dc_onclose was called
+PASS dc.readyState is 'closed'
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/workers/README.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/workers/README.txt
index cb3dbc97..de49eb0 100644
--- a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/workers/README.txt
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/fast/workers/README.txt
@@ -1,5 +1,3 @@
-# This suite runs the tests in fast/workers with
-# --js-flags=--harmony-sharedarraybuffer
-# This enables the experimental SharedArrayBuffer language feature in V8.
-# See
-# https://github.com/lars-t-hansen/ecmascript_sharedmem for more information.
+# This suite runs the tests in --js-flags=--harmony-sharedarraybuffer
+# This enables the SharedArrayBuffer language feature in V8.
+# See https://github.com/tc39/ecmascript_sharedmem for more information.
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/http/tests/media/media-source/README.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/http/tests/media/media-source/README.txt
new file mode 100644
index 0000000..de49eb0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/http/tests/media/media-source/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in --js-flags=--harmony-sharedarraybuffer
+# This enables the SharedArrayBuffer language feature in V8.
+# See https://github.com/tc39/ecmascript_sharedmem for more information.
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/http/tests/push_messaging/README.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/http/tests/push_messaging/README.txt
new file mode 100644
index 0000000..de49eb0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/http/tests/push_messaging/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in --js-flags=--harmony-sharedarraybuffer
+# This enables the SharedArrayBuffer language feature in V8.
+# See https://github.com/tc39/ecmascript_sharedmem for more information.
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/http/tests/websocket/README.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/http/tests/websocket/README.txt
new file mode 100644
index 0000000..de49eb0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/http/tests/websocket/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in --js-flags=--harmony-sharedarraybuffer
+# This enables the SharedArrayBuffer language feature in V8.
+# See https://github.com/tc39/ecmascript_sharedmem for more information.
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/http/tests/websocket/send-arraybufferview-expected.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/http/tests/websocket/send-arraybufferview-expected.txt
new file mode 100644
index 0000000..f4970fe
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/http/tests/websocket/send-arraybufferview-expected.txt
@@ -0,0 +1,13 @@
+WebSocket: Send ArrayBufferViews.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS ws.send(createSharedArrayBufferView()) threw exception TypeError: Failed to execute 'send' on 'WebSocket': The provided ArrayBufferView value must not be shared..
+PASS PASS: Message #0.
+PASS PASS: Message #1.
+PASS PASS: Message #2.
+PASS closeEvent.wasClean is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/sensor/README.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/sensor/README.txt
new file mode 100644
index 0000000..de49eb0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/sensor/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in --js-flags=--harmony-sharedarraybuffer
+# This enables the SharedArrayBuffer language feature in V8.
+# See https://github.com/tc39/ecmascript_sharedmem for more information.
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/webaudio/README.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/webaudio/README.txt
new file mode 100644
index 0000000..de49eb0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/webaudio/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in --js-flags=--harmony-sharedarraybuffer
+# This enables the SharedArrayBuffer language feature in V8.
+# See https://github.com/tc39/ecmascript_sharedmem for more information.
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/webaudio/dom-exceptions-expected.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/webaudio/dom-exceptions-expected.txt
new file mode 100644
index 0000000..82728fbe
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/webaudio/dom-exceptions-expected.txt
@@ -0,0 +1,250 @@
+CONSOLE WARNING: line 341: The provided value 'fancy' is not a valid enum value of type ChannelCountMode.
+CONSOLE WARNING: line 347: The provided value 'undefined' is not a valid enum value of type ChannelInterpretation.
+CONSOLE WARNING: line 502: The provided value '9x' is not a valid enum value of type OverSampleType.
+CONSOLE WARNING: line 717: The provided value 'junk' is not a valid enum value of type ChannelCountMode.
+CONSOLE WARNING: line 746: The provided value 'junk' is not a valid enum value of type ChannelCountMode.
+This is a testharness.js-based test.
+PASS # AUDIT TASK RUNNER STARTED. 
+PASS > [initialize] Initialize contexts for testing 
+PASS   context = new AudioContext() did not throw an exception. 
+PASS   otherContext = new AudioContext() did not throw an exception. 
+PASS < [initialize] All assertions passed. (total 2 assertions) 
+PASS > [createBuffer]  
+PASS   context.createBuffer(99, 1, context.sampleRate) threw NotSupportedError: "Failed to execute 'createBuffer' on 'BaseAudioContext': The number of channels provided (99) is outside the range [1, 32].". 
+PASS   context.createBuffer(0, 1, context.sampleRate) threw NotSupportedError: "Failed to execute 'createBuffer' on 'BaseAudioContext': The number of channels provided (0) is outside the range [1, 32].". 
+PASS   context.createBuffer(1, 1, 1) threw NotSupportedError: "Failed to execute 'createBuffer' on 'BaseAudioContext': The sample rate provided (1) is outside the range [3000, 384000].". 
+PASS   context.createBuffer(1, 1, 2999) threw NotSupportedError: "Failed to execute 'createBuffer' on 'BaseAudioContext': The sample rate provided (2999) is outside the range [3000, 384000].". 
+PASS   context.createBuffer(1, 1, 384001) threw NotSupportedError: "Failed to execute 'createBuffer' on 'BaseAudioContext': The sample rate provided (384001) is outside the range [3000, 384000].". 
+PASS   context.createBuffer(1, 1, 1e6) threw NotSupportedError: "Failed to execute 'createBuffer' on 'BaseAudioContext': The sample rate provided (1.00000e+6) is outside the range [3000, 384000].". 
+PASS   context.createBuffer(1, 1, 3000) did not throw an exception. 
+PASS   context.createBuffer(1, 1, 192000) did not throw an exception. 
+PASS   context.createBuffer(1, 1, 384000) did not throw an exception. 
+PASS   context.createBuffer(1, 0, context.sampleRate) threw NotSupportedError: "Failed to execute 'createBuffer' on 'BaseAudioContext': The number of frames provided (0) is less than or equal to the minimum bound (0).". 
+PASS   context.createBuffer(new ArrayBuffer(100), true) threw TypeError: "Failed to execute 'createBuffer' on 'BaseAudioContext': 3 arguments required, but only 2 present.". 
+PASS < [createBuffer] All assertions passed. (total 11 assertions) 
+PASS > [createMediaElementSource]  
+PASS   context.createMediaElementSource(null) threw TypeError: "Failed to execute 'createMediaElementSource' on 'BaseAudioContext': parameter 1 is not of type 'HTMLMediaElement'.". 
+PASS < [createMediaElementSource] All assertions passed. (total 1 assertions) 
+PASS > [createMediaStreamSource]  
+PASS   context.createMediaStreamSource(null) threw TypeError: "Failed to execute 'createMediaStreamSource' on 'BaseAudioContext': parameter 1 is not of type 'MediaStream'.". 
+PASS < [createMediaStreamSource] All assertions passed. (total 1 assertions) 
+PASS > [createScriptProcessor]  
+PASS   context.createScriptProcessor(1, 1, 1) threw IndexSizeError: "Failed to execute 'createScriptProcessor' on 'BaseAudioContext': buffer size (1) must be 0 or a power of two between 256 and 16384.". 
+PASS   context.createScriptProcessor(4096, 100, 1) threw IndexSizeError: "Failed to execute 'createScriptProcessor' on 'BaseAudioContext': number of input channels (100) exceeds maximum (32).". 
+PASS   context.createScriptProcessor(4096, 1, 100) threw IndexSizeError: "Failed to execute 'createScriptProcessor' on 'BaseAudioContext': number of output channels (100) exceeds maximum (32).". 
+PASS   context.createScriptProcessor() did not throw an exception. 
+PASS   context.createScriptProcessor(0) did not throw an exception. 
+PASS < [createScriptProcessor] All assertions passed. (total 5 assertions) 
+PASS > [createChannelSplitter]  
+PASS   context.createChannelSplitter(0) threw IndexSizeError: "Failed to execute 'createChannelSplitter' on 'BaseAudioContext': The number of outputs provided (0) is outside the range [1, 32].". 
+PASS   context.createChannelSplitter(99) threw IndexSizeError: "Failed to execute 'createChannelSplitter' on 'BaseAudioContext': The number of outputs provided (99) is outside the range [1, 32].". 
+PASS   context.createChannelMerger(0) threw IndexSizeError: "Failed to execute 'createChannelMerger' on 'BaseAudioContext': The number of inputs provided (0) is outside the range [1, 32].". 
+PASS < [createChannelSplitter] All assertions passed. (total 3 assertions) 
+PASS > [createChannelMerger]  
+PASS   context.createChannelMerger(99) threw IndexSizeError: "Failed to execute 'createChannelMerger' on 'BaseAudioContext': The number of inputs provided (99) is outside the range [1, 32].". 
+PASS < [createChannelMerger] All assertions passed. (total 1 assertions) 
+PASS > [createPeriodicWave]  
+PASS   context.createPeriodicWave(null, null) threw TypeError: "Failed to execute 'createPeriodicWave' on 'BaseAudioContext': parameter 1 is not of type 'Float32Array'.". 
+PASS   context.createPeriodicWave(new Float32Array(10), null) threw TypeError: "Failed to execute 'createPeriodicWave' on 'BaseAudioContext': parameter 2 is not of type 'Float32Array'.". 
+PASS   context.createPeriodicWave(new Float32Array(4100), new Float32Array(4100)) did not throw an exception. 
+PASS   context.createPeriodicWave(new Float32Array(8192), new Float32Array(8192)) did not throw an exception. 
+PASS   context.createPeriodicWave(new Float32Array(10000), new Float32Array(10000)) did not throw an exception. 
+PASS   context.createPeriodicWave(new Float32Array(10), new Float32Array(7)) threw IndexSizeError: "Failed to execute 'createPeriodicWave' on 'BaseAudioContext': length of real array (10) and length of imaginary array (7) must match.". 
+PASS   context.createPeriodicWave(shared_view, nonshared_view) threw TypeError: "Failed to execute 'createPeriodicWave' on 'BaseAudioContext': The provided ArrayBufferView value must not be shared.". 
+PASS   context.createPeriodicWave(nonshared_view, shared_view) threw TypeError: "Failed to execute 'createPeriodicWave' on 'BaseAudioContext': The provided ArrayBufferView value must not be shared.". 
+PASS < [createPeriodicWave] All assertions passed. (total 8 assertions) 
+PASS > [createAnalyser]  
+PASS   AnalyserNode.fftSize = 42 threw IndexSizeError: "Failed to set the 'fftSize' property on 'AnalyserNode': The value provided (42) is not a power of two.". 
+PASS   AnalyserNode.fftSize is not equal to 42. 
+PASS   AnalyserNode.fftSize = 16 threw IndexSizeError: "Failed to set the 'fftSize' property on 'AnalyserNode': The FFT size provided (16) is outside the range [32, 32768].". 
+PASS   AnalyserNode.fftSize is not equal to 16. 
+PASS   AnalyserNode.fftSize = 32768 did not throw an exception. 
+PASS   AnalyserNode.fftSize = 65536 threw IndexSizeError: "Failed to set the 'fftSize' property on 'AnalyserNode': The FFT size provided (65536) is outside the range [32, 32768].". 
+PASS   AnalyserNode.fftSize is not equal to 65536. 
+PASS   AnalyserNode.minDecibels = -10 threw IndexSizeError: "Failed to set the 'minDecibels' property on 'AnalyserNode': The minDecibels provided (-10) is greater than the maximum bound (-30).". 
+PASS   AnalyserNode.minDecibels is not equal to -10. 
+PASS   AnalyserNode.maxDecibels = -150 threw IndexSizeError: "Failed to set the 'maxDecibels' property on 'AnalyserNode': The maxDecibels provided (-150) is less than the minimum bound (-100).". 
+PASS   AnalyserNode.maxDecibels is not equal to -150. 
+PASS   AnalyserNode.minDecibels = -30 threw IndexSizeError: "Failed to set the 'minDecibels' property on 'AnalyserNode': The minDecibels provided (-30) is greater than or equal to the maximum bound (-30).". 
+PASS   AnalyserNode.minDecibels is not equal to -30. 
+PASS   AnalyserNode.maxDecibels = -100 threw IndexSizeError: "Failed to set the 'maxDecibels' property on 'AnalyserNode': The maxDecibels provided (-100) is less than or equal to the minimum bound (-100).". 
+PASS   AnalyserNode.maxDecibels is not equal to -100. 
+PASS   AnalyserNode.smoothingTimeConstant = -0.1 threw IndexSizeError: "Failed to set the 'smoothingTimeConstant' property on 'AnalyserNode': The smoothing value provided (-0.1) is outside the range [0, 1].". 
+PASS   AnalyserNode.smoothingTimeConstant is not equal to -0.1. 
+PASS   AnalyserNode.smoothingTimeConstant = 1.5 threw IndexSizeError: "Failed to set the 'smoothingTimeConstant' property on 'AnalyserNode': The smoothing value provided (1.5) is outside the range [0, 1].". 
+PASS   AnalyserNode.smoothingTimeConstant is not equal to 1.5. 
+PASS   AnalyserNode.getFloatFrequencyData(null) threw TypeError: "Failed to execute 'getFloatFrequencyData' on 'AnalyserNode': parameter 1 is not of type 'Float32Array'.". 
+PASS   AnalyserNode.getByteFrequencyData(null) threw TypeError: "Failed to execute 'getByteFrequencyData' on 'AnalyserNode': parameter 1 is not of type 'Uint8Array'.". 
+PASS   AnalyserNode.getFloatTimeDomainData(null) threw TypeError: "Failed to execute 'getFloatTimeDomainData' on 'AnalyserNode': parameter 1 is not of type 'Float32Array'.". 
+PASS   AnalyserNode.getByteTimeDomainData(null) threw TypeError: "Failed to execute 'getByteTimeDomainData' on 'AnalyserNode': parameter 1 is not of type 'Uint8Array'.". 
+PASS   AnalyserNode.getFloatFrequencyData(SharedArrayBuffer view) threw TypeError: "Failed to execute 'getFloatFrequencyData' on 'AnalyserNode': The provided ArrayBufferView value must not be shared.". 
+PASS   AnalyserNode.getByteFrequencyData(SharedArrayBuffer view) threw TypeError: "Failed to execute 'getByteFrequencyData' on 'AnalyserNode': The provided ArrayBufferView value must not be shared.". 
+PASS   AnalyserNode.getFloatTimeDomainData(SharedArrayBuffer view) threw TypeError: "Failed to execute 'getFloatTimeDomainData' on 'AnalyserNode': The provided ArrayBufferView value must not be shared.". 
+PASS   AnalyserNode.getByteTimeDomainData(SharedArrayBuffer view) threw TypeError: "Failed to execute 'getByteTimeDomainData' on 'AnalyserNode': The provided ArrayBufferView value must not be shared.". 
+PASS   AudioBuffer.getChannelData(2) threw IndexSizeError: "Failed to execute 'getChannelData' on 'AudioBuffer': channel index (2) exceeds number of channels (1)". 
+PASS < [createAnalyser] All assertions passed. (total 28 assertions) 
+PASS > [Init test nodes] Create test nodes for the following tests 
+PASS   node = context.createGain() did not throw an exception. 
+PASS   node2 = context.createGain() did not throw an exception. 
+PASS < [Init test nodes] All assertions passed. (total 2 assertions) 
+PASS > [connections] AudioNode connections 
+PASS   node.connect(null, 0, 0) threw TypeError: "Failed to execute 'connect' on 'AudioNode': parameter 1 is not of type 'AudioNode'.". 
+PASS   node.connect(context.destination, 100, 0) threw IndexSizeError: "Failed to execute 'connect' on 'AudioNode': output index (100) exceeds number of outputs (1).". 
+PASS   node.connect(context.destination, 0, 100) threw IndexSizeError: "Failed to execute 'connect' on 'AudioNode': input index (100) exceeds number of inputs (1).". 
+PASS   node.connect(node2.gain, 100) threw IndexSizeError: "Failed to execute 'connect' on 'AudioNode': output index (100) exceeds number of outputs (1).". 
+PASS   node.disconnect(99) threw IndexSizeError: "Failed to execute 'disconnect' on 'AudioNode': The output index provided (99) is outside the range [0, 0].". 
+PASS   node.connect(otherContext.destination) threw InvalidAccessError: "Failed to execute 'connect' on 'AudioNode': cannot connect to a destination belonging to a different audio context.". 
+PASS < [connections] All assertions passed. (total 6 assertions) 
+PASS > [channel-stuff] channelCount, channelCountMode, channelInterpretation 
+PASS   GainNode.channelCount = 99 threw NotSupportedError: "Failed to set the 'channelCount' property on 'AudioNode': The channel count provided (99) is outside the range [1, 32].". 
+PASS   GainNode.channelCount is not equal to 99. 
+PASS   node.channelCountMode = "fancy" did not throw an exception. 
+PASS   node.channelCountMode is equal to max. 
+PASS   node.channelInterpretation = mode did not throw an exception. 
+PASS   node.channelInterpretation is equal to speakers. 
+PASS   context.destination.channelCount = 99 threw IndexSizeError: [error message omitted]. 
+PASS < [channel-stuff] All assertions passed. (total 7 assertions) 
+PASS > [audioparam]  
+PASS   param.setValueCurveAtTime(null, 0, 0) threw TypeError: "Failed to execute 'setValueCurveAtTime' on 'AudioParam': parameter 1 is not of type 'Float32Array'.". 
+PASS   param.setValueCurveAtTime(SharedArrayBuffer view, 0, 0) threw TypeError: "Failed to execute 'setValueCurveAtTime' on 'AudioParam': The provided ArrayBufferView value must not be shared.". 
+PASS   node.gain.exponentialRampToValueAtTime(-1, 0.1) did not throw an exception. 
+PASS   node.gain.exponentialRampToValueAtTime(0, 0.1) threw InvalidAccessError: "Failed to execute 'exponentialRampToValueAtTime' on 'AudioParam': The float target value provided (0) should not be in the range (-1.40130e-45, 1.40130e-45).". 
+PASS   node.gain.exponentialRampToValueAtTime(1e-100, 0.1) threw InvalidAccessError: "Failed to execute 'exponentialRampToValueAtTime' on 'AudioParam': The float target value provided (0) should not be in the range (-1.40130e-45, 1.40130e-45).". 
+PASS   node.gain.exponentialRampToValueAtTime(Math.pow(2, -149), 0.1) did not throw an exception. 
+PASS   node.gain.exponentialRampToValueAtTime(Math.pow(2, -150), 0.1) threw InvalidAccessError: "Failed to execute 'exponentialRampToValueAtTime' on 'AudioParam': The float target value provided (0) should not be in the range (-1.40130e-45, 1.40130e-45).". 
+PASS < [audioparam] All assertions passed. (total 7 assertions) 
+PASS > [biquad]  
+PASS   node.getFrequencyResponse(new Float32Array(1), new Float32Array(1), new Float32Array(1)) did not throw an exception. 
+PASS   node.getFrequencyResponse(null, new Float32Array(1), new Float32Array(1)) threw TypeError: "Failed to execute 'getFrequencyResponse' on 'BiquadFilterNode': parameter 1 is not of type 'Float32Array'.". 
+PASS   node.getFrequencyResponse(new Float32Array(1), null, new Float32Array(1)) threw TypeError: "Failed to execute 'getFrequencyResponse' on 'BiquadFilterNode': parameter 2 is not of type 'Float32Array'.". 
+PASS   node.getFrequencyResponse(new Float32Array(1), new Float32Array(1), null) threw TypeError: "Failed to execute 'getFrequencyResponse' on 'BiquadFilterNode': parameter 3 is not of type 'Float32Array'.". 
+PASS   node.getFrequencyResponse(shared_view, nonshared_view, nonshared_view) threw TypeError: "Failed to execute 'getFrequencyResponse' on 'BiquadFilterNode': The provided ArrayBufferView value must not be shared.". 
+PASS   node.getFrequencyResponse(nonshared_view, shared_view, nonshared_view) threw TypeError: "Failed to execute 'getFrequencyResponse' on 'BiquadFilterNode': The provided ArrayBufferView value must not be shared.". 
+PASS   node.getFrequencyResponse(nonshared_view, nonshared_view, shared_view) threw TypeError: "Failed to execute 'getFrequencyResponse' on 'BiquadFilterNode': The provided ArrayBufferView value must not be shared.". 
+PASS < [biquad] All assertions passed. (total 7 assertions) 
+PASS > [offline-audio-context]  
+PASS   new OfflineAudioContext(32, 100, context.sampleRate) did not throw an exception. 
+PASS   new OfflineAudioContext(0, 100, context.sampleRate) threw NotSupportedError: "Failed to construct 'OfflineAudioContext': The number of channels provided (0) is outside the range [1, 32].". 
+PASS   new OfflineAudioContext(99, 100, context.sampleRate) threw NotSupportedError: "Failed to construct 'OfflineAudioContext': The number of channels provided (99) is outside the range [1, 32].". 
+PASS   new OfflineAudioContext(1, 100, 1) threw NotSupportedError: "Failed to construct 'OfflineAudioContext': The sampleRate provided (1) is outside the range [3000, 384000].". 
+PASS   new OfflineAudioContext(1, 100, 1e6) threw NotSupportedError: "Failed to construct 'OfflineAudioContext': The sampleRate provided (1.00000e+6) is outside the range [3000, 384000].". 
+PASS   new OfflineAudioContext(1, -88200000000000, 44100) threw NotSupportedError: "Failed to construct 'OfflineAudioContext': OfflineAudioContext(1, 1448390656, 44100)". 
+PASS   new OfflineAudioContext(1, 0, 44100) threw NotSupportedError: "Failed to construct 'OfflineAudioContext': The number of frames provided (0) is less than the minimum bound (1).". 
+PASS < [offline-audio-context] All assertions passed. (total 7 assertions) 
+PASS > [waveshaper]  
+PASS   node.oversample = "9x" did not throw an exception. 
+PASS   node.oversample is equal to none. 
+PASS   node.curve = {} threw TypeError: "Failed to set the 'curve' property on 'WaveShaperNode': The provided value is not of type 'Float32Array'.". 
+PASS   node.curve = new Float32Array(1) threw InvalidAccessError: "Failed to set the 'curve' property on 'WaveShaperNode': The curve length provided (1) is less than the minimum bound (2).". 
+PASS   node.curve is equal to ${expected}. 
+PASS   node.curve = new Float32Array(2) did not throw an exception. 
+PASS   node.curve = null did not throw an exception. 
+PASS < [waveshaper] All assertions passed. (total 7 assertions) 
+PASS > [audio-buffer-source] AudioBufferSource start/stop 
+PASS   source = context.createBufferSource() did not throw an exception. 
+PASS   source.buffer = buffer did not throw an exception. 
+PASS   source.buffer = context.createBuffer(1, 10, context.sampleRate) threw InvalidStateError: "Failed to set the 'buffer' property on 'AudioBufferSourceNode': Cannot set buffer after it has been already been set". 
+PASS   source.start(-1) threw InvalidAccessError: "Failed to execute 'start' on 'AudioBufferSourceNode': The start time provided (-1) is less than the minimum bound (0).". 
+PASS   source.start(Infinity) threw TypeError: "Failed to execute 'start' on 'AudioBufferSourceNode': The provided double value is non-finite.". 
+PASS   source.start(-Infinity) threw TypeError: "Failed to execute 'start' on 'AudioBufferSourceNode': The provided double value is non-finite.". 
+PASS   source.start(NaN) threw TypeError: "Failed to execute 'start' on 'AudioBufferSourceNode': The provided double value is non-finite.". 
+PASS   source.start(1, Infinity) threw TypeError: "Failed to execute 'start' on 'AudioBufferSourceNode': The provided double value is non-finite.". 
+PASS   source.start(1, -Infinity) threw TypeError: "Failed to execute 'start' on 'AudioBufferSourceNode': The provided double value is non-finite.". 
+PASS   source.start(1, NaN) threw TypeError: "Failed to execute 'start' on 'AudioBufferSourceNode': The provided double value is non-finite.". 
+PASS   source.start(1, -1) threw InvalidStateError: "Failed to execute 'start' on 'AudioBufferSourceNode': The offset provided (-1) is less than the minimum bound (0).". 
+PASS   source.start(1, -Number.MIN_VALUE) threw InvalidStateError: "Failed to execute 'start' on 'AudioBufferSourceNode': The offset provided (-4.94066e-324) is less than the minimum bound (0).". 
+PASS   source.start(1, 1, Infinity) threw TypeError: "Failed to execute 'start' on 'AudioBufferSourceNode': The provided double value is non-finite.". 
+PASS   source.start(1, 1, -Infinity) threw TypeError: "Failed to execute 'start' on 'AudioBufferSourceNode': The provided double value is non-finite.". 
+PASS   source.start(1, 1, NaN) threw TypeError: "Failed to execute 'start' on 'AudioBufferSourceNode': The provided double value is non-finite.". 
+PASS   source.start(1, 1, -1) threw InvalidStateError: "Failed to execute 'start' on 'AudioBufferSourceNode': The duration provided (-1) is less than the minimum bound (0).". 
+PASS   source.start(1, 1, -Number.MIN_VALUE) threw InvalidStateError: "Failed to execute 'start' on 'AudioBufferSourceNode': The duration provided (-4.94066e-324) is less than the minimum bound (0).". 
+PASS   source.start() did not throw an exception. 
+PASS   source.stop(-Number.MIN_VALUE) threw InvalidAccessError: "Failed to execute 'stop' on 'AudioScheduledSourceNode': The stop time provided (-4.94066e-324) is less than the minimum bound (0).". 
+PASS   source.stop(Infinity) threw TypeError: "Failed to execute 'stop' on 'AudioScheduledSourceNode': The provided double value is non-finite.". 
+PASS   source.stop(-Infinity) threw TypeError: "Failed to execute 'stop' on 'AudioScheduledSourceNode': The provided double value is non-finite.". 
+PASS   source.stop(NaN) threw TypeError: "Failed to execute 'stop' on 'AudioScheduledSourceNode': The provided double value is non-finite.". 
+PASS   source.stop() did not throw an exception. 
+PASS   source2 = context.createBufferSource() did not throw an exception. 
+PASS   source2.buffer = buffer did not throw an exception. 
+PASS   source2.start(0, 0) did not throw an exception. 
+PASS   source3 = context.createBufferSource() did not throw an exception. 
+PASS   source3.buffer = buffer did not throw an exception. 
+PASS   source3.start(0, -1/Infinity) did not throw an exception. 
+PASS   source4 = context.createBufferSource() did not throw an exception. 
+PASS   source4.start() did not throw an exception. 
+PASS   source5 = context.createBufferSource() did not throw an exception. 
+PASS   source5.buffer = buffer did not throw an exception. 
+PASS   source5.stop() threw InvalidStateError: "Failed to execute 'stop' on 'AudioScheduledSourceNode': cannot call stop without calling start first.". 
+PASS   source6 = context.createBufferSource() did not throw an exception. 
+PASS   source6.buffer = buffer did not throw an exception. 
+PASS   source6.start() did not throw an exception. 
+PASS   source6.start() threw InvalidStateError: "Failed to execute 'start' on 'AudioBufferSourceNode': cannot call start more than once.". 
+PASS   source7 = context.createBufferSource() did not throw an exception. 
+PASS   source7.buffer = buffer did not throw an exception. 
+PASS   source7.start() did not throw an exception. 
+PASS   source7.stop() did not throw an exception. 
+PASS < [audio-buffer-source] All assertions passed. (total 42 assertions) 
+PASS > [oscillator] start/stop 
+PASS   source8 = context.createOscillator() did not throw an exception. 
+PASS   source8.start(-Number.MIN_VALUE) threw InvalidAccessError: "Failed to execute 'start' on 'AudioScheduledSourceNode': The start time provided (-4.94066e-324) is less than the minimum bound (0).". 
+PASS   source8.start(Infinity) threw TypeError: "Failed to execute 'start' on 'AudioScheduledSourceNode': The provided double value is non-finite.". 
+PASS   source8.start(-Infinity) threw TypeError: "Failed to execute 'start' on 'AudioScheduledSourceNode': The provided double value is non-finite.". 
+PASS   source8.start(NaN) threw TypeError: "Failed to execute 'start' on 'AudioScheduledSourceNode': The provided double value is non-finite.". 
+PASS   source8.start() did not throw an exception. 
+PASS   source8.stop(-Number.MIN_VALUE) threw InvalidAccessError: "Failed to execute 'stop' on 'AudioScheduledSourceNode': The stop time provided (-4.94066e-324) is less than the minimum bound (0).". 
+PASS   source8.stop(Infinity) threw TypeError: "Failed to execute 'stop' on 'AudioScheduledSourceNode': The provided double value is non-finite.". 
+PASS   source8.stop(-Infinity) threw TypeError: "Failed to execute 'stop' on 'AudioScheduledSourceNode': The provided double value is non-finite.". 
+PASS   source8.stop(NaN) threw TypeError: "Failed to execute 'stop' on 'AudioScheduledSourceNode': The provided double value is non-finite.". 
+PASS   source8.stop() did not throw an exception. 
+PASS   osc = context.createOscillator() did not throw an exception. 
+PASS   osc.stop() threw InvalidStateError: "Failed to execute 'stop' on 'AudioScheduledSourceNode': cannot call stop without calling start first.". 
+PASS   osc1 = context.createOscillator() did not throw an exception. 
+PASS   osc1.start() did not throw an exception. 
+PASS   osc1.stop() did not throw an exception. 
+PASS   osc.setPeriodicWave(null) threw TypeError: "Failed to execute 'setPeriodicWave' on 'OscillatorNode': parameter 1 is not of type 'PeriodicWave'.". 
+PASS < [oscillator] All assertions passed. (total 17 assertions) 
+PASS > [convolver]  
+PASS   oc = new OfflineAudioContext(1, 44100, 44100) did not throw an exception. 
+PASS   conv = oc.createConvolver() did not throw an exception. 
+PASS   conv.buffer = {} threw TypeError: "Failed to set the 'buffer' property on 'ConvolverNode': The provided value is not of type 'AudioBuffer'.". 
+PASS   conv.buffer = oc.createBuffer(1, 100, 22050) threw NotSupportedError: "Failed to set the 'buffer' property on 'ConvolverNode': The buffer sample rate of 22050 does not match the context rate of 44100 Hz.". 
+PASS   conv.buffer is equal to ${expected}. 
+PASS < [convolver] All assertions passed. (total 5 assertions) 
+PASS > [panner]  
+PASS   panner.channelCount = 1 did not throw an exception. 
+PASS   panner.channelCount = 2 did not throw an exception. 
+PASS   PannerNode.channelCount = 0 threw NotSupportedError: "Failed to set the 'channelCount' property on 'AudioNode': The channelCount provided (0) is outside the range [1, 2].". 
+PASS   PannerNode.channelCount is not equal to 0. 
+PASS   PannerNode.channelCount = 3 threw NotSupportedError: "Failed to set the 'channelCount' property on 'AudioNode': The channelCount provided (3) is outside the range [1, 2].". 
+PASS   PannerNode.channelCount is not equal to 3. 
+PASS   PannerNode.channelCountMode = max threw NotSupportedError: "Failed to set the 'channelCountMode' property on 'AudioNode': Panner: 'max' is not allowed". 
+PASS   PannerNode.channelCountMode is not equal to max. 
+PASS   panner.channelCountMode = "explicit" did not throw an exception. 
+PASS   panner.channelCountMode = "clamped-max" did not throw an exception. 
+PASS   panner.channelCountMode = "junk" did not throw an exception. 
+PASS < [panner] All assertions passed. (total 11 assertions) 
+PASS > [script-processor]  
+PASS   script = context.createScriptProcessor(256, 3) did not throw an exception. 
+PASS   script.channelCount is equal to 3. 
+PASS   script.channelCountMode is equal to explicit. 
+PASS   script.channelCount = 3 did not throw an exception. 
+PASS   ScriptProcessorNode.channelCount = 1 threw NotSupportedError: "Failed to set the 'channelCount' property on 'AudioNode': channelCount cannot be changed from 3 to 1". 
+PASS   ScriptProcessorNode.channelCount is not equal to 1. 
+PASS   ScriptProcessorNode.channelCount = 7 threw NotSupportedError: "Failed to set the 'channelCount' property on 'AudioNode': channelCount cannot be changed from 3 to 7". 
+PASS   ScriptProcessorNode.channelCount is not equal to 7. 
+PASS   script.channelCountMode = "explicit" did not throw an exception. 
+PASS   ScriptProcessorNode.channelCountMode = max threw NotSupportedError: "Failed to set the 'channelCountMode' property on 'AudioNode': channelCountMode cannot be changed from 'explicit' to 'max'". 
+PASS   ScriptProcessorNode.channelCountMode is not equal to max. 
+PASS   ScriptProcessorNode.channelCountMode = clamped-max threw NotSupportedError: "Failed to set the 'channelCountMode' property on 'AudioNode': channelCountMode cannot be changed from 'explicit' to 'clamped-max'". 
+PASS   ScriptProcessorNode.channelCountMode is not equal to clamped-max. 
+PASS   script.channelCountMode = "junk" did not throw an exception. 
+PASS < [script-processor] All assertions passed. (total 14 assertions) 
+PASS > [misc] Miscellaneous tests 
+PASS   osc.noteOn is equal to undefined. 
+PASS   osc.noteOff is equal to undefined. 
+PASS   source.noteOn is equal to undefined. 
+PASS   source.noteOff is equal to undefined. 
+PASS < [misc] All assertions passed. (total 4 assertions) 
+PASS # AUDIT TASK RUNNER FINISHED: 22 tasks ran successfully. 
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/webmidi/README.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/webmidi/README.txt
new file mode 100644
index 0000000..de49eb0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/webmidi/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in --js-flags=--harmony-sharedarraybuffer
+# This enables the SharedArrayBuffer language feature in V8.
+# See https://github.com/tc39/ecmascript_sharedmem for more information.
diff --git a/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/webmidi/send-messages-expected.txt b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/webmidi/send-messages-expected.txt
new file mode 100644
index 0000000..27b298d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/sharedarraybuffer/webmidi/send-messages-expected.txt
@@ -0,0 +1,48 @@
+Test if various kinds of MIDI messages can be validated.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS navigator.requestMIDIAccess is defined.
+PASS output.send([0x00, 0x01]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Running status is not allowed at index 0 (0)..
+PASS output.send([0xf7]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Unexpected end of system exclusive message at index 0 (247)..
+PASS output.send([0xf4]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Reserved status is not allowed at index 0 (244)..
+PASS output.send([0xf5]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Reserved status is not allowed at index 0 (245)..
+PASS output.send([0xf9]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Reserved status is not allowed at index 0 (249)..
+PASS output.send([0xfd]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Reserved status is not allowed at index 0 (253)..
+PASS output.send([0x80]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Message is incomplete..
+PASS output.send([0x80, 0x00]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Message is incomplete..
+PASS output.send([0x90]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Message is incomplete..
+PASS output.send([0x90, 0x00]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Message is incomplete..
+PASS output.send([0xa0]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Message is incomplete..
+PASS output.send([0xa0, 0x00]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Message is incomplete..
+PASS output.send([0xb0]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Message is incomplete..
+PASS output.send([0xb0, 0x00]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Message is incomplete..
+PASS output.send([0xc0]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Message is incomplete..
+PASS output.send([0xd0]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Message is incomplete..
+PASS output.send([0xe0]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Message is incomplete..
+PASS output.send([0xe0, 0x00]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Message is incomplete..
+PASS output.send([0xf1]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Message is incomplete..
+PASS output.send([0xf2]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Message is incomplete..
+PASS output.send([0xf2, 0x00]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Message is incomplete..
+PASS output.send([0xf3]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Message is incomplete..
+PASS output.send([0x80, 0x80, 0x00]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Unexpected status byte at index 1 (128)..
+PASS output.send([0x80, 0x00, 0x80]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Unexpected status byte at index 2 (128)..
+PASS output.send([0xf0, 0x80, 0xf7]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': System exclusive message contains a status byte at index 1 (128)..
+PASS output.send([0xf0, 0xf0, 0xf7]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': System exclusive message contains a status byte at index 1 (240)..
+PASS output.send([0xf0, 0xff, 0xf7, 0xf7]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Unexpected end of system exclusive message at index 3 (247)..
+PASS output.send([0xf4, 0x80, 0x00, 0x00]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Reserved status is not allowed at index 0 (244)..
+PASS output.send([0x80, 0xf4, 0x00, 0x00]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Unexpected status byte at index 1 (244)..
+PASS output.send([0x80, 0x00, 0xf4, 0x00]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Unexpected status byte at index 2 (244)..
+PASS output.send([0x80, 0x00, 0x00, 0xf4]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Reserved status is not allowed at index 3 (244)..
+PASS output.send([0xf0, 0xff, 0xf4, 0xf7]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': System exclusive message contains a status byte at index 2 (244)..
+PASS output.send([], NaN) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': The provided double value is non-finite..
+PASS output.send([], Infinity) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': The provided double value is non-finite..
+PASS output.send(new Uint8Array(), NaN) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': The provided double value is non-finite..
+PASS output.send(new Uint8Array(), Infinity) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': The provided double value is non-finite..
+PASS output.send(new Uint8Array(new SharedArrayBuffer(4))) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': The provided ArrayBufferView value must not be shared..
+PASS output.send(new Uint8Array(new SharedArrayBuffer(4), 0)) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': The provided ArrayBufferView value must not be shared..
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/third_party/WebKit/LayoutTests/webaudio/AudioBuffer/audiobuffer-copy-channel.html b/third_party/WebKit/LayoutTests/webaudio/AudioBuffer/audiobuffer-copy-channel.html
index 2b1db0a..dfd55bdd 100644
--- a/third_party/WebKit/LayoutTests/webaudio/AudioBuffer/audiobuffer-copy-channel.html
+++ b/third_party/WebKit/LayoutTests/webaudio/AudioBuffer/audiobuffer-copy-channel.html
@@ -146,6 +146,19 @@
           }, "7: buffer.copyFromChannel(x, 3)")
           .throw("IndexSizeError");
 
+        if (window.SharedArrayBuffer) {
+          var shared_buffer = new Float32Array(new SharedArrayBuffer(32));
+          should(() => {
+              buffer.copyFromChannel(shared_buffer, 0);
+            }, "8: buffer.copyFromChannel(SharedArrayBuffer view, 0)")
+            .throw("TypeError");
+
+          should(() => {
+              buffer.copyFromChannel(shared_buffer, 0, 0);
+            }, "9: buffer.copyFromChannel(SharedArrayBuffer view, 0, 0)")
+            .throw("TypeError");
+        }
+
         task.done();
       });
 
@@ -184,6 +197,19 @@
           }, "6: buffer.copyToChannel(x, 3)")
           .throw("IndexSizeError");
 
+        if (window.SharedArrayBuffer) {
+          var shared_buffer = new Float32Array(new SharedArrayBuffer(32));
+          should(() => {
+              buffer.copyToChannel(shared_buffer, 0);
+            }, "7: buffer.copyToChannel(SharedArrayBuffer view, 0)")
+            .throw("TypeError");
+
+          should(() => {
+              buffer.copyToChannel(shared_buffer, 0, 0);
+            }, "8: buffer.copyToChannel(SharedArrayBuffer view, 0, 0)")
+            .throw("TypeError");
+        }
+
         task.done();
       });
 
diff --git a/third_party/WebKit/LayoutTests/webaudio/IIRFilter/iirfilter-basic.html b/third_party/WebKit/LayoutTests/webaudio/IIRFilter/iirfilter-basic.html
index 43ffe81..9c693e4 100644
--- a/third_party/WebKit/LayoutTests/webaudio/IIRFilter/iirfilter-basic.html
+++ b/third_party/WebKit/LayoutTests/webaudio/IIRFilter/iirfilter-basic.html
@@ -188,6 +188,27 @@
               30));
           }).notThrow() && success;
 
+        if (window.SharedArrayBuffer) {
+          var shared_view = new Float32Array(new SharedArrayBuffer(4));
+          var nonshared_view = new Float32Array(1);
+
+          success = Should(
+              "getFrequencyResponse(shared_view, nonshared_view, nonshared_view)",
+              function () {
+                f.getFrequencyResponse(shared_view, nonshared_view, nonshared_view);
+            }).throw("TypeError") && success;
+          success = Should(
+              "getFrequencyResponse(nonshared_view, shared_view, nonshared_view)",
+              function () {
+                f.getFrequencyResponse(nonshared_view, shared_view, nonshared_view);
+            }).throw("TypeError") && success;
+          success = Should(
+              "getFrequencyResponse(nonshared_view, nonshared_view, shared_view)",
+              function () {
+                f.getFrequencyResponse(nonshared_view, nonshared_view, shared_view);
+            }).throw("TypeError") && success;
+        }
+
         Should("getFrequencyResponse exceptions handled", success)
           .summarize("correctly", "incorrectly");
         done();
diff --git a/third_party/WebKit/LayoutTests/webaudio/constructor/waveshaper.html b/third_party/WebKit/LayoutTests/webaudio/constructor/waveshaper.html
index 40c2624..2968b1c 100644
--- a/third_party/WebKit/LayoutTests/webaudio/constructor/waveshaper.html
+++ b/third_party/WebKit/LayoutTests/webaudio/constructor/waveshaper.html
@@ -96,6 +96,17 @@
         taskDone();
       });
 
+      if (window.SharedArrayBuffer) {
+        audit.defineTask("invalid setCurve", function (taskDone) {
+          var node = new WaveShaperNode(context);
+
+          Should("WaveShaper.curve = SharedArrayBuffer view", function () {
+              node.curve = new Float32Array(new SharedArrayBuffer(16));
+            }).throw("TypeError");
+          taskDone();
+        });
+      }
+
       audit.runTasks();
     </script>
   </body>
diff --git a/third_party/WebKit/LayoutTests/webaudio/dom-exceptions-expected.txt b/third_party/WebKit/LayoutTests/webaudio/dom-exceptions-expected.txt
index b2b85c9..34e9429b 100644
--- a/third_party/WebKit/LayoutTests/webaudio/dom-exceptions-expected.txt
+++ b/third_party/WebKit/LayoutTests/webaudio/dom-exceptions-expected.txt
@@ -1,8 +1,8 @@
-CONSOLE WARNING: line 308: The provided value 'fancy' is not a valid enum value of type ChannelCountMode.
-CONSOLE WARNING: line 314: The provided value 'undefined' is not a valid enum value of type ChannelInterpretation.
-CONSOLE WARNING: line 444: The provided value '9x' is not a valid enum value of type OverSampleType.
-CONSOLE WARNING: line 659: The provided value 'junk' is not a valid enum value of type ChannelCountMode.
-CONSOLE WARNING: line 688: The provided value 'junk' is not a valid enum value of type ChannelCountMode.
+CONSOLE WARNING: line 341: The provided value 'fancy' is not a valid enum value of type ChannelCountMode.
+CONSOLE WARNING: line 347: The provided value 'undefined' is not a valid enum value of type ChannelInterpretation.
+CONSOLE WARNING: line 502: The provided value '9x' is not a valid enum value of type OverSampleType.
+CONSOLE WARNING: line 717: The provided value 'junk' is not a valid enum value of type ChannelCountMode.
+CONSOLE WARNING: line 746: The provided value 'junk' is not a valid enum value of type ChannelCountMode.
 This is a testharness.js-based test.
 PASS # AUDIT TASK RUNNER STARTED. 
 PASS > [initialize] Initialize contexts for testing 
diff --git a/third_party/WebKit/LayoutTests/webaudio/dom-exceptions.html b/third_party/WebKit/LayoutTests/webaudio/dom-exceptions.html
index 436fd83a..72a36d1f 100644
--- a/third_party/WebKit/LayoutTests/webaudio/dom-exceptions.html
+++ b/third_party/WebKit/LayoutTests/webaudio/dom-exceptions.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script> 
+<script src="../resources/testharnessreport.js"></script>
 <script src="resources/audit-util.js"></script>
 <script src="resources/audit.js"></script>
 <script src="resources/biquad-testing.js"></script>
@@ -197,6 +197,20 @@
       'context.createPeriodicWave(new Float32Array(10), new Float32Array(7))')
       .throw('IndexSizeError');
 
+  if (window.SharedArrayBuffer) {
+    let shared_view = new Float32Array(new SharedArrayBuffer(4100 * 4));
+    let nonshared_view = new Float32Array(4100);
+    should(
+        () => context.createPeriodicWave(shared_view, nonshared_view),
+        'context.createPeriodicWave(shared_view, nonshared_view)')
+        .throw('TypeError');
+
+    should(
+        () => context.createPeriodicWave(nonshared_view, shared_view),
+        'context.createPeriodicWave(nonshared_view, shared_view)')
+        .throw('TypeError');
+  }
+
   task.done();
 });
 
@@ -234,6 +248,25 @@
       node.constructor.name + '.getByteTimeDomainData(null)')
       .throw();
 
+  if (window.SharedArrayBuffer) {
+    should(
+        () => node.getFloatFrequencyData(new Float32Array(new SharedArrayBuffer(16))),
+        'AnalyserNode.getFloatFrequencyData(SharedArrayBuffer view)')
+        .throw();
+    should(
+        () => node.getByteFrequencyData(new Uint8Array(new SharedArrayBuffer(16))),
+        node.constructor.name + '.getByteFrequencyData(SharedArrayBuffer view)')
+        .throw();
+    should(
+        () => node.getFloatTimeDomainData(new Float32Array(new SharedArrayBuffer(16))),
+        node.constructor.name + '.getFloatTimeDomainData(SharedArrayBuffer view)')
+        .throw();
+    should(
+        () => node.getByteTimeDomainData(new Uint8Array(new SharedArrayBuffer(16))),
+        node.constructor.name + '.getByteTimeDomainData(SharedArrayBuffer view)')
+        .throw();
+  }
+
   // AudioBuffers
   node = context.createBuffer(1, 1, context.sampleRate);
   // Invalid channel index: IndexSizeError
@@ -336,6 +369,13 @@
       'param.setValueCurveAtTime(null, 0, 0)')
       .throw();
 
+  if (window.SharedArrayBuffer) {
+    should(
+        () => param.setValueCurveAtTime(new Float32Array(new SharedArrayBuffer(16)), 0, 0),
+        'param.setValueCurveAtTime(SharedArrayBuffer view, 0, 0)')
+        .throw();
+  }
+
   // exponentialRampToValue should throw only for "zero" target values.
   should(
       () => node.gain.exponentialRampToValueAtTime(-1, 0.1),
@@ -396,6 +436,24 @@
       'node.getFrequencyResponse(new Float32Array(1), new Float32Array(1), null)')
       .throw();
 
+  if (window.SharedArrayBuffer) {
+    let shared_view = new Float32Array(new SharedArrayBuffer(4));
+    let nonshared_view = new Float32Array(1);
+
+    should(
+        () => node.getFrequencyResponse(shared_view, nonshared_view, nonshared_view),
+        'node.getFrequencyResponse(shared_view, nonshared_view, nonshared_view)')
+        .throw();
+    should(
+        () => node.getFrequencyResponse(nonshared_view, shared_view, nonshared_view),
+        'node.getFrequencyResponse(nonshared_view, shared_view, nonshared_view)')
+        .throw();
+    should(
+        () => node.getFrequencyResponse(nonshared_view, nonshared_view, shared_view),
+        'node.getFrequencyResponse(nonshared_view, nonshared_view, shared_view)')
+        .throw();
+  }
+
   task.done();
 });
 
diff --git a/third_party/WebKit/LayoutTests/webmidi/send-messages.html b/third_party/WebKit/LayoutTests/webmidi/send-messages.html
index 2073dce..53d9367 100644
--- a/third_party/WebKit/LayoutTests/webmidi/send-messages.html
+++ b/third_party/WebKit/LayoutTests/webmidi/send-messages.html
@@ -98,6 +98,11 @@
     shouldThrow('output.send(new Uint8Array(), NaN)');
     shouldThrow('output.send(new Uint8Array(), Infinity)');
 
+    if (window.SharedArrayBuffer) {
+        shouldThrow('output.send(new Uint8Array(new SharedArrayBuffer(4)))');
+        shouldThrow('output.send(new Uint8Array(new SharedArrayBuffer(4), 0))');
+    }
+
     finishJSTest();
 }).catch(function () {
     testFailed("requestMIDIAccess() return an error.");
diff --git a/third_party/WebKit/Source/bindings/core/v8/ExceptionMessages.cpp b/third_party/WebKit/Source/bindings/core/v8/ExceptionMessages.cpp
index c145537..a075ee1 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ExceptionMessages.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ExceptionMessages.cpp
@@ -35,6 +35,10 @@
 
 namespace blink {
 
+String ExceptionMessages::FailedToConvertJSValue(const char* type) {
+  return String::Format("Failed to convert value to '%s'.", type);
+}
+
 String ExceptionMessages::FailedToConstruct(const char* type,
                                             const String& detail) {
   return "Failed to construct '" + String(type) +
diff --git a/third_party/WebKit/Source/bindings/core/v8/ExceptionMessages.h b/third_party/WebKit/Source/bindings/core/v8/ExceptionMessages.h
index 60f020b1..ce6ed05 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ExceptionMessages.h
+++ b/third_party/WebKit/Source/bindings/core/v8/ExceptionMessages.h
@@ -54,6 +54,8 @@
                                             const String& expected_type);
   static String ConstructorNotCallableAsFunction(const char* type);
 
+  static String FailedToConvertJSValue(const char* type);
+
   static String FailedToConstruct(const char* type, const String& detail);
   static String FailedToEnumerate(const char* type, const String& detail);
   static String FailedToExecute(const char* method,
diff --git a/third_party/WebKit/Source/bindings/core/v8/NativeValueTraitsImplTest.cpp b/third_party/WebKit/Source/bindings/core/v8/NativeValueTraitsImplTest.cpp
index b6e750d..062d7ff 100644
--- a/third_party/WebKit/Source/bindings/core/v8/NativeValueTraitsImplTest.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/NativeValueTraitsImplTest.cpp
@@ -31,7 +31,7 @@
         scope.GetIsolate(), v8::Number::New(scope.GetIsolate(), 42),
         exception_state);
     EXPECT_TRUE(exception_state.HadException());
-    EXPECT_EQ("Unable to convert value to Internals.",
+    EXPECT_EQ("Failed to convert value to 'Internals'.",
               exception_state.Message());
     EXPECT_EQ(nullptr, internals);
   }
@@ -42,7 +42,7 @@
             scope.GetIsolate(), v8::Undefined(scope.GetIsolate()),
             exception_state);
     EXPECT_TRUE(exception_state.HadException());
-    EXPECT_EQ("Unable to convert value to TestSequenceCallback.",
+    EXPECT_EQ("Failed to convert value to 'TestSequenceCallback'.",
               exception_state.Message());
     EXPECT_EQ(nullptr, callback_function);
   }
diff --git a/third_party/WebKit/Source/bindings/core/v8/ToV8.h b/third_party/WebKit/Source/bindings/core/v8/ToV8.h
index b443d20..e96c472b 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ToV8.h
+++ b/third_party/WebKit/Source/bindings/core/v8/ToV8.h
@@ -17,6 +17,7 @@
 #include "bindings/core/v8/ScriptWrappable.h"
 #include "bindings/core/v8/V8Binding.h"
 #include "core/CoreExport.h"
+#include "core/dom/NotShared.h"
 #include "platform/heap/Handle.h"
 #include "platform/wtf/Forward.h"
 #include "v8/include/v8.h"
@@ -275,6 +276,13 @@
   return object;
 }
 
+template <typename T>
+inline v8::Local<v8::Value> ToV8(NotShared<T> value,
+                                 v8::Local<v8::Object> creation_context,
+                                 v8::Isolate* isolate) {
+  return ToV8(value.View(), creation_context, isolate);
+}
+
 template <typename Sequence>
 inline v8::Local<v8::Value> ToV8SequenceInternal(
     const Sequence& sequence,
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8Binding.h b/third_party/WebKit/Source/bindings/core/v8/V8Binding.h
index 208eee2..a1f218f 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8Binding.h
+++ b/third_party/WebKit/Source/bindings/core/v8/V8Binding.h
@@ -47,6 +47,7 @@
 #include "bindings/core/v8/V8ThrowException.h"
 #include "bindings/core/v8/V8ValueCache.h"
 #include "core/CoreExport.h"
+#include "core/dom/NotShared.h"
 #include "platform/heap/Handle.h"
 #include "platform/wtf/text/AtomicString.h"
 #include "platform/wtf/text/StringView.h"
@@ -216,6 +217,12 @@
   V8SetReturnValue(callback_info, impl.Get());
 }
 
+template <typename CallbackInfo, typename T>
+inline void V8SetReturnValue(const CallbackInfo& callbackInfo,
+                             NotShared<T> notShared) {
+  V8SetReturnValue(callbackInfo, notShared.View());
+}
+
 template <typename CallbackInfo>
 inline void V8SetReturnValueForMainWorld(const CallbackInfo& callback_info,
                                          ScriptWrappable* impl) {
@@ -334,6 +341,13 @@
   V8SetReturnValue(callback_info, handle);
 }
 
+template <typename CallbackInfo, typename T>
+inline void V8SetReturnValueFast(const CallbackInfo& callbackInfo,
+                                 NotShared<T> notShared,
+                                 const ScriptWrappable* wrappable) {
+  V8SetReturnValueFast(callbackInfo, notShared.View(), wrappable);
+}
+
 // Convert v8::String to a WTF::String. If the V8 string is not already
 // an external string then it is transformed into an external string at this
 // point to avoid repeated conversions.
@@ -1163,6 +1177,25 @@
 CORE_EXPORT v8::Local<v8::Value> FromJSONString(v8::Isolate*,
                                                 const String& stringified_json,
                                                 ExceptionState&);
+
+// Ensure that a typed array value is not backed by a SharedArrayBuffer. If it
+// is, an exception will be thrown. The return value will use the NotShared
+// wrapper type.
+template <typename NotSharedType>
+NotSharedType ToNotShared(v8::Isolate* isolate,
+                          v8::Local<v8::Value> value,
+                          ExceptionState& exception_state) {
+  using DOMTypedArray = typename NotSharedType::TypedArrayType;
+  DOMTypedArray* dom_typed_array =
+      V8TypeOf<DOMTypedArray>::Type::toImplWithTypeCheck(isolate, value);
+  if (dom_typed_array && dom_typed_array->IsShared()) {
+    exception_state.ThrowTypeError(
+        "The provided ArrayBufferView value must not be shared.");
+    return NotSharedType();
+  }
+  return NotSharedType(dom_typed_array);
+}
+
 }  // namespace blink
 
 #endif  // V8Binding_h
diff --git a/third_party/WebKit/Source/bindings/scripts/v8_attributes.py b/third_party/WebKit/Source/bindings/scripts/v8_attributes.py
index ecc6277f..2ab1aecf1 100644
--- a/third_party/WebKit/Source/bindings/scripts/v8_attributes.py
+++ b/third_party/WebKit/Source/bindings/scripts/v8_attributes.py
@@ -286,8 +286,13 @@
             cpp_value, extended_attributes=extended_attributes, script_wrappable='impl',
             for_main_world=for_main_world, is_static=attribute.is_static)
 
+    cpp_value_to_script_wrappable = cpp_value
+    if idl_type.is_array_buffer_view_or_typed_array:
+        cpp_value_to_script_wrappable += '.View()'
+
     context.update({
         'cpp_value': cpp_value,
+        'cpp_value_to_script_wrappable': cpp_value_to_script_wrappable,
         'cpp_value_to_v8_value': idl_type.cpp_value_to_v8_value(
             cpp_value=cpp_value, creation_context='holder',
             extended_attributes=extended_attributes),
@@ -318,7 +323,8 @@
     expression = '%s(%s)' % (getter_name, ', '.join(arguments))
     # Needed to handle getter expressions returning Type& as the
     # use site for |expression| expects Type*.
-    if attribute.idl_type.is_interface_type and len(arguments) == 0:
+    if (attribute.idl_type.is_interface_type and len(arguments) == 0 and
+            not attribute.idl_type.is_array_buffer_view_or_typed_array):
         return 'WTF::GetPtr(%s)' % expression
     return expression
 
diff --git a/third_party/WebKit/Source/bindings/scripts/v8_dictionary.py b/third_party/WebKit/Source/bindings/scripts/v8_dictionary.py
index 66fd18f..92d310d 100644
--- a/third_party/WebKit/Source/bindings/scripts/v8_dictionary.py
+++ b/third_party/WebKit/Source/bindings/scripts/v8_dictionary.py
@@ -231,6 +231,10 @@
     else:
         header_includes.update(idl_type.impl_includes_for_type(interfaces_info))
 
+    setter_value = 'value'
+    if idl_type.is_array_buffer_view_or_typed_array:
+        setter_value += '.View()'
+
     return {
         'cpp_default_value': cpp_default_value,
         'cpp_name': cpp_name,
@@ -245,4 +249,5 @@
         'nullable_indicator_name': nullable_indicator_name,
         'rvalue_cpp_type': idl_type.cpp_type_args(used_as_rvalue_type=True),
         'setter_name': setter_name_for_dictionary_member(member),
+        'setter_value': setter_value,
     }
diff --git a/third_party/WebKit/Source/bindings/scripts/v8_types.py b/third_party/WebKit/Source/bindings/scripts/v8_types.py
index 3e3cf39..32c6b85 100644
--- a/third_party/WebKit/Source/bindings/scripts/v8_types.py
+++ b/third_party/WebKit/Source/bindings/scripts/v8_types.py
@@ -74,6 +74,9 @@
     'Uint16Array',
     'Uint32Array',
 ])
+ARRAY_BUFFER_VIEW_AND_TYPED_ARRAY_TYPES = TYPED_ARRAY_TYPES.union(frozenset([
+    'ArrayBufferView'
+]))
 ARRAY_BUFFER_AND_VIEW_TYPES = TYPED_ARRAY_TYPES.union(frozenset([
     'ArrayBuffer',
     'ArrayBufferView',
@@ -85,6 +88,9 @@
 IdlType.is_array_buffer_or_view = property(
     lambda self: self.base_type in ARRAY_BUFFER_AND_VIEW_TYPES)
 
+IdlType.is_array_buffer_view_or_typed_array = property(
+    lambda self: self.base_type in ARRAY_BUFFER_VIEW_AND_TYPED_ARRAY_TYPES)
+
 IdlType.is_typed_array = property(
     lambda self: self.base_type in TYPED_ARRAY_TYPES)
 
@@ -202,6 +208,9 @@
         return 'FlexibleArrayBufferView'
     if base_idl_type in TYPED_ARRAY_TYPES and 'FlexibleArrayBufferView' in extended_attributes:
         return 'Flexible' + base_idl_type + 'View'
+    if base_idl_type in ARRAY_BUFFER_VIEW_AND_TYPED_ARRAY_TYPES:
+        if not used_in_cpp_sequence:
+            return cpp_template_type('NotShared', idl_type.implemented_as)
     if idl_type.is_interface_type:
         implemented_as_class = idl_type.implemented_as
         if raw_type or (used_as_rvalue_type and idl_type.is_garbage_collected) or not used_in_cpp_sequence:
@@ -343,6 +352,7 @@
 INCLUDES_FOR_TYPE = {
     'object': set(),
     'ArrayBufferView': set(['bindings/core/v8/V8ArrayBufferView.h',
+                            'core/dom/NotShared.h',
                             'core/dom/FlexibleArrayBufferView.h']),
     'Dictionary': set(['bindings/core/v8/Dictionary.h']),
     'EventHandler': set(['bindings/core/v8/V8AbstractEventListener.h',
@@ -459,8 +469,8 @@
         includes_for_type.add(interface_info['include_path'])
     if base_idl_type in INCLUDES_FOR_TYPE:
         includes_for_type.update(INCLUDES_FOR_TYPE[base_idl_type])
-    if idl_type.is_typed_array:
-        return set(['core/dom/DOMTypedArray.h'])
+    if idl_type.is_array_buffer_view_or_typed_array:
+        return set(['core/dom/DOMTypedArray.h', 'core/dom/NotShared.h'])
     return includes_for_type
 
 
@@ -519,6 +529,7 @@
     return (idl_type.is_numeric_type or
             idl_type.is_enum or
             idl_type.is_dictionary or
+            idl_type.is_array_buffer_view_or_typed_array or
             idl_type.name in ('Boolean', 'ByteString', 'Date', 'Dictionary', 'USVString', 'SerializedScriptValue'))
 
 IdlType.v8_conversion_needs_exception_state = property(v8_conversion_needs_exception_state)
@@ -581,7 +592,7 @@
     base_idl_type = idl_type.as_union_type.name if idl_type.is_union_type else idl_type.base_type
 
     if 'FlexibleArrayBufferView' in extended_attributes:
-        if base_idl_type not in TYPED_ARRAY_TYPES.union(set(['ArrayBufferView'])):
+        if base_idl_type not in ARRAY_BUFFER_VIEW_AND_TYPED_ARRAY_TYPES:
             raise ValueError("Unrecognized base type for extended attribute 'FlexibleArrayBufferView': %s" % (idl_type.base_type))
         base_idl_type = 'FlexibleArrayBufferView'
 
@@ -599,10 +610,13 @@
 
     if base_idl_type in V8_VALUE_TO_CPP_VALUE:
         cpp_expression_format = V8_VALUE_TO_CPP_VALUE[base_idl_type]
-    elif idl_type.is_array_buffer_or_view:
+    elif idl_type.name == 'ArrayBuffer':
         cpp_expression_format = (
             '{v8_value}->Is{idl_type}() ? '
             'V8{idl_type}::toImpl(v8::Local<v8::{idl_type}>::Cast({v8_value})) : 0')
+    elif idl_type.is_array_buffer_view_or_typed_array:
+        this_cpp_type = idl_type.cpp_type
+        cpp_expression_format = ('ToNotShared<%s>({isolate}, {v8_value}, exceptionState)' % this_cpp_type)
     elif idl_type.is_union_type:
         nullable = 'UnionTypeConversionMode::kNullable' if idl_type.includes_nullable_type \
             else 'UnionTypeConversionMode::kNotNullable'
@@ -677,7 +691,11 @@
     # meaningful if 'check_expression' is not None.
     return_expression = bailout_return_value
 
-    if idl_type.is_string_type or idl_type.v8_conversion_needs_exception_state:
+    if 'FlexibleArrayBufferView' in extended_attributes:
+        if idl_type.base_type not in ARRAY_BUFFER_VIEW_AND_TYPED_ARRAY_TYPES:
+            raise ValueError("Unrecognized base type for extended attribute 'FlexibleArrayBufferView': %s" % (idl_type.base_type))
+        set_expression = cpp_value
+    elif idl_type.is_string_type or idl_type.v8_conversion_needs_exception_state:
         # Types for which conversion can fail and that need error handling.
 
         check_expression = 'exceptionState.HadException()'
@@ -699,10 +717,6 @@
         return {
             'error_message': 'no V8 -> C++ conversion for IDL type: %s' % idl_type.name
         }
-    elif 'FlexibleArrayBufferView' in extended_attributes:
-        if idl_type.base_type not in TYPED_ARRAY_TYPES.union(set(['ArrayBufferView'])):
-            raise ValueError("Unrecognized base type for extended attribute 'FlexibleArrayBufferView': %s" % (idl_type.base_type))
-        set_expression = cpp_value
     else:
         assign_expression = cpp_value
 
diff --git a/third_party/WebKit/Source/bindings/scripts/v8_union.py b/third_party/WebKit/Source/bindings/scripts/v8_union.py
index 35477a1a..1723495 100644
--- a/third_party/WebKit/Source/bindings/scripts/v8_union.py
+++ b/third_party/WebKit/Source/bindings/scripts/v8_union.py
@@ -129,7 +129,7 @@
         cpp_includes.update(interface_info.get(
             'dependencies_include_paths', []))
         # We need complete types for IDL dictionaries in union containers.
-        if member.is_dictionary or member.is_typed_array:
+        if member.is_dictionary or member.is_array_buffer_view_or_typed_array:
             header_includes.update(member.includes_for_type())
         else:
             cpp_includes.update(member.includes_for_type())
@@ -161,6 +161,7 @@
             creation_context='creationContext'),
         'enum_values': member.enum_values,
         'is_array_buffer_or_view_type': member.is_array_buffer_or_view,
+        'is_array_buffer_view_or_typed_array': member.is_array_buffer_view_or_typed_array,
         'is_traceable': member.is_traceable,
         'rvalue_cpp_type': member.cpp_type_args(used_as_rvalue_type=True),
         'specific_type_enum': 'SpecificType' + member.name,
diff --git a/third_party/WebKit/Source/bindings/templates/attributes.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/attributes.cpp.tmpl
index 55fa01b..93dd7c8 100644
--- a/third_party/WebKit/Source/bindings/templates/attributes.cpp.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/attributes.cpp.tmpl
@@ -151,9 +151,9 @@
   {% if attribute.is_keep_alive_for_gc %}
   // Keep the wrapper object for the return value alive as long as |this|
   // object is alive in order to save creation time of the wrapper object.
-  if ({{attribute.cpp_value}} && DOMDataStore::SetReturnValue{{world_suffix}}(info.GetReturnValue(), {{attribute.cpp_value}}))
+  if ({{attribute.cpp_value}} && DOMDataStore::SetReturnValue{{world_suffix}}(info.GetReturnValue(), {{attribute.cpp_value_to_script_wrappable}}))
     return;
-  v8::Local<v8::Value> v8Value(ToV8({{attribute.cpp_value}}, holder, info.GetIsolate()));
+  v8::Local<v8::Value> v8Value(ToV8({{attribute.cpp_value_to_script_wrappable}}, holder, info.GetIsolate()));
   V8PrivateProperty::GetSymbol(
       info.GetIsolate(), "KeepAlive#{{interface_name}}#{{attribute.name}}")
       .Set(holder, v8Value);
diff --git a/third_party/WebKit/Source/bindings/templates/callback_function.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/callback_function.cpp.tmpl
index 75d62af..65856a3 100644
--- a/third_party/WebKit/Source/bindings/templates/callback_function.cpp.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/callback_function.cpp.tmpl
@@ -82,8 +82,10 @@
 
 {{cpp_class}}* NativeValueTraits<{{cpp_class}}>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   {{cpp_class}}* nativeValue = {{cpp_class}}::Create(ScriptState::Current(isolate), value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to {{callback_function_name}}.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "{{callback_function_name}}"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/templates/dictionary_impl.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/dictionary_impl.cpp.tmpl
index f9865bb..dc7b9af 100644
--- a/third_party/WebKit/Source/bindings/templates/dictionary_impl.cpp.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/dictionary_impl.cpp.tmpl
@@ -33,7 +33,7 @@
   return {{member.getter_expression}};
 }
 void {{cpp_class}}::{{member.setter_name}}({{member.rvalue_cpp_type}} value) {
-  m_{{member.cpp_name}} = value;
+  m_{{member.cpp_name}} = {{member.setter_value}};
   {% if member.nullable_indicator_name %}
   {{member.nullable_indicator_name}} = true;
   {% endif %}
diff --git a/third_party/WebKit/Source/bindings/templates/interface.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/interface.cpp.tmpl
index 289ea7e..bbd979c 100644
--- a/third_party/WebKit/Source/bindings/templates/interface.cpp.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/interface.cpp.tmpl
@@ -893,8 +893,10 @@
 {% block native_value_traits %}
 {{cpp_class}}* NativeValueTraits<{{cpp_class}}>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   {{cpp_class}}* nativeValue = {{v8_class}}::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to {{interface_name}}.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "{{interface_name}}"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl
index 02dca6c..e361c1b 100644
--- a/third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl
@@ -7,7 +7,13 @@
 {% else %}
 if (V8{{member.type_name}}::hasInstance(v8Value, isolate)) {
 {% endif %}
+{% if member.is_array_buffer_view_or_typed_array %}
+  {{member.cpp_local_type}} cppValue = ToNotShared<{{member.cpp_local_type}}>(isolate, v8Value, exceptionState);
+  if (exceptionState.HadException())
+    return;
+{% else %}
   {{member.cpp_local_type}} cppValue = V8{{member.type_name}}::toImpl(v8::Local<v8::Object>::Cast(v8Value));
+{% endif %}
   impl.set{{member.type_name}}(cppValue);
   return;
 }
@@ -41,7 +47,11 @@
     return;
   }
   {% endif %}
+  {% if member.is_array_buffer_view_or_typed_array %}
+  m_{{member.cpp_name}} = {{member.cpp_type}}(value.View());
+  {% else %}
   m_{{member.cpp_name}} = value;
+  {% endif %}
   m_type = {{member.specific_type_enum}};
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/idls/core/TestObject.idl b/third_party/WebKit/Source/bindings/tests/idls/core/TestObject.idl
index 3757b87..704db79 100644
--- a/third_party/WebKit/Source/bindings/tests/idls/core/TestObject.idl
+++ b/third_party/WebKit/Source/bindings/tests/idls/core/TestObject.idl
@@ -285,6 +285,7 @@
     // Typed arrays
     ArrayBuffer arrayBufferMethod();
     ArrayBufferView arrayBufferViewMethod();
+    [RaisesException] ArrayBufferView arrayBufferViewMethodRaisesException();
     Float32Array float32ArrayMethod();
     Int32Array int32ArrayMethod();
     Uint8Array uint8ArrayMethod();
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/AnyCallbackFunctionOptionalAnyArg.cpp b/third_party/WebKit/Source/bindings/tests/results/core/AnyCallbackFunctionOptionalAnyArg.cpp
index 89a9fb9..af786cba 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/AnyCallbackFunctionOptionalAnyArg.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/AnyCallbackFunctionOptionalAnyArg.cpp
@@ -84,8 +84,10 @@
 
 AnyCallbackFunctionOptionalAnyArg* NativeValueTraits<AnyCallbackFunctionOptionalAnyArg>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   AnyCallbackFunctionOptionalAnyArg* nativeValue = AnyCallbackFunctionOptionalAnyArg::Create(ScriptState::Current(isolate), value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to AnyCallbackFunctionOptionalAnyArg.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "AnyCallbackFunctionOptionalAnyArg"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/ArrayBufferOrArrayBufferViewOrDictionary.cpp b/third_party/WebKit/Source/bindings/tests/results/core/ArrayBufferOrArrayBufferViewOrDictionary.cpp
index ec2b13a3..f38af45 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/ArrayBufferOrArrayBufferViewOrDictionary.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/ArrayBufferOrArrayBufferViewOrDictionary.cpp
@@ -14,8 +14,6 @@
 #include "bindings/core/v8/Dictionary.h"
 #include "bindings/core/v8/ToV8.h"
 #include "bindings/core/v8/V8ArrayBuffer.h"
-#include "bindings/core/v8/V8ArrayBufferView.h"
-#include "core/dom/FlexibleArrayBufferView.h"
 
 namespace blink {
 
@@ -38,18 +36,18 @@
   return container;
 }
 
-TestArrayBufferView* ArrayBufferOrArrayBufferViewOrDictionary::getAsArrayBufferView() const {
+NotShared<TestArrayBufferView> ArrayBufferOrArrayBufferViewOrDictionary::getAsArrayBufferView() const {
   DCHECK(isArrayBufferView());
   return m_arrayBufferView;
 }
 
-void ArrayBufferOrArrayBufferViewOrDictionary::setArrayBufferView(TestArrayBufferView* value) {
+void ArrayBufferOrArrayBufferViewOrDictionary::setArrayBufferView(NotShared<TestArrayBufferView> value) {
   DCHECK(isNull());
-  m_arrayBufferView = value;
+  m_arrayBufferView = Member<TestArrayBufferView>(value.View());
   m_type = SpecificTypeArrayBufferView;
 }
 
-ArrayBufferOrArrayBufferViewOrDictionary ArrayBufferOrArrayBufferViewOrDictionary::fromArrayBufferView(TestArrayBufferView* value) {
+ArrayBufferOrArrayBufferViewOrDictionary ArrayBufferOrArrayBufferViewOrDictionary::fromArrayBufferView(NotShared<TestArrayBufferView> value) {
   ArrayBufferOrArrayBufferViewOrDictionary container;
   container.setArrayBufferView(value);
   return container;
@@ -95,7 +93,9 @@
   }
 
   if (v8Value->IsArrayBufferView()) {
-    TestArrayBufferView* cppValue = V8ArrayBufferView::toImpl(v8::Local<v8::Object>::Cast(v8Value));
+    NotShared<TestArrayBufferView> cppValue = ToNotShared<NotShared<TestArrayBufferView>>(isolate, v8Value, exceptionState);
+    if (exceptionState.HadException())
+      return;
     impl.setArrayBufferView(cppValue);
     return;
   }
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/ArrayBufferOrArrayBufferViewOrDictionary.h b/third_party/WebKit/Source/bindings/tests/results/core/ArrayBufferOrArrayBufferViewOrDictionary.h
index c81a1bd..826a920b 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/ArrayBufferOrArrayBufferViewOrDictionary.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/ArrayBufferOrArrayBufferViewOrDictionary.h
@@ -15,14 +15,16 @@
 #include "bindings/core/v8/Dictionary.h"
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/NativeValueTraits.h"
+#include "bindings/core/v8/V8ArrayBufferView.h"
 #include "bindings/core/v8/V8Binding.h"
 #include "core/CoreExport.h"
+#include "core/dom/FlexibleArrayBufferView.h"
+#include "core/dom/NotShared.h"
 #include "platform/heap/Handle.h"
 
 namespace blink {
 
 class TestArrayBuffer;
-class TestArrayBufferView;
 
 class CORE_EXPORT ArrayBufferOrArrayBufferViewOrDictionary final {
   DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
@@ -36,9 +38,9 @@
   static ArrayBufferOrArrayBufferViewOrDictionary fromArrayBuffer(TestArrayBuffer*);
 
   bool isArrayBufferView() const { return m_type == SpecificTypeArrayBufferView; }
-  TestArrayBufferView* getAsArrayBufferView() const;
-  void setArrayBufferView(TestArrayBufferView*);
-  static ArrayBufferOrArrayBufferViewOrDictionary fromArrayBufferView(TestArrayBufferView*);
+  NotShared<TestArrayBufferView> getAsArrayBufferView() const;
+  void setArrayBufferView(NotShared<TestArrayBufferView>);
+  static ArrayBufferOrArrayBufferViewOrDictionary fromArrayBufferView(NotShared<TestArrayBufferView>);
 
   bool isDictionary() const { return m_type == SpecificTypeDictionary; }
   Dictionary getAsDictionary() const;
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/LongCallbackFunction.cpp b/third_party/WebKit/Source/bindings/tests/results/core/LongCallbackFunction.cpp
index 32ca61f..84ae62e 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/LongCallbackFunction.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/LongCallbackFunction.cpp
@@ -88,8 +88,10 @@
 
 LongCallbackFunction* NativeValueTraits<LongCallbackFunction>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   LongCallbackFunction* nativeValue = LongCallbackFunction::Create(ScriptState::Current(isolate), value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to LongCallbackFunction.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "LongCallbackFunction"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/StringOrArrayBufferOrArrayBufferView.cpp b/third_party/WebKit/Source/bindings/tests/results/core/StringOrArrayBufferOrArrayBufferView.cpp
index 82ca4fc..71fb6df1 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/StringOrArrayBufferOrArrayBufferView.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/StringOrArrayBufferOrArrayBufferView.cpp
@@ -15,8 +15,6 @@
 #include "bindings/core/v8/NativeValueTraitsImpl.h"
 #include "bindings/core/v8/ToV8.h"
 #include "bindings/core/v8/V8ArrayBuffer.h"
-#include "bindings/core/v8/V8ArrayBufferView.h"
-#include "core/dom/FlexibleArrayBufferView.h"
 
 namespace blink {
 
@@ -39,18 +37,18 @@
   return container;
 }
 
-TestArrayBufferView* StringOrArrayBufferOrArrayBufferView::getAsArrayBufferView() const {
+NotShared<TestArrayBufferView> StringOrArrayBufferOrArrayBufferView::getAsArrayBufferView() const {
   DCHECK(isArrayBufferView());
   return m_arrayBufferView;
 }
 
-void StringOrArrayBufferOrArrayBufferView::setArrayBufferView(TestArrayBufferView* value) {
+void StringOrArrayBufferOrArrayBufferView::setArrayBufferView(NotShared<TestArrayBufferView> value) {
   DCHECK(isNull());
-  m_arrayBufferView = value;
+  m_arrayBufferView = Member<TestArrayBufferView>(value.View());
   m_type = SpecificTypeArrayBufferView;
 }
 
-StringOrArrayBufferOrArrayBufferView StringOrArrayBufferOrArrayBufferView::fromArrayBufferView(TestArrayBufferView* value) {
+StringOrArrayBufferOrArrayBufferView StringOrArrayBufferOrArrayBufferView::fromArrayBufferView(NotShared<TestArrayBufferView> value) {
   StringOrArrayBufferOrArrayBufferView container;
   container.setArrayBufferView(value);
   return container;
@@ -96,7 +94,9 @@
   }
 
   if (v8Value->IsArrayBufferView()) {
-    TestArrayBufferView* cppValue = V8ArrayBufferView::toImpl(v8::Local<v8::Object>::Cast(v8Value));
+    NotShared<TestArrayBufferView> cppValue = ToNotShared<NotShared<TestArrayBufferView>>(isolate, v8Value, exceptionState);
+    if (exceptionState.HadException())
+      return;
     impl.setArrayBufferView(cppValue);
     return;
   }
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/StringOrArrayBufferOrArrayBufferView.h b/third_party/WebKit/Source/bindings/tests/results/core/StringOrArrayBufferOrArrayBufferView.h
index b04bab9..9d3c93e 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/StringOrArrayBufferOrArrayBufferView.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/StringOrArrayBufferOrArrayBufferView.h
@@ -15,14 +15,16 @@
 #include "bindings/core/v8/Dictionary.h"
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/NativeValueTraits.h"
+#include "bindings/core/v8/V8ArrayBufferView.h"
 #include "bindings/core/v8/V8Binding.h"
 #include "core/CoreExport.h"
+#include "core/dom/FlexibleArrayBufferView.h"
+#include "core/dom/NotShared.h"
 #include "platform/heap/Handle.h"
 
 namespace blink {
 
 class TestArrayBuffer;
-class TestArrayBufferView;
 
 class CORE_EXPORT StringOrArrayBufferOrArrayBufferView final {
   DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
@@ -36,9 +38,9 @@
   static StringOrArrayBufferOrArrayBufferView fromArrayBuffer(TestArrayBuffer*);
 
   bool isArrayBufferView() const { return m_type == SpecificTypeArrayBufferView; }
-  TestArrayBufferView* getAsArrayBufferView() const;
-  void setArrayBufferView(TestArrayBufferView*);
-  static StringOrArrayBufferOrArrayBufferView fromArrayBufferView(TestArrayBufferView*);
+  NotShared<TestArrayBufferView> getAsArrayBufferView() const;
+  void setArrayBufferView(NotShared<TestArrayBufferView>);
+  static StringOrArrayBufferOrArrayBufferView fromArrayBufferView(NotShared<TestArrayBufferView>);
 
   bool isString() const { return m_type == SpecificTypeString; }
   String getAsString() const;
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/StringSequenceCallbackFunctionLongSequenceArg.cpp b/third_party/WebKit/Source/bindings/tests/results/core/StringSequenceCallbackFunctionLongSequenceArg.cpp
index 149073d..904a2d0 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/StringSequenceCallbackFunctionLongSequenceArg.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/StringSequenceCallbackFunctionLongSequenceArg.cpp
@@ -87,8 +87,10 @@
 
 StringSequenceCallbackFunctionLongSequenceArg* NativeValueTraits<StringSequenceCallbackFunctionLongSequenceArg>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   StringSequenceCallbackFunctionLongSequenceArg* nativeValue = StringSequenceCallbackFunctionLongSequenceArg::Create(ScriptState::Current(isolate), value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to StringSequenceCallbackFunctionLongSequenceArg.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "StringSequenceCallbackFunctionLongSequenceArg"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.cpp b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.cpp
index 69af3f64..6d96d38b 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.cpp
@@ -400,11 +400,11 @@
 bool TestDictionary::hasUint8ArrayMember() const {
   return m_uint8ArrayMember;
 }
-DOMUint8Array* TestDictionary::uint8ArrayMember() const {
+NotShared<DOMUint8Array> TestDictionary::uint8ArrayMember() const {
   return m_uint8ArrayMember;
 }
-void TestDictionary::setUint8ArrayMember(DOMUint8Array* value) {
-  m_uint8ArrayMember = value;
+void TestDictionary::setUint8ArrayMember(NotShared<DOMUint8Array> value) {
+  m_uint8ArrayMember = value.View();
 }
 bool TestDictionary::hasUnionInRecordMember() const {
   return m_hasUnionInRecordMember;
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h
index 0c37adb0..951f536 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h
@@ -22,6 +22,7 @@
 #include "bindings/tests/idls/core/TestInterface2.h"
 #include "core/CoreExport.h"
 #include "core/dom/DOMTypedArray.h"
+#include "core/dom/NotShared.h"
 #include "core/testing/InternalDictionary.h"
 #include "platform/heap/Handle.h"
 #include "platform/wtf/Vector.h"
@@ -186,8 +187,8 @@
   void setTestObjectSequenceMember(const HeapVector<Member<TestObject>>&);
 
   bool hasUint8ArrayMember() const;
-  DOMUint8Array* uint8ArrayMember() const;
-  void setUint8ArrayMember(DOMUint8Array*);
+  NotShared<DOMUint8Array> uint8ArrayMember() const;
+  void setUint8ArrayMember(NotShared<DOMUint8Array>);
 
   bool hasUnionInRecordMember() const;
   const HeapVector<std::pair<String, LongOrBoolean>>& unionInRecordMember() const;
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestInterface2OrUint8Array.cpp b/third_party/WebKit/Source/bindings/tests/results/core/TestInterface2OrUint8Array.cpp
index daeda9dc..2fef52b 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/TestInterface2OrUint8Array.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/TestInterface2OrUint8Array.cpp
@@ -35,18 +35,18 @@
   return container;
 }
 
-DOMUint8Array* TestInterface2OrUint8Array::getAsUint8Array() const {
+NotShared<DOMUint8Array> TestInterface2OrUint8Array::getAsUint8Array() const {
   DCHECK(isUint8Array());
   return m_uint8Array;
 }
 
-void TestInterface2OrUint8Array::setUint8Array(DOMUint8Array* value) {
+void TestInterface2OrUint8Array::setUint8Array(NotShared<DOMUint8Array> value) {
   DCHECK(isNull());
-  m_uint8Array = value;
+  m_uint8Array = Member<DOMUint8Array>(value.View());
   m_type = SpecificTypeUint8Array;
 }
 
-TestInterface2OrUint8Array TestInterface2OrUint8Array::fromUint8Array(DOMUint8Array* value) {
+TestInterface2OrUint8Array TestInterface2OrUint8Array::fromUint8Array(NotShared<DOMUint8Array> value) {
   TestInterface2OrUint8Array container;
   container.setUint8Array(value);
   return container;
@@ -75,7 +75,9 @@
   }
 
   if (v8Value->IsUint8Array()) {
-    DOMUint8Array* cppValue = V8Uint8Array::toImpl(v8::Local<v8::Object>::Cast(v8Value));
+    NotShared<DOMUint8Array> cppValue = ToNotShared<NotShared<DOMUint8Array>>(isolate, v8Value, exceptionState);
+    if (exceptionState.HadException())
+      return;
     impl.setUint8Array(cppValue);
     return;
   }
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestInterface2OrUint8Array.h b/third_party/WebKit/Source/bindings/tests/results/core/TestInterface2OrUint8Array.h
index 4192249..ae0f637 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/TestInterface2OrUint8Array.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/TestInterface2OrUint8Array.h
@@ -20,6 +20,7 @@
 #include "bindings/core/v8/V8Uint8Array.h"
 #include "core/CoreExport.h"
 #include "core/dom/FlexibleArrayBufferView.h"
+#include "core/dom/NotShared.h"
 #include "platform/heap/Handle.h"
 
 namespace blink {
@@ -38,9 +39,9 @@
   static TestInterface2OrUint8Array fromTestInterface2(TestInterface2*);
 
   bool isUint8Array() const { return m_type == SpecificTypeUint8Array; }
-  DOMUint8Array* getAsUint8Array() const;
-  void setUint8Array(DOMUint8Array*);
-  static TestInterface2OrUint8Array fromUint8Array(DOMUint8Array*);
+  NotShared<DOMUint8Array> getAsUint8Array() const;
+  void setUint8Array(NotShared<DOMUint8Array>);
+  static TestInterface2OrUint8Array fromUint8Array(NotShared<DOMUint8Array>);
 
   TestInterface2OrUint8Array(const TestInterface2OrUint8Array&);
   ~TestInterface2OrUint8Array();
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8ArrayBuffer.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8ArrayBuffer.cpp
index f50da1b..598d706 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8ArrayBuffer.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8ArrayBuffer.cpp
@@ -81,8 +81,10 @@
 
 TestArrayBuffer* NativeValueTraits<TestArrayBuffer>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestArrayBuffer* nativeValue = V8ArrayBuffer::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to ArrayBuffer.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "ArrayBuffer"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8ArrayBufferView.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8ArrayBufferView.cpp
index df9d2ee9..9c0bdf0 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8ArrayBufferView.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8ArrayBufferView.cpp
@@ -101,8 +101,10 @@
 
 TestArrayBufferView* NativeValueTraits<TestArrayBufferView>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestArrayBufferView* nativeValue = V8ArrayBufferView::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to ArrayBufferView.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "ArrayBufferView"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8DataView.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8DataView.cpp
index 21cb4fd..0e9a43d 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8DataView.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8DataView.cpp
@@ -81,8 +81,10 @@
 
 TestDataView* NativeValueTraits<TestDataView>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestDataView* nativeValue = V8DataView::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to DataView.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "DataView"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8DataView.h b/third_party/WebKit/Source/bindings/tests/results/core/V8DataView.h
index c45d0b5..e27504c 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8DataView.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8DataView.h
@@ -23,6 +23,7 @@
 #include "bindings/tests/idls/core/TestDataView.h"
 #include "core/CoreExport.h"
 #include "core/dom/FlexibleArrayBufferView.h"
+#include "core/dom/NotShared.h"
 #include "platform/heap/Handle.h"
 
 namespace blink {
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8SVGTestInterface.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8SVGTestInterface.cpp
index ee7f6b1..12b1de4 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8SVGTestInterface.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8SVGTestInterface.cpp
@@ -132,8 +132,10 @@
 
 SVGTestInterface* NativeValueTraits<SVGTestInterface>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   SVGTestInterface* nativeValue = V8SVGTestInterface::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to SVGTestInterface.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "SVGTestInterface"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackFunctions.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackFunctions.cpp
index f2664b0..10a3fc8 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackFunctions.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackFunctions.cpp
@@ -336,8 +336,10 @@
 
 TestCallbackFunctions* NativeValueTraits<TestCallbackFunctions>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestCallbackFunctions* nativeValue = V8TestCallbackFunctions::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestCallbackFunctions.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestCallbackFunctions"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestConstants.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestConstants.cpp
index 4757657..ea03305 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestConstants.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestConstants.cpp
@@ -199,8 +199,10 @@
 
 TestConstants* NativeValueTraits<TestConstants>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestConstants* nativeValue = V8TestConstants::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestConstants.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestConstants"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestDictionary.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestDictionary.cpp
index d41d7e2..f841653f 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestDictionary.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestDictionary.cpp
@@ -26,6 +26,7 @@
 #include "bindings/core/v8/V8TestObject.h"
 #include "bindings/core/v8/V8Uint8Array.h"
 #include "core/dom/FlexibleArrayBufferView.h"
+#include "core/dom/NotShared.h"
 #include "core/frame/Deprecation.h"
 #include "platform/RuntimeEnabledFeatures.h"
 
@@ -643,7 +644,9 @@
   if (uint8ArrayMemberValue.IsEmpty() || uint8ArrayMemberValue->IsUndefined()) {
     // Do nothing.
   } else {
-    DOMUint8Array* uint8ArrayMember = uint8ArrayMemberValue->IsUint8Array() ? V8Uint8Array::toImpl(v8::Local<v8::Uint8Array>::Cast(uint8ArrayMemberValue)) : 0;
+    NotShared<DOMUint8Array> uint8ArrayMember = ToNotShared<NotShared<DOMUint8Array>>(isolate, uint8ArrayMemberValue, exceptionState);
+    if (exceptionState.HadException())
+      return;
     if (!uint8ArrayMember) {
       exceptionState.ThrowTypeError("member uint8ArrayMember is not of type Uint8Array.");
       return;
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestException.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestException.cpp
index 7910b468..2049192b 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestException.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestException.cpp
@@ -172,8 +172,10 @@
 
 TestException* NativeValueTraits<TestException>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestException* nativeValue = V8TestException::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestException.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestException"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexed.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexed.cpp
index 5e19ae7..d812c6c3 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexed.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexed.cpp
@@ -215,8 +215,10 @@
 
 TestIntegerIndexed* NativeValueTraits<TestIntegerIndexed>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestIntegerIndexed* nativeValue = V8TestIntegerIndexed::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestIntegerIndexed.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestIntegerIndexed"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedGlobal.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedGlobal.cpp
index 5f4b6f0f..b77637e 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedGlobal.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedGlobal.cpp
@@ -236,8 +236,10 @@
 
 TestIntegerIndexedGlobal* NativeValueTraits<TestIntegerIndexedGlobal>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestIntegerIndexedGlobal* nativeValue = V8TestIntegerIndexedGlobal::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestIntegerIndexedGlobal.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestIntegerIndexedGlobal"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedPrimaryGlobal.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedPrimaryGlobal.cpp
index 1d7a347..12835f1 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedPrimaryGlobal.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedPrimaryGlobal.cpp
@@ -236,8 +236,10 @@
 
 TestIntegerIndexedPrimaryGlobal* NativeValueTraits<TestIntegerIndexedPrimaryGlobal>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestIntegerIndexedPrimaryGlobal* nativeValue = V8TestIntegerIndexedPrimaryGlobal::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestIntegerIndexedPrimaryGlobal.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestIntegerIndexedPrimaryGlobal"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface.cpp
index 6901e10..fff5df3 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface.cpp
@@ -3062,8 +3062,10 @@
 
 TestInterfaceImplementation* NativeValueTraits<TestInterfaceImplementation>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestInterfaceImplementation* nativeValue = V8TestInterface::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestInterface.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestInterface"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface2.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface2.cpp
index 68098e3f..9cbc1bf8 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface2.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface2.cpp
@@ -665,8 +665,10 @@
 
 TestInterface2* NativeValueTraits<TestInterface2>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestInterface2* nativeValue = V8TestInterface2::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestInterface2.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestInterface2"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface3.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface3.cpp
index 1a171d0..03caa8b6 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface3.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface3.cpp
@@ -222,8 +222,10 @@
 
 TestInterface3* NativeValueTraits<TestInterface3>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestInterface3* nativeValue = V8TestInterface3::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestInterface3.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestInterface3"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCheckSecurity.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCheckSecurity.cpp
index 942a43905..ae947c347 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCheckSecurity.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCheckSecurity.cpp
@@ -556,8 +556,10 @@
 
 TestInterfaceCheckSecurity* NativeValueTraits<TestInterfaceCheckSecurity>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestInterfaceCheckSecurity* nativeValue = V8TestInterfaceCheckSecurity::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestInterfaceCheckSecurity.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestInterfaceCheckSecurity"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor.cpp
index 314daf6..00b22cb 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor.cpp
@@ -444,8 +444,10 @@
 
 TestInterfaceConstructor* NativeValueTraits<TestInterfaceConstructor>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestInterfaceConstructor* nativeValue = V8TestInterfaceConstructor::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestInterfaceConstructor.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestInterfaceConstructor"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor2.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor2.cpp
index b1e83bc..c7802d3 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor2.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor2.cpp
@@ -268,8 +268,10 @@
 
 TestInterfaceConstructor2* NativeValueTraits<TestInterfaceConstructor2>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestInterfaceConstructor2* nativeValue = V8TestInterfaceConstructor2::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestInterfaceConstructor2.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestInterfaceConstructor2"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor3.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor3.cpp
index d960556..ca090d78 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor3.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor3.cpp
@@ -121,8 +121,10 @@
 
 TestInterfaceConstructor3* NativeValueTraits<TestInterfaceConstructor3>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestInterfaceConstructor3* nativeValue = V8TestInterfaceConstructor3::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestInterfaceConstructor3.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestInterfaceConstructor3"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor4.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor4.cpp
index 292e415..5b99fde1 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor4.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor4.cpp
@@ -154,8 +154,10 @@
 
 TestInterfaceConstructor4* NativeValueTraits<TestInterfaceConstructor4>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestInterfaceConstructor4* nativeValue = V8TestInterfaceConstructor4::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestInterfaceConstructor4.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestInterfaceConstructor4"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCustomConstructor.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCustomConstructor.cpp
index e57fb80d..550ce5017 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCustomConstructor.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCustomConstructor.cpp
@@ -102,8 +102,10 @@
 
 TestInterfaceCustomConstructor* NativeValueTraits<TestInterfaceCustomConstructor>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestInterfaceCustomConstructor* nativeValue = V8TestInterfaceCustomConstructor::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestInterfaceCustomConstructor.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestInterfaceCustomConstructor"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceDocument.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceDocument.cpp
index acf18ea3..907e876 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceDocument.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceDocument.cpp
@@ -134,8 +134,10 @@
 
 TestInterfaceDocument* NativeValueTraits<TestInterfaceDocument>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestInterfaceDocument* nativeValue = V8TestInterfaceDocument::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestInterfaceDocument.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestInterfaceDocument"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEmpty.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEmpty.cpp
index 2dd0a69..b150665 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEmpty.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEmpty.cpp
@@ -85,8 +85,10 @@
 
 TestInterfaceEmpty* NativeValueTraits<TestInterfaceEmpty>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestInterfaceEmpty* nativeValue = V8TestInterfaceEmpty::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestInterfaceEmpty.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestInterfaceEmpty"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEventInitConstructor.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEventInitConstructor.cpp
index 0b3a76e20..6c656cc 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEventInitConstructor.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEventInitConstructor.cpp
@@ -164,8 +164,10 @@
 
 TestInterfaceEventInitConstructor* NativeValueTraits<TestInterfaceEventInitConstructor>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestInterfaceEventInitConstructor* nativeValue = V8TestInterfaceEventInitConstructor::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestInterfaceEventInitConstructor.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestInterfaceEventInitConstructor"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEventTarget.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEventTarget.cpp
index 09a0848..2c36e3dc 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEventTarget.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEventTarget.cpp
@@ -161,8 +161,10 @@
 
 TestInterfaceEventTarget* NativeValueTraits<TestInterfaceEventTarget>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestInterfaceEventTarget* nativeValue = V8TestInterfaceEventTarget::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestInterfaceEventTarget.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestInterfaceEventTarget"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceGarbageCollected.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceGarbageCollected.cpp
index 82c05801..c0292cdd 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceGarbageCollected.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceGarbageCollected.cpp
@@ -415,8 +415,10 @@
 
 TestInterfaceGarbageCollected* NativeValueTraits<TestInterfaceGarbageCollected>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestInterfaceGarbageCollected* nativeValue = V8TestInterfaceGarbageCollected::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestInterfaceGarbageCollected.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestInterfaceGarbageCollected"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNamedConstructor.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNamedConstructor.cpp
index 022892a..59f04aa0 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNamedConstructor.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNamedConstructor.cpp
@@ -237,8 +237,10 @@
 
 TestInterfaceNamedConstructor* NativeValueTraits<TestInterfaceNamedConstructor>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestInterfaceNamedConstructor* nativeValue = V8TestInterfaceNamedConstructor::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestInterfaceNamedConstructor.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestInterfaceNamedConstructor"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNamedConstructor2.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNamedConstructor2.cpp
index eadd365..5260443 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNamedConstructor2.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNamedConstructor2.cpp
@@ -172,8 +172,10 @@
 
 TestInterfaceNamedConstructor2* NativeValueTraits<TestInterfaceNamedConstructor2>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestInterfaceNamedConstructor2* nativeValue = V8TestInterfaceNamedConstructor2::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestInterfaceNamedConstructor2.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestInterfaceNamedConstructor2"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNode.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNode.cpp
index d111f07..d137329 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNode.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNode.cpp
@@ -401,8 +401,10 @@
 
 TestInterfaceNode* NativeValueTraits<TestInterfaceNode>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestInterfaceNode* nativeValue = V8TestInterfaceNode::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestInterfaceNode.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestInterfaceNode"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceOriginTrialEnabled.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceOriginTrialEnabled.cpp
index e9276f80..a3a161d 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceOriginTrialEnabled.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceOriginTrialEnabled.cpp
@@ -317,8 +317,10 @@
 
 TestInterfaceOriginTrialEnabled* NativeValueTraits<TestInterfaceOriginTrialEnabled>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestInterfaceOriginTrialEnabled* nativeValue = V8TestInterfaceOriginTrialEnabled::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestInterfaceOriginTrialEnabled.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestInterfaceOriginTrialEnabled"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceSecureContext.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceSecureContext.cpp
index 562e4491..28bcd5f 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceSecureContext.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceSecureContext.cpp
@@ -382,8 +382,10 @@
 
 TestInterfaceSecureContext* NativeValueTraits<TestInterfaceSecureContext>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestInterfaceSecureContext* nativeValue = V8TestInterfaceSecureContext::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestInterfaceSecureContext.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestInterfaceSecureContext"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestNode.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestNode.cpp
index 261e59d..53ac540 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestNode.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestNode.cpp
@@ -257,8 +257,10 @@
 
 TestNode* NativeValueTraits<TestNode>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestNode* nativeValue = V8TestNode::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestNode.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestNode"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
index b0e8d99..32f4791 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
@@ -61,6 +61,7 @@
 #include "core/dom/DOMArrayBufferBase.h"
 #include "core/dom/Document.h"
 #include "core/dom/FlexibleArrayBufferView.h"
+#include "core/dom/NotShared.h"
 #include "core/dom/TagCollection.h"
 #include "core/dom/custom/V0CustomElementProcessingStack.h"
 #include "core/frame/Deprecation.h"
@@ -1099,7 +1100,7 @@
 
   TestObject* impl = V8TestObject::toImpl(holder);
 
-  V8SetReturnValueFast(info, WTF::GetPtr(impl->float32ArrayAttribute()), impl);
+  V8SetReturnValueFast(info, impl->float32ArrayAttribute(), impl);
 }
 
 static void float32ArrayAttributeAttributeSetter(v8::Local<v8::Value> v8Value, const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -1112,7 +1113,9 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "float32ArrayAttribute");
 
   // Prepare the value to be set.
-  DOMFloat32Array* cppValue = v8Value->IsFloat32Array() ? V8Float32Array::toImpl(v8::Local<v8::Float32Array>::Cast(v8Value)) : 0;
+  NotShared<DOMFloat32Array> cppValue = ToNotShared<NotShared<DOMFloat32Array>>(info.GetIsolate(), v8Value, exceptionState);
+  if (exceptionState.HadException())
+    return;
 
   // Type check per: http://heycam.github.io/webidl/#es-interface
   if (!cppValue) {
@@ -1128,7 +1131,7 @@
 
   TestObject* impl = V8TestObject::toImpl(holder);
 
-  V8SetReturnValueFast(info, WTF::GetPtr(impl->uint8ArrayAttribute()), impl);
+  V8SetReturnValueFast(info, impl->uint8ArrayAttribute(), impl);
 }
 
 static void uint8ArrayAttributeAttributeSetter(v8::Local<v8::Value> v8Value, const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -1141,7 +1144,9 @@
   ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "TestObject", "uint8ArrayAttribute");
 
   // Prepare the value to be set.
-  DOMUint8Array* cppValue = v8Value->IsUint8Array() ? V8Uint8Array::toImpl(v8::Local<v8::Uint8Array>::Cast(v8Value)) : 0;
+  NotShared<DOMUint8Array> cppValue = ToNotShared<NotShared<DOMUint8Array>>(info.GetIsolate(), v8Value, exceptionState);
+  if (exceptionState.HadException())
+    return;
 
   // Type check per: http://heycam.github.io/webidl/#es-interface
   if (!cppValue) {
@@ -4663,6 +4668,18 @@
   V8SetReturnValue(info, impl->arrayBufferViewMethod());
 }
 
+static void arrayBufferViewMethodRaisesExceptionMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  ExceptionState exceptionState(info.GetIsolate(), ExceptionState::kExecutionContext, "TestObject", "arrayBufferViewMethodRaisesException");
+
+  TestObject* impl = V8TestObject::toImpl(info.Holder());
+
+  NotShared<TestArrayBufferView> result = impl->arrayBufferViewMethodRaisesException(exceptionState);
+  if (exceptionState.HadException()) {
+    return;
+  }
+  V8SetReturnValue(info, result);
+}
+
 static void float32ArrayMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
   TestObject* impl = V8TestObject::toImpl(info.Holder());
 
@@ -4709,7 +4726,7 @@
   }
 
   TestArrayBuffer* arrayBufferArg;
-  arrayBufferArg = info[0]->IsArrayBuffer() ? V8ArrayBuffer::toImpl(v8::Local<v8::ArrayBuffer>::Cast(info[0])) : 0;
+  arrayBufferArg = V8ArrayBuffer::toImplWithTypeCheck(info.GetIsolate(), info[0]);
   if (!arrayBufferArg && !IsUndefinedOrNull(info[0])) {
     V8ThrowException::ThrowTypeError(info.GetIsolate(), ExceptionMessages::FailedToExecute("voidMethodArrayBufferOrNullArg", "TestObject", "parameter 1 is not of type 'ArrayBuffer'."));
 
@@ -4720,17 +4737,21 @@
 }
 
 static void voidMethodArrayBufferViewArgMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  ExceptionState exceptionState(info.GetIsolate(), ExceptionState::kExecutionContext, "TestObject", "voidMethodArrayBufferViewArg");
+
   TestObject* impl = V8TestObject::toImpl(info.Holder());
 
   if (UNLIKELY(info.Length() < 1)) {
-    V8ThrowException::ThrowTypeError(info.GetIsolate(), ExceptionMessages::FailedToExecute("voidMethodArrayBufferViewArg", "TestObject", ExceptionMessages::NotEnoughArguments(1, info.Length())));
+    exceptionState.ThrowTypeError(ExceptionMessages::NotEnoughArguments(1, info.Length()));
     return;
   }
 
-  TestArrayBufferView* arrayBufferViewArg;
-  arrayBufferViewArg = info[0]->IsArrayBufferView() ? V8ArrayBufferView::toImpl(v8::Local<v8::ArrayBufferView>::Cast(info[0])) : 0;
+  NotShared<TestArrayBufferView> arrayBufferViewArg;
+  arrayBufferViewArg = ToNotShared<NotShared<TestArrayBufferView>>(info.GetIsolate(), info[0], exceptionState);
+  if (exceptionState.HadException())
+    return;
   if (!arrayBufferViewArg) {
-    V8ThrowException::ThrowTypeError(info.GetIsolate(), ExceptionMessages::FailedToExecute("voidMethodArrayBufferViewArg", "TestObject", "parameter 1 is not of type 'ArrayBufferView'."));
+    exceptionState.ThrowTypeError("parameter 1 is not of type 'ArrayBufferView'.");
 
     return;
   }
@@ -4739,17 +4760,19 @@
 }
 
 static void voidMethodFlexibleArrayBufferViewArgMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  ExceptionState exceptionState(info.GetIsolate(), ExceptionState::kExecutionContext, "TestObject", "voidMethodFlexibleArrayBufferViewArg");
+
   TestObject* impl = V8TestObject::toImpl(info.Holder());
 
   if (UNLIKELY(info.Length() < 1)) {
-    V8ThrowException::ThrowTypeError(info.GetIsolate(), ExceptionMessages::FailedToExecute("voidMethodFlexibleArrayBufferViewArg", "TestObject", ExceptionMessages::NotEnoughArguments(1, info.Length())));
+    exceptionState.ThrowTypeError(ExceptionMessages::NotEnoughArguments(1, info.Length()));
     return;
   }
 
   FlexibleArrayBufferView arrayBufferViewArg;
   ToFlexibleArrayBufferView(info.GetIsolate(), info[0], arrayBufferViewArg, allocateFlexibleArrayBufferViewStorage(info[0]));
   if (!arrayBufferViewArg) {
-    V8ThrowException::ThrowTypeError(info.GetIsolate(), ExceptionMessages::FailedToExecute("voidMethodFlexibleArrayBufferViewArg", "TestObject", "parameter 1 is not of type 'ArrayBufferView'."));
+    exceptionState.ThrowTypeError("parameter 1 is not of type 'ArrayBufferView'.");
 
     return;
   }
@@ -4758,17 +4781,19 @@
 }
 
 static void voidMethodFlexibleArrayBufferViewTypedArgMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  ExceptionState exceptionState(info.GetIsolate(), ExceptionState::kExecutionContext, "TestObject", "voidMethodFlexibleArrayBufferViewTypedArg");
+
   TestObject* impl = V8TestObject::toImpl(info.Holder());
 
   if (UNLIKELY(info.Length() < 1)) {
-    V8ThrowException::ThrowTypeError(info.GetIsolate(), ExceptionMessages::FailedToExecute("voidMethodFlexibleArrayBufferViewTypedArg", "TestObject", ExceptionMessages::NotEnoughArguments(1, info.Length())));
+    exceptionState.ThrowTypeError(ExceptionMessages::NotEnoughArguments(1, info.Length()));
     return;
   }
 
   FlexibleFloat32ArrayView typedArrayBufferViewArg;
   ToFlexibleArrayBufferView(info.GetIsolate(), info[0], typedArrayBufferViewArg, allocateFlexibleArrayBufferViewStorage(info[0]));
   if (!typedArrayBufferViewArg) {
-    V8ThrowException::ThrowTypeError(info.GetIsolate(), ExceptionMessages::FailedToExecute("voidMethodFlexibleArrayBufferViewTypedArg", "TestObject", "parameter 1 is not of type 'Float32Array'."));
+    exceptionState.ThrowTypeError("parameter 1 is not of type 'Float32Array'.");
 
     return;
   }
@@ -4777,17 +4802,21 @@
 }
 
 static void voidMethodFloat32ArrayArgMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  ExceptionState exceptionState(info.GetIsolate(), ExceptionState::kExecutionContext, "TestObject", "voidMethodFloat32ArrayArg");
+
   TestObject* impl = V8TestObject::toImpl(info.Holder());
 
   if (UNLIKELY(info.Length() < 1)) {
-    V8ThrowException::ThrowTypeError(info.GetIsolate(), ExceptionMessages::FailedToExecute("voidMethodFloat32ArrayArg", "TestObject", ExceptionMessages::NotEnoughArguments(1, info.Length())));
+    exceptionState.ThrowTypeError(ExceptionMessages::NotEnoughArguments(1, info.Length()));
     return;
   }
 
-  DOMFloat32Array* float32ArrayArg;
-  float32ArrayArg = info[0]->IsFloat32Array() ? V8Float32Array::toImpl(v8::Local<v8::Float32Array>::Cast(info[0])) : 0;
+  NotShared<DOMFloat32Array> float32ArrayArg;
+  float32ArrayArg = ToNotShared<NotShared<DOMFloat32Array>>(info.GetIsolate(), info[0], exceptionState);
+  if (exceptionState.HadException())
+    return;
   if (!float32ArrayArg) {
-    V8ThrowException::ThrowTypeError(info.GetIsolate(), ExceptionMessages::FailedToExecute("voidMethodFloat32ArrayArg", "TestObject", "parameter 1 is not of type 'Float32Array'."));
+    exceptionState.ThrowTypeError("parameter 1 is not of type 'Float32Array'.");
 
     return;
   }
@@ -4796,17 +4825,21 @@
 }
 
 static void voidMethodInt32ArrayArgMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  ExceptionState exceptionState(info.GetIsolate(), ExceptionState::kExecutionContext, "TestObject", "voidMethodInt32ArrayArg");
+
   TestObject* impl = V8TestObject::toImpl(info.Holder());
 
   if (UNLIKELY(info.Length() < 1)) {
-    V8ThrowException::ThrowTypeError(info.GetIsolate(), ExceptionMessages::FailedToExecute("voidMethodInt32ArrayArg", "TestObject", ExceptionMessages::NotEnoughArguments(1, info.Length())));
+    exceptionState.ThrowTypeError(ExceptionMessages::NotEnoughArguments(1, info.Length()));
     return;
   }
 
-  DOMInt32Array* int32ArrayArg;
-  int32ArrayArg = info[0]->IsInt32Array() ? V8Int32Array::toImpl(v8::Local<v8::Int32Array>::Cast(info[0])) : 0;
+  NotShared<DOMInt32Array> int32ArrayArg;
+  int32ArrayArg = ToNotShared<NotShared<DOMInt32Array>>(info.GetIsolate(), info[0], exceptionState);
+  if (exceptionState.HadException())
+    return;
   if (!int32ArrayArg) {
-    V8ThrowException::ThrowTypeError(info.GetIsolate(), ExceptionMessages::FailedToExecute("voidMethodInt32ArrayArg", "TestObject", "parameter 1 is not of type 'Int32Array'."));
+    exceptionState.ThrowTypeError("parameter 1 is not of type 'Int32Array'.");
 
     return;
   }
@@ -4815,17 +4848,21 @@
 }
 
 static void voidMethodUint8ArrayArgMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  ExceptionState exceptionState(info.GetIsolate(), ExceptionState::kExecutionContext, "TestObject", "voidMethodUint8ArrayArg");
+
   TestObject* impl = V8TestObject::toImpl(info.Holder());
 
   if (UNLIKELY(info.Length() < 1)) {
-    V8ThrowException::ThrowTypeError(info.GetIsolate(), ExceptionMessages::FailedToExecute("voidMethodUint8ArrayArg", "TestObject", ExceptionMessages::NotEnoughArguments(1, info.Length())));
+    exceptionState.ThrowTypeError(ExceptionMessages::NotEnoughArguments(1, info.Length()));
     return;
   }
 
-  DOMUint8Array* uint8ArrayArg;
-  uint8ArrayArg = info[0]->IsUint8Array() ? V8Uint8Array::toImpl(v8::Local<v8::Uint8Array>::Cast(info[0])) : 0;
+  NotShared<DOMUint8Array> uint8ArrayArg;
+  uint8ArrayArg = ToNotShared<NotShared<DOMUint8Array>>(info.GetIsolate(), info[0], exceptionState);
+  if (exceptionState.HadException())
+    return;
   if (!uint8ArrayArg) {
-    V8ThrowException::ThrowTypeError(info.GetIsolate(), ExceptionMessages::FailedToExecute("voidMethodUint8ArrayArg", "TestObject", "parameter 1 is not of type 'Uint8Array'."));
+    exceptionState.ThrowTypeError("parameter 1 is not of type 'Uint8Array'.");
 
     return;
   }
@@ -10904,6 +10941,10 @@
   TestObjectV8Internal::arrayBufferViewMethodMethod(info);
 }
 
+void V8TestObject::arrayBufferViewMethodRaisesExceptionMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  TestObjectV8Internal::arrayBufferViewMethodRaisesExceptionMethod(info);
+}
+
 void V8TestObject::float32ArrayMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
   TestObjectV8Internal::float32ArrayMethodMethod(info);
 }
@@ -12010,6 +12051,7 @@
     {"voidMethodNodeArg", V8TestObject::voidMethodNodeArgMethodCallback, 1, v8::None, V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kDoNotCheckAccess, V8DOMConfiguration::kAllWorlds},
     {"arrayBufferMethod", V8TestObject::arrayBufferMethodMethodCallback, 0, v8::None, V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kDoNotCheckAccess, V8DOMConfiguration::kAllWorlds},
     {"arrayBufferViewMethod", V8TestObject::arrayBufferViewMethodMethodCallback, 0, v8::None, V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kDoNotCheckAccess, V8DOMConfiguration::kAllWorlds},
+    {"arrayBufferViewMethodRaisesException", V8TestObject::arrayBufferViewMethodRaisesExceptionMethodCallback, 0, v8::None, V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kDoNotCheckAccess, V8DOMConfiguration::kAllWorlds},
     {"float32ArrayMethod", V8TestObject::float32ArrayMethodMethodCallback, 0, v8::None, V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kDoNotCheckAccess, V8DOMConfiguration::kAllWorlds},
     {"int32ArrayMethod", V8TestObject::int32ArrayMethodMethodCallback, 0, v8::None, V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kDoNotCheckAccess, V8DOMConfiguration::kAllWorlds},
     {"uint8ArrayMethod", V8TestObject::uint8ArrayMethodMethodCallback, 0, v8::None, V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kDoNotCheckAccess, V8DOMConfiguration::kAllWorlds},
@@ -12342,8 +12384,10 @@
 
 TestObject* NativeValueTraits<TestObject>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestObject* nativeValue = V8TestObject::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestObject.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestObject"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.h b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.h
index 39b5333..b4e77b9 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.h
@@ -425,6 +425,7 @@
   CORE_EXPORT static void voidMethodNodeArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void arrayBufferMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void arrayBufferViewMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
+  CORE_EXPORT static void arrayBufferViewMethodRaisesExceptionMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void float32ArrayMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void int32ArrayMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void uint8ArrayMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperations.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperations.cpp
index 8173b94..a91db45 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperations.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperations.cpp
@@ -214,8 +214,10 @@
 
 TestSpecialOperations* NativeValueTraits<TestSpecialOperations>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestSpecialOperations* nativeValue = V8TestSpecialOperations::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestSpecialOperations.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestSpecialOperations"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperationsNotEnumerable.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperationsNotEnumerable.cpp
index 48c66545..03542db6 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperationsNotEnumerable.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperationsNotEnumerable.cpp
@@ -128,8 +128,10 @@
 
 TestSpecialOperationsNotEnumerable* NativeValueTraits<TestSpecialOperationsNotEnumerable>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestSpecialOperationsNotEnumerable* nativeValue = V8TestSpecialOperationsNotEnumerable::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestSpecialOperationsNotEnumerable.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestSpecialOperationsNotEnumerable"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.cpp
index b47994a..f8bc673 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.cpp
@@ -532,8 +532,10 @@
 
 TestTypedefs* NativeValueTraits<TestTypedefs>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestTypedefs* nativeValue = V8TestTypedefs::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestTypedefs.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestTypedefs"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8Uint8ClampedArray.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8Uint8ClampedArray.cpp
index ae096db7..9bfcf56b 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8Uint8ClampedArray.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8Uint8ClampedArray.cpp
@@ -74,8 +74,10 @@
 
 TestUint8ClampedArray* NativeValueTraits<TestUint8ClampedArray>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestUint8ClampedArray* nativeValue = V8Uint8ClampedArray::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to Uint8ClampedArray.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "Uint8ClampedArray"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8Uint8ClampedArray.h b/third_party/WebKit/Source/bindings/tests/results/core/V8Uint8ClampedArray.h
index 5595eed..806bac0 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8Uint8ClampedArray.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8Uint8ClampedArray.h
@@ -23,6 +23,7 @@
 #include "core/CoreExport.h"
 #include "core/dom/DOMTypedArray.h"
 #include "core/dom/FlexibleArrayBufferView.h"
+#include "core/dom/NotShared.h"
 #include "platform/heap/Handle.h"
 
 namespace blink {
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunction.cpp b/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunction.cpp
index 162383d..e0f871c 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunction.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunction.cpp
@@ -80,8 +80,10 @@
 
 VoidCallbackFunction* NativeValueTraits<VoidCallbackFunction>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   VoidCallbackFunction* nativeValue = VoidCallbackFunction::Create(ScriptState::Current(isolate), value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to VoidCallbackFunction.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "VoidCallbackFunction"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunctionInterfaceArg.cpp b/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunctionInterfaceArg.cpp
index 4f51ed91b..1d7c61b 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunctionInterfaceArg.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunctionInterfaceArg.cpp
@@ -82,8 +82,10 @@
 
 VoidCallbackFunctionInterfaceArg* NativeValueTraits<VoidCallbackFunctionInterfaceArg>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   VoidCallbackFunctionInterfaceArg* nativeValue = VoidCallbackFunctionInterfaceArg::Create(ScriptState::Current(isolate), value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to VoidCallbackFunctionInterfaceArg.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "VoidCallbackFunctionInterfaceArg"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunctionTypedef.cpp b/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunctionTypedef.cpp
index bb6480bf..c366b8840 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunctionTypedef.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunctionTypedef.cpp
@@ -83,8 +83,10 @@
 
 VoidCallbackFunctionTypedef* NativeValueTraits<VoidCallbackFunctionTypedef>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   VoidCallbackFunctionTypedef* nativeValue = VoidCallbackFunctionTypedef::Create(ScriptState::Current(isolate), value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to VoidCallbackFunctionTypedef.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "VoidCallbackFunctionTypedef"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.cpp b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.cpp
index bc00c37..c451091 100644
--- a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.cpp
@@ -879,8 +879,10 @@
 
 TestInterface5Implementation* NativeValueTraits<TestInterface5Implementation>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   TestInterface5Implementation* nativeValue = V8TestInterface5::toImplWithTypeCheck(isolate, value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to TestInterface5.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestInterface5"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/modules/VoidCallbackFunctionModules.cpp b/third_party/WebKit/Source/bindings/tests/results/modules/VoidCallbackFunctionModules.cpp
index 0b89961..fdfd5fef 100644
--- a/third_party/WebKit/Source/bindings/tests/results/modules/VoidCallbackFunctionModules.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/modules/VoidCallbackFunctionModules.cpp
@@ -80,8 +80,10 @@
 
 VoidCallbackFunctionModules* NativeValueTraits<VoidCallbackFunctionModules>::NativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
   VoidCallbackFunctionModules* nativeValue = VoidCallbackFunctionModules::Create(ScriptState::Current(isolate), value);
-  if (!nativeValue)
-    exceptionState.ThrowTypeError("Unable to convert value to VoidCallbackFunctionModules.");
+  if (!nativeValue) {
+    exceptionState.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "VoidCallbackFunctionModules"));
+  }
   return nativeValue;
 }
 
diff --git a/third_party/WebKit/Source/core/css/FontFace.cpp b/third_party/WebKit/Source/core/css/FontFace.cpp
index 8070741..9b9d25e 100644
--- a/third_party/WebKit/Source/core/css/FontFace.cpp
+++ b/third_party/WebKit/Source/core/css/FontFace.cpp
@@ -82,8 +82,10 @@
     return Create(context, family, source.getAsString(), descriptors);
   if (source.isArrayBuffer())
     return Create(context, family, source.getAsArrayBuffer(), descriptors);
-  if (source.isArrayBufferView())
-    return Create(context, family, source.getAsArrayBufferView(), descriptors);
+  if (source.isArrayBufferView()) {
+    return Create(context, family, source.getAsArrayBufferView().View(),
+                  descriptors);
+  }
   NOTREACHED();
   return nullptr;
 }
diff --git a/third_party/WebKit/Source/core/dom/DOMArrayPiece.cpp b/third_party/WebKit/Source/core/dom/DOMArrayPiece.cpp
index 30f25b6dd..f95b19e 100644
--- a/third_party/WebKit/Source/core/dom/DOMArrayPiece.cpp
+++ b/third_party/WebKit/Source/core/dom/DOMArrayPiece.cpp
@@ -16,7 +16,7 @@
     InitWithData(array_buffer->Data(), array_buffer->ByteLength());
   } else if (array_buffer_or_view.isArrayBufferView()) {
     DOMArrayBufferView* array_buffer_view =
-        array_buffer_or_view.getAsArrayBufferView();
+        array_buffer_or_view.getAsArrayBufferView().View();
     InitWithData(array_buffer_view->BaseAddress(),
                  array_buffer_view->byteLength());
   } else if (array_buffer_or_view.isNull() &&
diff --git a/third_party/WebKit/Source/core/dom/NotShared.h b/third_party/WebKit/Source/core/dom/NotShared.h
new file mode 100644
index 0000000..69173aa
--- /dev/null
+++ b/third_party/WebKit/Source/core/dom/NotShared.h
@@ -0,0 +1,68 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NotShared_h
+#define NotShared_h
+
+// A wrapper template type that is used to ensure that a TypedArray is not
+// backed by a SharedArrayBuffer.
+//
+// Typically this is used as an annotation on C++ functions that are called by
+// the bindings layer, e.g.:
+//
+//   void Foo(NotShared<DOMUint32Array> param) {
+//     DOMUint32Array* array = param.View();
+//     ...
+//   }
+
+#include "platform/heap/Handle.h"
+
+namespace blink {
+
+template <typename T>
+class NotShared {
+  STACK_ALLOCATED();
+
+ public:
+  using TypedArrayType = T;
+
+  NotShared() {}
+
+  explicit NotShared(T* typedArray) : typed_array_(typedArray) {
+    DCHECK(!(typedArray && typedArray->View()->IsShared()));
+  }
+  NotShared(const NotShared& other) = default;
+  template <typename U>
+  NotShared(const NotShared<U>& other) : typed_array_(other.View()) {}
+  template <typename U>
+  NotShared(const Member<U>& other) {
+    DCHECK(!other->View()->IsShared());
+    typed_array_ = other.Get();
+  }
+
+  NotShared& operator=(const NotShared& other) = default;
+  template <typename U>
+  NotShared& operator=(const NotShared<U>& other) {
+    typed_array_ = other.View();
+    return *this;
+  }
+
+  T* View() const { return typed_array_.Get(); }
+
+  bool operator!() const { return !typed_array_; }
+  explicit operator bool() const { return !!typed_array_; }
+
+ private:
+  // Must use an untraced member here since this object may be constructed on a
+  // thread without a ThreadState (e.g. an Audio worklet). It is safe in that
+  // case because the pointed-to ArrayBuffer is being kept alive another way
+  // (e.g. CrossThreadPersistent).
+  //
+  // TODO(binji): update to using Member, see crbug.com/710295.
+  UntracedMember<T> typed_array_;
+};
+
+}  // namespace blink
+
+#endif  // NotShared_h
diff --git a/third_party/WebKit/Source/core/fileapi/Blob.cpp b/third_party/WebKit/Source/core/fileapi/Blob.cpp
index 79f2741c..dfb0b18d 100644
--- a/third_party/WebKit/Source/core/fileapi/Blob.cpp
+++ b/third_party/WebKit/Source/core/fileapi/Blob.cpp
@@ -132,7 +132,8 @@
       DOMArrayBuffer* array_buffer = item.getAsArrayBuffer();
       blob_data->AppendBytes(array_buffer->Data(), array_buffer->ByteLength());
     } else if (item.isArrayBufferView()) {
-      DOMArrayBufferView* array_buffer_view = item.getAsArrayBufferView();
+      DOMArrayBufferView* array_buffer_view =
+          item.getAsArrayBufferView().View();
       blob_data->AppendBytes(array_buffer_view->BaseAddress(),
                              array_buffer_view->byteLength());
     } else if (item.isBlob()) {
diff --git a/third_party/WebKit/Source/core/frame/Deprecation.cpp b/third_party/WebKit/Source/core/frame/Deprecation.cpp
index 61d203d2..e6426cf0 100644
--- a/third_party/WebKit/Source/core/frame/Deprecation.cpp
+++ b/third_party/WebKit/Source/core/frame/Deprecation.cpp
@@ -15,11 +15,10 @@
 namespace {
 
 enum Milestone {
-  M56,
-  M57,
   M58,
   M59,
   M60,
+  M61,
 };
 
 const char* milestoneString(Milestone milestone) {
@@ -27,16 +26,14 @@
   // https://www.chromium.org/developers/calendar
 
   switch (milestone) {
-    case M56:
-      return "M56, around January 2017";
-    case M57:
-      return "M57, around March 2017";
     case M58:
       return "M58, around April 2017";
     case M59:
       return "M59, around June 2017";
     case M60:
       return "M60, around August 2017";
+    case M61:
+      return "M61, around September 2017";
   }
 
   ASSERT_NOT_REACHED();
@@ -354,7 +351,16 @@
           "deprecated and will be removed in %s. You should consider "
           "switching your application to a secure origin, such as HTTPS. See "
           "https://goo.gl/rStTGz for more details.",
-          milestoneString(M60));
+          milestoneString(M61));
+
+    case UseCounter::kNotificationPermissionRequestedIframe:
+      return String::Format(
+          "Using the Notification API from an iframe is deprecated and will "
+          "be removed in %s. You should consider requesting permission from "
+          "the top-level frame or opening a new window instead. See "
+          "https://www.chromestatus.com/feature/6451284559265792 for more "
+          "details.",
+          milestoneString(M61));
 
     case UseCounter::kElementCreateShadowRootMultiple:
       return "Calling Element.createShadowRoot() for an element which already "
diff --git a/third_party/WebKit/Source/core/frame/ImageBitmapTest.cpp b/third_party/WebKit/Source/core/frame/ImageBitmapTest.cpp
index a59e6a6e..96ebafa3 100644
--- a/third_party/WebKit/Source/core/frame/ImageBitmapTest.cpp
+++ b/third_party/WebKit/Source/core/frame/ImageBitmapTest.cpp
@@ -498,7 +498,8 @@
 TEST_F(ImageBitmapTest, ImageBitmapColorSpaceConversionImageData) {
   unsigned char data_buffer[4] = {255, 0, 0, 255};
   DOMUint8ClampedArray* data = DOMUint8ClampedArray::Create(data_buffer, 4);
-  ImageData* image_data = ImageData::Create(IntSize(1, 1), data);
+  ImageData* image_data =
+      ImageData::Create(IntSize(1, 1), NotShared<DOMUint8ClampedArray>(data));
   std::unique_ptr<uint8_t[]> src_pixel(new uint8_t[4]());
   memcpy(src_pixel.get(), image_data->data()->Data(), 4);
 
diff --git a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.cpp b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.cpp
index 053ddde..392d868 100644
--- a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.cpp
+++ b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.cpp
@@ -40,11 +40,6 @@
                                   digest.size(), kBase64DoNotInsertLFs);
 }
 
-template <typename CharType>
-inline bool IsASCIIAlphanumericOrHyphen(CharType c) {
-  return IsASCIIAlphanumeric(c) || c == '-';
-}
-
 ContentSecurityPolicyHashAlgorithm ConvertHashAlgorithmToCSPHashAlgorithm(
     HashAlgorithm algorithm) {
   switch (algorithm) {
diff --git a/third_party/WebKit/Source/core/geometry/DOMMatrix.cpp b/third_party/WebKit/Source/core/geometry/DOMMatrix.cpp
index 799107e0..3dc15d7 100644
--- a/third_party/WebKit/Source/core/geometry/DOMMatrix.cpp
+++ b/third_party/WebKit/Source/core/geometry/DOMMatrix.cpp
@@ -39,26 +39,30 @@
   return new DOMMatrix(sequence, sequence.size());
 }
 
-DOMMatrix* DOMMatrix::fromFloat32Array(DOMFloat32Array* float32_array,
+DOMMatrix* DOMMatrix::fromFloat32Array(NotShared<DOMFloat32Array> float32_array,
                                        ExceptionState& exception_state) {
-  if (float32_array->length() != 6 && float32_array->length() != 16) {
+  if (float32_array.View()->length() != 6 &&
+      float32_array.View()->length() != 16) {
     exception_state.ThrowTypeError(
         "The sequence must contain 6 elements for a 2D matrix or 16 elements "
         "for a 3D matrix.");
     return nullptr;
   }
-  return new DOMMatrix(float32_array->Data(), float32_array->length());
+  return new DOMMatrix(float32_array.View()->Data(),
+                       float32_array.View()->length());
 }
 
-DOMMatrix* DOMMatrix::fromFloat64Array(DOMFloat64Array* float64_array,
+DOMMatrix* DOMMatrix::fromFloat64Array(NotShared<DOMFloat64Array> float64_array,
                                        ExceptionState& exception_state) {
-  if (float64_array->length() != 6 && float64_array->length() != 16) {
+  if (float64_array.View()->length() != 6 &&
+      float64_array.View()->length() != 16) {
     exception_state.ThrowTypeError(
         "The sequence must contain 6 elements for a 2D matrix or 16 elements "
         "for a 3D matrix.");
     return nullptr;
   }
-  return new DOMMatrix(float64_array->Data(), float64_array->length());
+  return new DOMMatrix(float64_array.View()->Data(),
+                       float64_array.View()->length());
 }
 
 template <typename T>
diff --git a/third_party/WebKit/Source/core/geometry/DOMMatrix.h b/third_party/WebKit/Source/core/geometry/DOMMatrix.h
index eb0a338..eede89ef 100644
--- a/third_party/WebKit/Source/core/geometry/DOMMatrix.h
+++ b/third_party/WebKit/Source/core/geometry/DOMMatrix.h
@@ -6,6 +6,7 @@
 #define DOMMatrix_h
 
 #include "bindings/core/v8/ExceptionState.h"
+#include "core/dom/NotShared.h"
 #include "core/geometry/DOMMatrixInit.h"
 #include "core/geometry/DOMMatrixReadOnly.h"
 
@@ -21,8 +22,10 @@
   static DOMMatrix* Create(const SkMatrix44&, ExceptionState&);
   static DOMMatrix* Create(const String&, ExceptionState&);
   static DOMMatrix* Create(Vector<double>, ExceptionState&);
-  static DOMMatrix* fromFloat32Array(DOMFloat32Array*, ExceptionState&);
-  static DOMMatrix* fromFloat64Array(DOMFloat64Array*, ExceptionState&);
+  static DOMMatrix* fromFloat32Array(NotShared<DOMFloat32Array>,
+                                     ExceptionState&);
+  static DOMMatrix* fromFloat64Array(NotShared<DOMFloat64Array>,
+                                     ExceptionState&);
   static DOMMatrix* fromMatrix(DOMMatrixInit&, ExceptionState&);
 
   void setA(double value) { matrix_->SetM11(value); }
diff --git a/third_party/WebKit/Source/core/geometry/DOMMatrixReadOnly.cpp b/third_party/WebKit/Source/core/geometry/DOMMatrixReadOnly.cpp
index 0d788c76..fd09781 100644
--- a/third_party/WebKit/Source/core/geometry/DOMMatrixReadOnly.cpp
+++ b/third_party/WebKit/Source/core/geometry/DOMMatrixReadOnly.cpp
@@ -115,27 +115,31 @@
 }
 
 DOMMatrixReadOnly* DOMMatrixReadOnly::fromFloat32Array(
-    DOMFloat32Array* float32_array,
+    NotShared<DOMFloat32Array> float32_array,
     ExceptionState& exception_state) {
-  if (float32_array->length() != 6 && float32_array->length() != 16) {
+  if (float32_array.View()->length() != 6 &&
+      float32_array.View()->length() != 16) {
     exception_state.ThrowTypeError(
         "The sequence must contain 6 elements for a 2D matrix or 16 elements a "
         "for 3D matrix.");
     return nullptr;
   }
-  return new DOMMatrixReadOnly(float32_array->Data(), float32_array->length());
+  return new DOMMatrixReadOnly(float32_array.View()->Data(),
+                               float32_array.View()->length());
 }
 
 DOMMatrixReadOnly* DOMMatrixReadOnly::fromFloat64Array(
-    DOMFloat64Array* float64_array,
+    NotShared<DOMFloat64Array> float64_array,
     ExceptionState& exception_state) {
-  if (float64_array->length() != 6 && float64_array->length() != 16) {
+  if (float64_array.View()->length() != 6 &&
+      float64_array.View()->length() != 16) {
     exception_state.ThrowTypeError(
         "The sequence must contain 6 elements for a 2D matrix or 16 elements "
         "for a 3D matrix.");
     return nullptr;
   }
-  return new DOMMatrixReadOnly(float64_array->Data(), float64_array->length());
+  return new DOMMatrixReadOnly(float64_array.View()->Data(),
+                               float64_array.View()->length());
 }
 
 DOMMatrixReadOnly* DOMMatrixReadOnly::fromMatrix(
@@ -275,7 +279,7 @@
   is2d_ = is2d;
 }
 
-DOMFloat32Array* DOMMatrixReadOnly::toFloat32Array() const {
+NotShared<DOMFloat32Array> DOMMatrixReadOnly::toFloat32Array() const {
   float array[] = {
       static_cast<float>(matrix_->M11()), static_cast<float>(matrix_->M12()),
       static_cast<float>(matrix_->M13()), static_cast<float>(matrix_->M14()),
@@ -286,17 +290,17 @@
       static_cast<float>(matrix_->M41()), static_cast<float>(matrix_->M42()),
       static_cast<float>(matrix_->M43()), static_cast<float>(matrix_->M44())};
 
-  return DOMFloat32Array::Create(array, 16);
+  return NotShared<DOMFloat32Array>(DOMFloat32Array::Create(array, 16));
 }
 
-DOMFloat64Array* DOMMatrixReadOnly::toFloat64Array() const {
+NotShared<DOMFloat64Array> DOMMatrixReadOnly::toFloat64Array() const {
   double array[] = {
       matrix_->M11(), matrix_->M12(), matrix_->M13(), matrix_->M14(),
       matrix_->M21(), matrix_->M22(), matrix_->M23(), matrix_->M24(),
       matrix_->M31(), matrix_->M32(), matrix_->M33(), matrix_->M34(),
       matrix_->M41(), matrix_->M42(), matrix_->M43(), matrix_->M44()};
 
-  return DOMFloat64Array::Create(array, 16);
+  return NotShared<DOMFloat64Array>(DOMFloat64Array::Create(array, 16));
 }
 
 const String DOMMatrixReadOnly::toString() const {
diff --git a/third_party/WebKit/Source/core/geometry/DOMMatrixReadOnly.h b/third_party/WebKit/Source/core/geometry/DOMMatrixReadOnly.h
index dcb42ea..708a07e 100644
--- a/third_party/WebKit/Source/core/geometry/DOMMatrixReadOnly.h
+++ b/third_party/WebKit/Source/core/geometry/DOMMatrixReadOnly.h
@@ -9,6 +9,7 @@
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/ScriptWrappable.h"
 #include "core/dom/DOMTypedArray.h"
+#include "core/dom/NotShared.h"
 #include "platform/heap/Handle.h"
 #include "platform/transforms/TransformationMatrix.h"
 
@@ -28,8 +29,10 @@
   static DOMMatrixReadOnly* Create(ExceptionState&);
   static DOMMatrixReadOnly* Create(const String&, ExceptionState&);
   static DOMMatrixReadOnly* Create(Vector<double>, ExceptionState&);
-  static DOMMatrixReadOnly* fromFloat32Array(DOMFloat32Array*, ExceptionState&);
-  static DOMMatrixReadOnly* fromFloat64Array(DOMFloat64Array*, ExceptionState&);
+  static DOMMatrixReadOnly* fromFloat32Array(NotShared<DOMFloat32Array>,
+                                             ExceptionState&);
+  static DOMMatrixReadOnly* fromFloat64Array(NotShared<DOMFloat64Array>,
+                                             ExceptionState&);
   static DOMMatrixReadOnly* fromMatrix(DOMMatrixInit&, ExceptionState&);
   virtual ~DOMMatrixReadOnly();
 
@@ -89,8 +92,8 @@
 
   DOMPoint* transformPoint(const DOMPointInit&);
 
-  DOMFloat32Array* toFloat32Array() const;
-  DOMFloat64Array* toFloat64Array() const;
+  NotShared<DOMFloat32Array> toFloat32Array() const;
+  NotShared<DOMFloat64Array> toFloat64Array() const;
 
   const String toString() const;
 
diff --git a/third_party/WebKit/Source/core/html/ImageData.cpp b/third_party/WebKit/Source/core/html/ImageData.cpp
index 91a16a09..b42e161b 100644
--- a/third_party/WebKit/Source/core/html/ImageData.cpp
+++ b/third_party/WebKit/Source/core/html/ImageData.cpp
@@ -229,12 +229,13 @@
 }
 
 ImageData* ImageData::Create(const IntSize& size,
-                             DOMArrayBufferView* data_array,
+                             NotShared<DOMArrayBufferView> data_array,
                              const ImageDataColorSettings* color_settings) {
-  if (!ImageData::ValidateConstructorArguments(
-          kParamSize | kParamData, &size, 0, 0, data_array, color_settings))
+  if (!ImageData::ValidateConstructorArguments(kParamSize | kParamData, &size,
+                                               0, 0, data_array.View(),
+                                               color_settings))
     return nullptr;
-  return new ImageData(size, data_array, color_settings);
+  return new ImageData(size, data_array.View(), color_settings);
 }
 
 ImageData* ImageData::Create(unsigned width,
@@ -251,28 +252,28 @@
                     : nullptr;
 }
 
-ImageData* ImageData::Create(DOMUint8ClampedArray* data,
+ImageData* ImageData::Create(NotShared<DOMUint8ClampedArray> data,
                              unsigned width,
                              ExceptionState& exception_state) {
   if (!ImageData::ValidateConstructorArguments(kParamData | kParamWidth,
-                                               nullptr, width, 0, data, nullptr,
-                                               &exception_state))
+                                               nullptr, width, 0, data.View(),
+                                               nullptr, &exception_state))
     return nullptr;
 
-  unsigned height = data->length() / (width * 4);
-  return new ImageData(IntSize(width, height), data);
+  unsigned height = data.View()->length() / (width * 4);
+  return new ImageData(IntSize(width, height), data.View());
 }
 
-ImageData* ImageData::Create(DOMUint8ClampedArray* data,
+ImageData* ImageData::Create(NotShared<DOMUint8ClampedArray> data,
                              unsigned width,
                              unsigned height,
                              ExceptionState& exception_state) {
   if (!ImageData::ValidateConstructorArguments(
-          kParamData | kParamWidth | kParamHeight, nullptr, width, height, data,
-          nullptr, &exception_state))
+          kParamData | kParamWidth | kParamHeight, nullptr, width, height,
+          data.View(), nullptr, &exception_state))
     return nullptr;
 
-  return new ImageData(IntSize(width, height), data);
+  return new ImageData(IntSize(width, height), data.View());
 }
 
 ImageData* ImageData::createImageData(
@@ -308,13 +309,13 @@
   String storage_format_name;
 
   if (data.isUint8ClampedArray()) {
-    buffer_view = data.getAsUint8ClampedArray();
+    buffer_view = data.getAsUint8ClampedArray().View();
     storage_format_name = kUint8ClampedArrayStorageFormatName;
   } else if (data.isUint16Array()) {
-    buffer_view = data.getAsUint16Array();
+    buffer_view = data.getAsUint16Array().View();
     storage_format_name = kUint16ArrayStorageFormatName;
   } else if (data.isFloat32Array()) {
-    buffer_view = data.getAsFloat32Array();
+    buffer_view = data.getAsFloat32Array().View();
     storage_format_name = kFloat32ArrayStorageFormatName;
   } else {
     NOTREACHED();
diff --git a/third_party/WebKit/Source/core/html/ImageData.h b/third_party/WebKit/Source/core/html/ImageData.h
index e0ffd1d2..86f5a59 100644
--- a/third_party/WebKit/Source/core/html/ImageData.h
+++ b/third_party/WebKit/Source/core/html/ImageData.h
@@ -33,6 +33,7 @@
 #include "bindings/core/v8/Uint8ClampedArrayOrUint16ArrayOrFloat32Array.h"
 #include "core/CoreExport.h"
 #include "core/dom/DOMTypedArray.h"
+#include "core/dom/NotShared.h"
 #include "core/html/ImageDataColorSettings.h"
 #include "core/html/canvas/CanvasRenderingContext.h"
 #include "core/imagebitmap/ImageBitmapSource.h"
@@ -77,14 +78,14 @@
   static ImageData* Create(const IntSize&,
                            const ImageDataColorSettings* = nullptr);
   static ImageData* Create(const IntSize&,
-                           DOMArrayBufferView*,
+                           NotShared<DOMArrayBufferView>,
                            const ImageDataColorSettings* = nullptr);
 
   static ImageData* Create(unsigned width, unsigned height, ExceptionState&);
-  static ImageData* Create(DOMUint8ClampedArray*,
+  static ImageData* Create(NotShared<DOMUint8ClampedArray>,
                            unsigned width,
                            ExceptionState&);
-  static ImageData* Create(DOMUint8ClampedArray*,
+  static ImageData* Create(NotShared<DOMUint8ClampedArray>,
                            unsigned width,
                            unsigned height,
                            ExceptionState&);
diff --git a/third_party/WebKit/Source/core/mojo/MojoHandle.cpp b/third_party/WebKit/Source/core/mojo/MojoHandle.cpp
index aed10b7..7cab227 100644
--- a/third_party/WebKit/Source/core/mojo/MojoHandle.cpp
+++ b/third_party/WebKit/Source/core/mojo/MojoHandle.cpp
@@ -62,7 +62,7 @@
     bytes = array->Data();
     num_bytes = array->ByteLength();
   } else {
-    DOMArrayBufferView* view = buffer.getAsArrayBufferView();
+    DOMArrayBufferView* view = buffer.getAsArrayBufferView().View();
     bytes = view->BaseAddress();
     num_bytes = view->byteLength();
   }
@@ -118,7 +118,7 @@
     elements = array->Data();
     num_bytes = array->ByteLength();
   } else {
-    DOMArrayBufferView* view = buffer.getAsArrayBufferView();
+    DOMArrayBufferView* view = buffer.getAsArrayBufferView().View();
     elements = view->BaseAddress();
     num_bytes = view->byteLength();
   }
@@ -166,7 +166,7 @@
     elements = array->Data();
     num_bytes = array->ByteLength();
   } else {
-    DOMArrayBufferView* view = buffer.getAsArrayBufferView();
+    DOMArrayBufferView* view = buffer.getAsArrayBufferView().View();
     elements = view->BaseAddress();
     num_bytes = view->byteLength();
   }
diff --git a/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp b/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
index 91265c49..9864143 100644
--- a/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
+++ b/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
@@ -714,7 +714,7 @@
   }
 
   if (body.isArrayBufferView()) {
-    send(body.getAsArrayBufferView(), exception_state);
+    send(body.getAsArrayBufferView().View(), exception_state);
     return;
   }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/network/BlockedURLsPane.js b/third_party/WebKit/Source/devtools/front_end/network/BlockedURLsPane.js
index 70daa6ec..35c0f73 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/BlockedURLsPane.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/BlockedURLsPane.js
@@ -175,7 +175,7 @@
    */
   _update() {
     var enabled = this._manager.blockingEnabled();
-    this._list.element.classList.toggle('blocking-disabled', !enabled);
+    this._list.element.classList.toggle('blocking-disabled', !enabled && !!this._manager.blockedPatterns().length);
     this._enabledCheckbox.setChecked(enabled);
     this._list.clear();
     for (var pattern of this._manager.blockedPatterns())
diff --git a/third_party/WebKit/Source/modules/beacon/NavigatorBeacon.cpp b/third_party/WebKit/Source/modules/beacon/NavigatorBeacon.cpp
index 829cf3c..8acd932b 100644
--- a/third_party/WebKit/Source/modules/beacon/NavigatorBeacon.cpp
+++ b/third_party/WebKit/Source/modules/beacon/NavigatorBeacon.cpp
@@ -118,7 +118,7 @@
   if (data.isArrayBufferView()) {
     allowed =
         PingLoader::SendBeacon(GetSupplementable()->GetFrame(), allowance, url,
-                               data.getAsArrayBufferView(), beacon_size);
+                               data.getAsArrayBufferView().View(), beacon_size);
   } else if (data.isBlob()) {
     Blob* blob = data.getAsBlob();
     if (!FetchUtils::IsSimpleContentType(AtomicString(blob->type()))) {
diff --git a/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp b/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp
index 9653160..ccc37be 100644
--- a/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp
+++ b/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp
@@ -9,6 +9,7 @@
 #include "bindings/core/v8/ScriptState.h"
 #include "core/css/cssom/CSSURLImageValue.h"
 #include "core/css/parser/CSSParser.h"
+#include "core/dom/NotShared.h"
 #include "core/frame/ImageBitmap.h"
 #include "core/html/HTMLCanvasElement.h"
 #include "core/html/HTMLImageElement.h"
@@ -1612,9 +1613,10 @@
   NeedsFinalizeFrame();
 
   DOMArrayBuffer* array_buffer = DOMArrayBuffer::Create(contents);
-  return ImageData::Create(image_data_rect.size(),
-                           DOMUint8ClampedArray::Create(
-                               array_buffer, 0, array_buffer->ByteLength()));
+  return ImageData::Create(
+      image_data_rect.size(),
+      NotShared<DOMUint8ClampedArray>(DOMUint8ClampedArray::Create(
+          array_buffer, 0, array_buffer->ByteLength())));
 }
 
 void BaseRenderingContext2D::putImageData(ImageData* data,
diff --git a/third_party/WebKit/Source/modules/crypto/Crypto.cpp b/third_party/WebKit/Source/modules/crypto/Crypto.cpp
index 483f20e7..0c91536 100644
--- a/third_party/WebKit/Source/modules/crypto/Crypto.cpp
+++ b/third_party/WebKit/Source/modules/crypto/Crypto.cpp
@@ -50,27 +50,29 @@
 
 }  // namespace
 
-DOMArrayBufferView* Crypto::getRandomValues(DOMArrayBufferView* array,
-                                            ExceptionState& exception_state) {
-  ASSERT(array);
-  if (!IsIntegerArray(array)) {
+NotShared<DOMArrayBufferView> Crypto::getRandomValues(
+    NotShared<DOMArrayBufferView> array,
+    ExceptionState& exception_state) {
+  DCHECK(array);
+  if (!IsIntegerArray(array.View())) {
     exception_state.ThrowDOMException(
         kTypeMismatchError,
         String::Format("The provided ArrayBufferView is of type '%s', which is "
                        "not an integer array type.",
-                       array->TypeName()));
-    return nullptr;
+                       array.View()->TypeName()));
+    return NotShared<DOMArrayBufferView>(nullptr);
   }
-  if (array->byteLength() > 65536) {
+  if (array.View()->byteLength() > 65536) {
     exception_state.ThrowDOMException(
         kQuotaExceededError,
         String::Format("The ArrayBufferView's byte length (%u) exceeds the "
                        "number of bytes of entropy available via this API "
                        "(65536).",
-                       array->byteLength()));
-    return nullptr;
+                       array.View()->byteLength()));
+    return NotShared<DOMArrayBufferView>(nullptr);
   }
-  CryptographicallyRandomValues(array->BaseAddress(), array->byteLength());
+  CryptographicallyRandomValues(array.View()->BaseAddress(),
+                                array.View()->byteLength());
   return array;
 }
 
diff --git a/third_party/WebKit/Source/modules/crypto/Crypto.h b/third_party/WebKit/Source/modules/crypto/Crypto.h
index b60f2d11..0591217 100644
--- a/third_party/WebKit/Source/modules/crypto/Crypto.h
+++ b/third_party/WebKit/Source/modules/crypto/Crypto.h
@@ -44,7 +44,8 @@
  public:
   static Crypto* Create() { return new Crypto(); }
 
-  DOMArrayBufferView* getRandomValues(DOMArrayBufferView*, ExceptionState&);
+  NotShared<DOMArrayBufferView> getRandomValues(NotShared<DOMArrayBufferView>,
+                                                ExceptionState&);
 
   SubtleCrypto* subtle();
 
diff --git a/third_party/WebKit/Source/modules/crypto/SubtleCrypto.cpp b/third_party/WebKit/Source/modules/crypto/SubtleCrypto.cpp
index c774ea3..d45a653 100644
--- a/third_party/WebKit/Source/modules/crypto/SubtleCrypto.cpp
+++ b/third_party/WebKit/Source/modules/crypto/SubtleCrypto.cpp
@@ -457,7 +457,7 @@
       if (raw_key_data.isArrayBuffer()) {
         key_data = CopyBytes(raw_key_data.getAsArrayBuffer());
       } else if (raw_key_data.isArrayBufferView()) {
-        key_data = CopyBytes(raw_key_data.getAsArrayBufferView());
+        key_data = CopyBytes(raw_key_data.getAsArrayBufferView().View());
       } else {
         result->CompleteWithError(
             kWebCryptoErrorTypeType,
diff --git a/third_party/WebKit/Source/modules/encoding/TextDecoder.cpp b/third_party/WebKit/Source/modules/encoding/TextDecoder.cpp
index e2c5fc9..b552a9bd 100644
--- a/third_party/WebKit/Source/modules/encoding/TextDecoder.cpp
+++ b/third_party/WebKit/Source/modules/encoding/TextDecoder.cpp
@@ -81,9 +81,9 @@
                            ExceptionState& exception_state) {
   ASSERT(!input.isNull());
   if (input.isArrayBufferView()) {
-    const char* start =
-        static_cast<const char*>(input.getAsArrayBufferView()->BaseAddress());
-    size_t length = input.getAsArrayBufferView()->byteLength();
+    const char* start = static_cast<const char*>(
+        input.getAsArrayBufferView().View()->BaseAddress());
+    size_t length = input.getAsArrayBufferView().View()->byteLength();
     return decode(start, length, options, exception_state);
   }
   ASSERT(input.isArrayBuffer());
diff --git a/third_party/WebKit/Source/modules/encoding/TextEncoder.cpp b/third_party/WebKit/Source/modules/encoding/TextEncoder.cpp
index f206788..2c3d23d 100644
--- a/third_party/WebKit/Source/modules/encoding/TextEncoder.cpp
+++ b/third_party/WebKit/Source/modules/encoding/TextEncoder.cpp
@@ -58,7 +58,7 @@
   return name;
 }
 
-DOMUint8Array* TextEncoder::encode(const String& input) {
+NotShared<DOMUint8Array> TextEncoder::encode(const String& input) {
   CString result;
   if (input.Is8Bit())
     result = codec_->Encode(input.Characters8(), input.length(),
@@ -71,7 +71,8 @@
   const unsigned char* unsigned_buffer =
       reinterpret_cast<const unsigned char*>(buffer);
 
-  return DOMUint8Array::Create(unsigned_buffer, result.length());
+  return NotShared<DOMUint8Array>(
+      DOMUint8Array::Create(unsigned_buffer, result.length()));
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/encoding/TextEncoder.h b/third_party/WebKit/Source/modules/encoding/TextEncoder.h
index 21c59997..538fffb 100644
--- a/third_party/WebKit/Source/modules/encoding/TextEncoder.h
+++ b/third_party/WebKit/Source/modules/encoding/TextEncoder.h
@@ -34,6 +34,7 @@
 #include <memory>
 #include "bindings/core/v8/ScriptWrappable.h"
 #include "core/dom/DOMTypedArray.h"
+#include "core/dom/NotShared.h"
 #include "platform/heap/Handle.h"
 #include "platform/wtf/text/TextCodec.h"
 #include "platform/wtf/text/TextEncoding.h"
@@ -54,7 +55,7 @@
 
   // Implement the IDL
   String encoding() const;
-  DOMUint8Array* encode(const String&);
+  NotShared<DOMUint8Array> encode(const String&);
 
   DEFINE_INLINE_TRACE() {}
 
diff --git a/third_party/WebKit/Source/modules/geolocation/BUILD.gn b/third_party/WebKit/Source/modules/geolocation/BUILD.gn
index 6c3a8c7f..fcb4e25 100644
--- a/third_party/WebKit/Source/modules/geolocation/BUILD.gn
+++ b/third_party/WebKit/Source/modules/geolocation/BUILD.gn
@@ -15,9 +15,9 @@
     "GeolocationError.h",
     "GeolocationWatchers.cpp",
     "GeolocationWatchers.h",
-    "Geoposition.h",
     "NavigatorGeolocation.cpp",
     "NavigatorGeolocation.h",
+    "Position.h",
     "PositionCallback.h",
     "PositionError.h",
     "PositionErrorCallback.h",
diff --git a/third_party/WebKit/Source/modules/geolocation/GeoNotifier.cpp b/third_party/WebKit/Source/modules/geolocation/GeoNotifier.cpp
index 00a0658..1cedb2f 100644
--- a/third_party/WebKit/Source/modules/geolocation/GeoNotifier.cpp
+++ b/third_party/WebKit/Source/modules/geolocation/GeoNotifier.cpp
@@ -56,7 +56,7 @@
   timer_.StartOneShot(0, BLINK_FROM_HERE);
 }
 
-void GeoNotifier::RunSuccessCallback(Geoposition* position) {
+void GeoNotifier::RunSuccessCallback(Position* position) {
   success_callback_->handleEvent(position);
 }
 
diff --git a/third_party/WebKit/Source/modules/geolocation/GeoNotifier.h b/third_party/WebKit/Source/modules/geolocation/GeoNotifier.h
index 67f02775c..976aca85 100644
--- a/third_party/WebKit/Source/modules/geolocation/GeoNotifier.h
+++ b/third_party/WebKit/Source/modules/geolocation/GeoNotifier.h
@@ -14,7 +14,7 @@
 namespace blink {
 
 class Geolocation;
-class Geoposition;
+class Position;
 class PositionError;
 
 class GeoNotifier : public GarbageCollectedFinalized<GeoNotifier> {
@@ -40,7 +40,7 @@
   // an interval of 0.
   void SetUseCachedPosition();
 
-  void RunSuccessCallback(Geoposition*);
+  void RunSuccessCallback(Position*);
   void RunErrorCallback(PositionError*);
 
   void StartTimer();
diff --git a/third_party/WebKit/Source/modules/geolocation/Geolocation.cpp b/third_party/WebKit/Source/modules/geolocation/Geolocation.cpp
index f792e058..1006c81 100644
--- a/third_party/WebKit/Source/modules/geolocation/Geolocation.cpp
+++ b/third_party/WebKit/Source/modules/geolocation/Geolocation.cpp
@@ -52,8 +52,7 @@
 const char kFramelessDocumentErrorMessage[] =
     "Geolocation cannot be used in frameless documents";
 
-Geoposition* CreateGeoposition(
-    const device::mojom::blink::Geoposition& position) {
+Position* CreatePosition(const device::mojom::blink::Geoposition& position) {
   Coordinates* coordinates = Coordinates::Create(
       position.latitude, position.longitude,
       // Lowest point on land is at approximately -400 meters.
@@ -61,8 +60,8 @@
       position.altitude_accuracy >= 0., position.altitude_accuracy,
       position.heading >= 0. && position.heading <= 360., position.heading,
       position.speed >= 0., position.speed);
-  return Geoposition::Create(coordinates,
-                             ConvertSecondsToDOMTimeStamp(position.timestamp));
+  return Position::Create(coordinates,
+                          ConvertSecondsToDOMTimeStamp(position.timestamp));
 }
 
 PositionError* CreatePositionError(
@@ -337,7 +336,7 @@
 }
 
 void Geolocation::SendPosition(GeoNotifierVector& notifiers,
-                               Geoposition* position) {
+                               Position* position) {
   for (GeoNotifier* notifier : notifiers)
     notifier->RunSuccessCallback(position);
 }
@@ -537,7 +536,7 @@
     device::mojom::blink::GeopositionPtr position) {
   disconnected_geolocation_service_ = false;
   if (position->valid) {
-    last_position_ = CreateGeoposition(*position);
+    last_position_ = CreatePosition(*position);
     PositionChanged();
   } else {
     HandleError(
diff --git a/third_party/WebKit/Source/modules/geolocation/Geolocation.h b/third_party/WebKit/Source/modules/geolocation/Geolocation.h
index be2f6e7..3a28ed5 100644
--- a/third_party/WebKit/Source/modules/geolocation/Geolocation.h
+++ b/third_party/WebKit/Source/modules/geolocation/Geolocation.h
@@ -34,7 +34,7 @@
 #include "modules/ModulesExport.h"
 #include "modules/geolocation/GeoNotifier.h"
 #include "modules/geolocation/GeolocationWatchers.h"
-#include "modules/geolocation/Geoposition.h"
+#include "modules/geolocation/Position.h"
 #include "modules/geolocation/PositionCallback.h"
 #include "modules/geolocation/PositionError.h"
 #include "modules/geolocation/PositionErrorCallback.h"
@@ -120,7 +120,7 @@
   }
 
   void SendError(GeoNotifierVector&, PositionError*);
-  void SendPosition(GeoNotifierVector&, Geoposition*);
+  void SendPosition(GeoNotifierVector&, Position*);
 
   // Removes notifiers that use a cached position from |notifiers| and
   // if |cached| is not null they are added to it.
@@ -187,7 +187,7 @@
   GeoNotifierSet one_shots_;
   GeolocationWatchers watchers_;
   GeoNotifierSet pending_for_permission_notifiers_;
-  Member<Geoposition> last_position_;
+  Member<Position> last_position_;
 
   // States of Geolocation permission as granted by the embedder. Unknown
   // means that the embedder still has to be asked for the current permission
diff --git a/third_party/WebKit/Source/modules/geolocation/Geoposition.h b/third_party/WebKit/Source/modules/geolocation/Position.h
similarity index 83%
rename from third_party/WebKit/Source/modules/geolocation/Geoposition.h
rename to third_party/WebKit/Source/modules/geolocation/Position.h
index c2419721..d274187 100644
--- a/third_party/WebKit/Source/modules/geolocation/Geoposition.h
+++ b/third_party/WebKit/Source/modules/geolocation/Position.h
@@ -23,8 +23,8 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef Geoposition_h
-#define Geoposition_h
+#ifndef Position_h
+#define Position_h
 
 #include "bindings/core/v8/ScriptWrappable.h"
 #include "modules/EventModules.h"
@@ -34,13 +34,13 @@
 
 namespace blink {
 
-class Geoposition final : public GarbageCollected<Geoposition>,
-                          public ScriptWrappable {
+class Position final : public GarbageCollected<Position>,
+                       public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static Geoposition* Create(Coordinates* coordinates, DOMTimeStamp timestamp) {
-    return new Geoposition(coordinates, timestamp);
+  static Position* Create(Coordinates* coordinates, DOMTimeStamp timestamp) {
+    return new Position(coordinates, timestamp);
   }
 
   DEFINE_INLINE_TRACE() { visitor->Trace(coordinates_); }
@@ -49,7 +49,7 @@
   Coordinates* coords() const { return coordinates_; }
 
  private:
-  Geoposition(Coordinates* coordinates, DOMTimeStamp timestamp)
+  Position(Coordinates* coordinates, DOMTimeStamp timestamp)
       : coordinates_(coordinates), timestamp_(timestamp) {
     DCHECK(coordinates_);
   }
@@ -60,4 +60,4 @@
 
 }  // namespace blink
 
-#endif  // Geoposition_h
+#endif  // Position_h
diff --git a/third_party/WebKit/Source/modules/geolocation/Geoposition.idl b/third_party/WebKit/Source/modules/geolocation/Position.idl
similarity index 94%
rename from third_party/WebKit/Source/modules/geolocation/Geoposition.idl
rename to third_party/WebKit/Source/modules/geolocation/Position.idl
index 1b5e3477..5c46d64 100644
--- a/third_party/WebKit/Source/modules/geolocation/Geoposition.idl
+++ b/third_party/WebKit/Source/modules/geolocation/Position.idl
@@ -23,9 +23,11 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+// https://www.w3.org/TR/geolocation-API/#position_interface
+
 [
     NoInterfaceObject
-] interface Geoposition {
+] interface Position {
     readonly attribute Coordinates coords;
     readonly attribute DOMTimeStamp timestamp;
 };
diff --git a/third_party/WebKit/Source/modules/geolocation/PositionCallback.h b/third_party/WebKit/Source/modules/geolocation/PositionCallback.h
index db0614e..a754851 100644
--- a/third_party/WebKit/Source/modules/geolocation/PositionCallback.h
+++ b/third_party/WebKit/Source/modules/geolocation/PositionCallback.h
@@ -30,13 +30,13 @@
 
 namespace blink {
 
-class Geoposition;
+class Position;
 
 class PositionCallback : public GarbageCollectedFinalized<PositionCallback> {
  public:
   virtual ~PositionCallback() {}
   DEFINE_INLINE_VIRTUAL_TRACE() {}
-  virtual void handleEvent(Geoposition*) = 0;
+  virtual void handleEvent(Position*) = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/geolocation/PositionCallback.idl b/third_party/WebKit/Source/modules/geolocation/PositionCallback.idl
index 64f3803..19e97bf 100644
--- a/third_party/WebKit/Source/modules/geolocation/PositionCallback.idl
+++ b/third_party/WebKit/Source/modules/geolocation/PositionCallback.idl
@@ -23,6 +23,11 @@
  * DAMAGE.
  */
 
+// https://www.w3.org/TR/geolocation-API/#position-callback
+
+// TODO(mcasas, foolip): PositionCallback should be a callback function, not a
+// callback interface, see https://crbug.com/569301
+
 callback interface PositionCallback {
-    void handleEvent(Geoposition position);
+    void handleEvent(Position position);
 };
diff --git a/third_party/WebKit/Source/modules/mediasource/SourceBuffer.cpp b/third_party/WebKit/Source/modules/mediasource/SourceBuffer.cpp
index 724e3ac3..3d52551 100644
--- a/third_party/WebKit/Source/modules/mediasource/SourceBuffer.cpp
+++ b/third_party/WebKit/Source/modules/mediasource/SourceBuffer.cpp
@@ -360,13 +360,15 @@
                        data->ByteLength(), exception_state);
 }
 
-void SourceBuffer::appendBuffer(DOMArrayBufferView* data,
+void SourceBuffer::appendBuffer(NotShared<DOMArrayBufferView> data,
                                 ExceptionState& exception_state) {
-  BLINK_SBLOG << __func__ << " this=" << this << " size=" << data->byteLength();
+  BLINK_SBLOG << __func__ << " this=" << this
+              << " size=" << data.View()->byteLength();
   // Section 3.2 appendBuffer()
   // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
-  AppendBufferInternal(static_cast<const unsigned char*>(data->BaseAddress()),
-                       data->byteLength(), exception_state);
+  AppendBufferInternal(
+      static_cast<const unsigned char*>(data.View()->BaseAddress()),
+      data.View()->byteLength(), exception_state);
 }
 
 void SourceBuffer::abort(ExceptionState& exception_state) {
diff --git a/third_party/WebKit/Source/modules/mediasource/SourceBuffer.h b/third_party/WebKit/Source/modules/mediasource/SourceBuffer.h
index 2a8ad76..6647c0c 100644
--- a/third_party/WebKit/Source/modules/mediasource/SourceBuffer.h
+++ b/third_party/WebKit/Source/modules/mediasource/SourceBuffer.h
@@ -33,6 +33,7 @@
 
 #include <memory>
 #include "bindings/core/v8/ActiveScriptWrappable.h"
+#include "core/dom/NotShared.h"
 #include "core/dom/SuspendableObject.h"
 #include "modules/EventTargetModules.h"
 #include "modules/mediasource/TrackDefaultList.h"
@@ -78,7 +79,7 @@
   double timestampOffset() const;
   void setTimestampOffset(double, ExceptionState&);
   void appendBuffer(DOMArrayBuffer* data, ExceptionState&);
-  void appendBuffer(DOMArrayBufferView* data, ExceptionState&);
+  void appendBuffer(NotShared<DOMArrayBufferView> data, ExceptionState&);
   void abort(ExceptionState&);
   void remove(double start, double end, ExceptionState&);
   double appendWindowStart() const;
diff --git a/third_party/WebKit/Source/modules/modules_idl_files.gni b/third_party/WebKit/Source/modules/modules_idl_files.gni
index 7af8a57..5c51f9a 100644
--- a/third_party/WebKit/Source/modules/modules_idl_files.gni
+++ b/third_party/WebKit/Source/modules/modules_idl_files.gni
@@ -127,7 +127,7 @@
                     "gamepad/GamepadPose.idl",
                     "geolocation/Coordinates.idl",
                     "geolocation/Geolocation.idl",
-                    "geolocation/Geoposition.idl",
+                    "geolocation/Position.idl",
                     "geolocation/PositionCallback.idl",
                     "geolocation/PositionError.idl",
                     "geolocation/PositionErrorCallback.idl",
diff --git a/third_party/WebKit/Source/modules/notifications/Notification.cpp b/third_party/WebKit/Source/modules/notifications/Notification.cpp
index 56b2186..0b801f6 100644
--- a/third_party/WebKit/Source/modules/notifications/Notification.cpp
+++ b/third_party/WebKit/Source/modules/notifications/Notification.cpp
@@ -368,11 +368,12 @@
     Deprecation::CountDeprecation(
         context, UseCounter::kNotificationPermissionRequestedInsecureOrigin);
   }
+
   if (context->IsDocument()) {
     LocalFrame* frame = ToDocument(context)->GetFrame();
     if (frame && !frame->IsMainFrame()) {
-      UseCounter::Count(context,
-                        UseCounter::kNotificationPermissionRequestedIframe);
+      Deprecation::CountDeprecation(
+          context, UseCounter::kNotificationPermissionRequestedIframe);
     }
   }
 
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp b/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
index 9b1f9b0..996611e9 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
@@ -1061,8 +1061,8 @@
       can_make_payment_resolver_->Resolve(false);
       break;
     case CanMakePaymentQueryResult::QUERY_QUOTA_EXCEEDED:
-      can_make_payment_resolver_->Reject(
-          DOMException::Create(kQuotaExceededError, "Query quota exceeded"));
+      can_make_payment_resolver_->Reject(DOMException::Create(
+          kNotAllowedError, "Not allowed to check whether can make payment"));
       break;
   }
 
diff --git a/third_party/WebKit/Source/modules/peerconnection/RTCDataChannel.cpp b/third_party/WebKit/Source/modules/peerconnection/RTCDataChannel.cpp
index 872f471..fcecd8e 100644
--- a/third_party/WebKit/Source/modules/peerconnection/RTCDataChannel.cpp
+++ b/third_party/WebKit/Source/modules/peerconnection/RTCDataChannel.cpp
@@ -227,10 +227,11 @@
   }
 }
 
-void RTCDataChannel::send(DOMArrayBufferView* data,
+void RTCDataChannel::send(NotShared<DOMArrayBufferView> data,
                           ExceptionState& exception_state) {
-  if (!handler_->SendRawData(static_cast<const char*>(data->BaseAddress()),
-                             data->byteLength())) {
+  if (!handler_->SendRawData(
+          static_cast<const char*>(data.View()->BaseAddress()),
+          data.View()->byteLength())) {
     // FIXME: This should not throw an exception but instead forcefully close
     // the data channel.
     ThrowCouldNotSendDataException(exception_state);
diff --git a/third_party/WebKit/Source/modules/peerconnection/RTCDataChannel.h b/third_party/WebKit/Source/modules/peerconnection/RTCDataChannel.h
index ca35fb7..3692b1e 100644
--- a/third_party/WebKit/Source/modules/peerconnection/RTCDataChannel.h
+++ b/third_party/WebKit/Source/modules/peerconnection/RTCDataChannel.h
@@ -28,6 +28,7 @@
 #include <memory>
 #include "base/gtest_prod_util.h"
 #include "bindings/core/v8/ActiveScriptWrappable.h"
+#include "core/dom/NotShared.h"
 #include "core/dom/SuspendableObject.h"
 #include "modules/EventTargetModules.h"
 #include "platform/Timer.h"
@@ -89,7 +90,7 @@
 
   void send(const String&, ExceptionState&);
   void send(DOMArrayBuffer*, ExceptionState&);
-  void send(DOMArrayBufferView*, ExceptionState&);
+  void send(NotShared<DOMArrayBufferView>, ExceptionState&);
   void send(Blob*, ExceptionState&);
 
   void close();
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationConnection.cpp b/third_party/WebKit/Source/modules/presentation/PresentationConnection.cpp
index 2069e04..c2d82a6 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationConnection.cpp
+++ b/third_party/WebKit/Source/modules/presentation/PresentationConnection.cpp
@@ -282,13 +282,14 @@
   HandleMessageQueue();
 }
 
-void PresentationConnection::send(DOMArrayBufferView* array_buffer_view,
-                                  ExceptionState& exception_state) {
-  ASSERT(array_buffer_view);
+void PresentationConnection::send(
+    NotShared<DOMArrayBufferView> array_buffer_view,
+    ExceptionState& exception_state) {
+  DCHECK(array_buffer_view);
   if (!CanSendMessage(exception_state))
     return;
 
-  messages_.push_back(new Message(array_buffer_view->buffer()));
+  messages_.push_back(new Message(array_buffer_view.View()->buffer()));
   HandleMessageQueue();
 }
 
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationConnection.h b/third_party/WebKit/Source/modules/presentation/PresentationConnection.h
index d4ed003..13346e5 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationConnection.h
+++ b/third_party/WebKit/Source/modules/presentation/PresentationConnection.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include "core/dom/ContextLifecycleObserver.h"
+#include "core/dom/NotShared.h"
 #include "core/events/EventTarget.h"
 #include "core/fileapi/Blob.h"
 #include "core/fileapi/FileError.h"
@@ -60,7 +61,7 @@
 
   void send(const String& message, ExceptionState&);
   void send(DOMArrayBuffer*, ExceptionState&);
-  void send(DOMArrayBufferView*, ExceptionState&);
+  void send(NotShared<DOMArrayBufferView>, ExceptionState&);
   void send(Blob*, ExceptionState&);
   void close();
   void terminate();
diff --git a/third_party/WebKit/Source/modules/push_messaging/PushMessageData.cpp b/third_party/WebKit/Source/modules/push_messaging/PushMessageData.cpp
index 5786e6b..454aeef 100644
--- a/third_party/WebKit/Source/modules/push_messaging/PushMessageData.cpp
+++ b/third_party/WebKit/Source/modules/push_messaging/PushMessageData.cpp
@@ -32,9 +32,10 @@
 PushMessageData* PushMessageData::Create(
     const ArrayBufferOrArrayBufferViewOrUSVString& message_data) {
   if (message_data.isArrayBuffer() || message_data.isArrayBufferView()) {
-    DOMArrayBuffer* buffer = message_data.isArrayBufferView()
-                                 ? message_data.getAsArrayBufferView()->buffer()
-                                 : message_data.getAsArrayBuffer();
+    DOMArrayBuffer* buffer =
+        message_data.isArrayBufferView()
+            ? message_data.getAsArrayBufferView().View()->buffer()
+            : message_data.getAsArrayBuffer();
 
     return new PushMessageData(static_cast<const char*>(buffer->Data()),
                                buffer->ByteLength());
diff --git a/third_party/WebKit/Source/modules/push_messaging/PushSubscriptionOptions.cpp b/third_party/WebKit/Source/modules/push_messaging/PushSubscriptionOptions.cpp
index 5505e24..31bef25f 100644
--- a/third_party/WebKit/Source/modules/push_messaging/PushSubscriptionOptions.cpp
+++ b/third_party/WebKit/Source/modules/push_messaging/PushSubscriptionOptions.cpp
@@ -31,9 +31,11 @@
     length = application_server_key.getAsArrayBuffer()->ByteLength();
   } else if (application_server_key.isArrayBufferView()) {
     input = static_cast<unsigned char*>(
-        application_server_key.getAsArrayBufferView()->buffer()->Data());
-    length =
-        application_server_key.getAsArrayBufferView()->buffer()->ByteLength();
+        application_server_key.getAsArrayBufferView().View()->buffer()->Data());
+    length = application_server_key.getAsArrayBufferView()
+                 .View()
+                 ->buffer()
+                 ->ByteLength();
   } else {
     NOTREACHED();
     return String();
diff --git a/third_party/WebKit/Source/modules/sensor/OrientationSensor.cpp b/third_party/WebKit/Source/modules/sensor/OrientationSensor.cpp
index a4d70a4..ed5454c 100644
--- a/third_party/WebKit/Source/modules/sensor/OrientationSensor.cpp
+++ b/third_party/WebKit/Source/modules/sensor/OrientationSensor.cpp
@@ -107,9 +107,9 @@
     Float32ArrayOrFloat64ArrayOrDOMMatrix& matrix,
     ExceptionState& exception_state) {
   if (matrix.isFloat32Array())
-    PopulateMatrixInternal(matrix.getAsFloat32Array(), exception_state);
+    PopulateMatrixInternal(matrix.getAsFloat32Array().View(), exception_state);
   else if (matrix.isFloat64Array())
-    PopulateMatrixInternal(matrix.getAsFloat64Array(), exception_state);
+    PopulateMatrixInternal(matrix.getAsFloat64Array().View(), exception_state);
   else if (matrix.isDOMMatrix())
     PopulateMatrixInternal(matrix.getAsDOMMatrix(), exception_state);
   else
diff --git a/third_party/WebKit/Source/modules/webaudio/AnalyserNode.cpp b/third_party/WebKit/Source/modules/webaudio/AnalyserNode.cpp
index 822e811..55a3109 100644
--- a/third_party/WebKit/Source/modules/webaudio/AnalyserNode.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AnalyserNode.cpp
@@ -265,20 +265,22 @@
   return GetAnalyserHandler().SmoothingTimeConstant();
 }
 
-void AnalyserNode::getFloatFrequencyData(DOMFloat32Array* array) {
-  GetAnalyserHandler().GetFloatFrequencyData(array, context()->currentTime());
+void AnalyserNode::getFloatFrequencyData(NotShared<DOMFloat32Array> array) {
+  GetAnalyserHandler().GetFloatFrequencyData(array.View(),
+                                             context()->currentTime());
 }
 
-void AnalyserNode::getByteFrequencyData(DOMUint8Array* array) {
-  GetAnalyserHandler().GetByteFrequencyData(array, context()->currentTime());
+void AnalyserNode::getByteFrequencyData(NotShared<DOMUint8Array> array) {
+  GetAnalyserHandler().GetByteFrequencyData(array.View(),
+                                            context()->currentTime());
 }
 
-void AnalyserNode::getFloatTimeDomainData(DOMFloat32Array* array) {
-  GetAnalyserHandler().GetFloatTimeDomainData(array);
+void AnalyserNode::getFloatTimeDomainData(NotShared<DOMFloat32Array> array) {
+  GetAnalyserHandler().GetFloatTimeDomainData(array.View());
 }
 
-void AnalyserNode::getByteTimeDomainData(DOMUint8Array* array) {
-  GetAnalyserHandler().GetByteTimeDomainData(array);
+void AnalyserNode::getByteTimeDomainData(NotShared<DOMUint8Array> array) {
+  GetAnalyserHandler().GetByteTimeDomainData(array.View());
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/webaudio/AnalyserNode.h b/third_party/WebKit/Source/modules/webaudio/AnalyserNode.h
index b0dc0da..e124487 100644
--- a/third_party/WebKit/Source/modules/webaudio/AnalyserNode.h
+++ b/third_party/WebKit/Source/modules/webaudio/AnalyserNode.h
@@ -27,6 +27,7 @@
 #define AnalyserNode_h
 
 #include "core/dom/DOMTypedArray.h"
+#include "core/dom/NotShared.h"
 #include "modules/webaudio/AudioBasicInspectorNode.h"
 #include "modules/webaudio/RealtimeAnalyser.h"
 
@@ -111,10 +112,10 @@
   double maxDecibels() const;
   void setSmoothingTimeConstant(double, ExceptionState&);
   double smoothingTimeConstant() const;
-  void getFloatFrequencyData(DOMFloat32Array*);
-  void getByteFrequencyData(DOMUint8Array*);
-  void getFloatTimeDomainData(DOMFloat32Array*);
-  void getByteTimeDomainData(DOMUint8Array*);
+  void getFloatFrequencyData(NotShared<DOMFloat32Array>);
+  void getByteFrequencyData(NotShared<DOMUint8Array>);
+  void getFloatTimeDomainData(NotShared<DOMFloat32Array>);
+  void getByteTimeDomainData(NotShared<DOMUint8Array>);
 
  private:
   AnalyserNode(BaseAudioContext&);
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioBuffer.cpp b/third_party/WebKit/Source/modules/webaudio/AudioBuffer.cpp
index 8d07c3c..190e104 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioBuffer.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AudioBuffer.cpp
@@ -185,33 +185,34 @@
   }
 }
 
-DOMFloat32Array* AudioBuffer::getChannelData(unsigned channel_index,
-                                             ExceptionState& exception_state) {
+NotShared<DOMFloat32Array> AudioBuffer::getChannelData(
+    unsigned channel_index,
+    ExceptionState& exception_state) {
   if (channel_index >= channels_.size()) {
     exception_state.ThrowDOMException(
         kIndexSizeError, "channel index (" + String::Number(channel_index) +
                              ") exceeds number of channels (" +
                              String::Number(channels_.size()) + ")");
-    return nullptr;
+    return NotShared<DOMFloat32Array>(nullptr);
   }
 
   return getChannelData(channel_index);
 }
 
-DOMFloat32Array* AudioBuffer::getChannelData(unsigned channel_index) {
+NotShared<DOMFloat32Array> AudioBuffer::getChannelData(unsigned channel_index) {
   if (channel_index >= channels_.size())
-    return nullptr;
+    return NotShared<DOMFloat32Array>(nullptr);
 
-  return channels_[channel_index].Get();
+  return NotShared<DOMFloat32Array>(channels_[channel_index].Get());
 }
 
-void AudioBuffer::copyFromChannel(DOMFloat32Array* destination,
+void AudioBuffer::copyFromChannel(NotShared<DOMFloat32Array> destination,
                                   long channel_number,
                                   ExceptionState& exception_state) {
   return copyFromChannel(destination, channel_number, 0, exception_state);
 }
 
-void AudioBuffer::copyFromChannel(DOMFloat32Array* destination,
+void AudioBuffer::copyFromChannel(NotShared<DOMFloat32Array> destination,
                                   long channel_number,
                                   unsigned long start_in_channel,
                                   ExceptionState& exception_state) {
@@ -223,6 +224,7 @@
                              ExceptionMessages::kInclusiveBound,
                              static_cast<long>(channels_.size() - 1),
                              ExceptionMessages::kInclusiveBound));
+
     return;
   }
 
@@ -240,10 +242,10 @@
   }
 
   unsigned count = channel_data->length() - start_in_channel;
-  count = std::min(destination->length(), count);
+  count = std::min(destination.View()->length(), count);
 
   const float* src = channel_data->Data();
-  float* dst = destination->Data();
+  float* dst = destination.View()->Data();
 
   DCHECK(src);
   DCHECK(dst);
@@ -251,13 +253,13 @@
   memcpy(dst, src + start_in_channel, count * sizeof(*src));
 }
 
-void AudioBuffer::copyToChannel(DOMFloat32Array* source,
+void AudioBuffer::copyToChannel(NotShared<DOMFloat32Array> source,
                                 long channel_number,
                                 ExceptionState& exception_state) {
   return copyToChannel(source, channel_number, 0, exception_state);
 }
 
-void AudioBuffer::copyToChannel(DOMFloat32Array* source,
+void AudioBuffer::copyToChannel(NotShared<DOMFloat32Array> source,
                                 long channel_number,
                                 unsigned long start_in_channel,
                                 ExceptionState& exception_state) {
@@ -286,9 +288,9 @@
   }
 
   unsigned count = channel_data->length() - start_in_channel;
-  count = std::min(source->length(), count);
+  count = std::min(source.View()->length(), count);
 
-  const float* src = source->Data();
+  const float* src = source.View()->Data();
   float* dst = channel_data->Data();
 
   DCHECK(src);
@@ -299,8 +301,8 @@
 
 void AudioBuffer::Zero() {
   for (unsigned i = 0; i < channels_.size(); ++i) {
-    if (DOMFloat32Array* array = getChannelData(i)) {
-      float* data = array->Data();
+    if (NotShared<DOMFloat32Array> array = getChannelData(i)) {
+      float* data = array.View()->Data();
       memset(data, 0, length() * sizeof(*data));
     }
   }
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioBuffer.h b/third_party/WebKit/Source/modules/webaudio/AudioBuffer.h
index 782b1b9..f42219520 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioBuffer.h
+++ b/third_party/WebKit/Source/modules/webaudio/AudioBuffer.h
@@ -31,6 +31,7 @@
 
 #include "bindings/core/v8/ScriptWrappable.h"
 #include "core/dom/DOMTypedArray.h"
+#include "core/dom/NotShared.h"
 #include "modules/ModulesExport.h"
 #include "platform/wtf/PassRefPtr.h"
 #include "platform/wtf/RefPtr.h"
@@ -74,15 +75,20 @@
 
   // Channel data access
   unsigned numberOfChannels() const { return channels_.size(); }
-  DOMFloat32Array* getChannelData(unsigned channel_index, ExceptionState&);
-  DOMFloat32Array* getChannelData(unsigned channel_index);
-  void copyFromChannel(DOMFloat32Array*, long channel_number, ExceptionState&);
-  void copyFromChannel(DOMFloat32Array*,
+  NotShared<DOMFloat32Array> getChannelData(unsigned channel_index,
+                                            ExceptionState&);
+  NotShared<DOMFloat32Array> getChannelData(unsigned channel_index);
+  void copyFromChannel(NotShared<DOMFloat32Array>,
+                       long channel_number,
+                       ExceptionState&);
+  void copyFromChannel(NotShared<DOMFloat32Array>,
                        long channel_number,
                        unsigned long start_in_channel,
                        ExceptionState&);
-  void copyToChannel(DOMFloat32Array*, long channel_number, ExceptionState&);
-  void copyToChannel(DOMFloat32Array*,
+  void copyToChannel(NotShared<DOMFloat32Array>,
+                     long channel_number,
+                     ExceptionState&);
+  void copyToChannel(NotShared<DOMFloat32Array>,
                      long channel_number,
                      unsigned long start_in_channel,
                      ExceptionState&);
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioBufferSourceNode.cpp b/third_party/WebKit/Source/modules/webaudio/AudioBufferSourceNode.cpp
index 771a7a2..f7316e1 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioBufferSourceNode.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AudioBufferSourceNode.cpp
@@ -425,7 +425,7 @@
     destination_channels_ = WrapArrayUnique(new float*[number_of_channels]);
 
     for (unsigned i = 0; i < number_of_channels; ++i)
-      source_channels_[i] = buffer->getChannelData(i)->Data();
+      source_channels_[i] = buffer->getChannelData(i).View()->Data();
 
     // If this is a grain (as set by a previous call to start()), validate the
     // grain parameters now since it wasn't validated when start was called
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioParam.cpp b/third_party/WebKit/Source/modules/webaudio/AudioParam.cpp
index 85805c51..b3b5be2 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioParam.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AudioParam.cpp
@@ -469,17 +469,17 @@
   return this;
 }
 
-AudioParam* AudioParam::setValueCurveAtTime(DOMFloat32Array* curve,
+AudioParam* AudioParam::setValueCurveAtTime(NotShared<DOMFloat32Array> curve,
                                             double time,
                                             double duration,
                                             ExceptionState& exception_state) {
-  float* curve_data = curve->Data();
+  float* curve_data = curve.View()->Data();
   float min = minValue();
   float max = maxValue();
 
   // First, find any non-finite value in the curve and throw an exception if
   // there are any.
-  for (unsigned k = 0; k < curve->length(); ++k) {
+  for (unsigned k = 0; k < curve.View()->length(); ++k) {
     float value = curve_data[k];
 
     if (!std::isfinite(value)) {
@@ -494,7 +494,7 @@
   // Second, find the first value in the curve (if any) that is outside the
   // nominal range.  It's probably not necessary to produce a warning on every
   // value outside the nominal range.
-  for (unsigned k = 0; k < curve->length(); ++k) {
+  for (unsigned k = 0; k < curve.View()->length(); ++k) {
     float value = curve_data[k];
 
     if (value < min || value > max) {
@@ -503,7 +503,7 @@
     }
   }
 
-  Handler().Timeline().SetValueCurveAtTime(curve, time, duration,
+  Handler().Timeline().SetValueCurveAtTime(curve.View(), time, duration,
                                            exception_state);
 
   // We could update the histogram with every value in the curve, due to
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioParam.h b/third_party/WebKit/Source/modules/webaudio/AudioParam.h
index c393187b..858e12f0 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioParam.h
+++ b/third_party/WebKit/Source/modules/webaudio/AudioParam.h
@@ -32,6 +32,7 @@
 #include <sys/types.h>
 #include "bindings/core/v8/ScriptWrappable.h"
 #include "core/dom/DOMTypedArray.h"
+#include "core/dom/NotShared.h"
 #include "modules/webaudio/AudioParamTimeline.h"
 #include "modules/webaudio/AudioSummingJunction.h"
 #include "modules/webaudio/BaseAudioContext.h"
@@ -246,7 +247,7 @@
                               double time,
                               double time_constant,
                               ExceptionState&);
-  AudioParam* setValueCurveAtTime(DOMFloat32Array* curve,
+  AudioParam* setValueCurveAtTime(NotShared<DOMFloat32Array> curve,
                                   double time,
                                   double duration,
                                   ExceptionState&);
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScopeTest.cpp b/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScopeTest.cpp
index 0f25c173..6e4e965 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScopeTest.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScopeTest.cpp
@@ -218,10 +218,12 @@
         AudioBuffer::Create(1, kRenderQuantumFrames, kTestingSampleRate);
     AudioBuffer* output_buffer =
         AudioBuffer::Create(1, kRenderQuantumFrames, kTestingSampleRate);
-    DOMFloat32Array* input_channel_data = input_buffer->getChannelData(0);
+    DOMFloat32Array* input_channel_data =
+        input_buffer->getChannelData(0).View();
     float* input_array_data = input_channel_data->Data();
     EXPECT_TRUE(input_array_data);
-    DOMFloat32Array* output_channel_data = output_buffer->getChannelData(0);
+    DOMFloat32Array* output_channel_data =
+        output_buffer->getChannelData(0).View();
     float* output_array_data = output_channel_data->Data();
     EXPECT_TRUE(output_array_data);
 
diff --git a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp
index 71c25830..4ff9d1bf 100644
--- a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp
@@ -523,8 +523,8 @@
 }
 
 PeriodicWave* BaseAudioContext::createPeriodicWave(
-    DOMFloat32Array* real,
-    DOMFloat32Array* imag,
+    NotShared<DOMFloat32Array> real,
+    NotShared<DOMFloat32Array> imag,
     ExceptionState& exception_state) {
   DCHECK(IsMainThread());
 
@@ -532,8 +532,8 @@
 }
 
 PeriodicWave* BaseAudioContext::createPeriodicWave(
-    DOMFloat32Array* real,
-    DOMFloat32Array* imag,
+    NotShared<DOMFloat32Array> real,
+    NotShared<DOMFloat32Array> imag,
     const PeriodicWaveConstraints& options,
     ExceptionState& exception_state) {
   DCHECK(IsMainThread());
diff --git a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h
index 6d88f775..a4a6f4f8 100644
--- a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h
+++ b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h
@@ -30,6 +30,7 @@
 #include "bindings/core/v8/ScriptPromise.h"
 #include "bindings/core/v8/ScriptPromiseResolver.h"
 #include "core/dom/DOMTypedArray.h"
+#include "core/dom/NotShared.h"
 #include "core/dom/SuspendableObject.h"
 #include "core/events/EventListener.h"
 #include "modules/EventTargetModules.h"
@@ -220,11 +221,11 @@
   ChannelMergerNode* createChannelMerger(size_t number_of_inputs,
                                          ExceptionState&);
   OscillatorNode* createOscillator(ExceptionState&);
-  PeriodicWave* createPeriodicWave(DOMFloat32Array* real,
-                                   DOMFloat32Array* imag,
+  PeriodicWave* createPeriodicWave(NotShared<DOMFloat32Array> real,
+                                   NotShared<DOMFloat32Array> imag,
                                    ExceptionState&);
-  PeriodicWave* createPeriodicWave(DOMFloat32Array* real,
-                                   DOMFloat32Array* imag,
+  PeriodicWave* createPeriodicWave(NotShared<DOMFloat32Array> real,
+                                   NotShared<DOMFloat32Array> imag,
                                    const PeriodicWaveConstraints&,
                                    ExceptionState&);
 
diff --git a/third_party/WebKit/Source/modules/webaudio/BiquadFilterNode.cpp b/third_party/WebKit/Source/modules/webaudio/BiquadFilterNode.cpp
index 1deb3078..df2d501 100644
--- a/third_party/WebKit/Source/modules/webaudio/BiquadFilterNode.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/BiquadFilterNode.cpp
@@ -172,18 +172,22 @@
   return true;
 }
 
-void BiquadFilterNode::getFrequencyResponse(const DOMFloat32Array* frequency_hz,
-                                            DOMFloat32Array* mag_response,
-                                            DOMFloat32Array* phase_response) {
+void BiquadFilterNode::getFrequencyResponse(
+    NotShared<const DOMFloat32Array> frequency_hz,
+    NotShared<DOMFloat32Array> mag_response,
+    NotShared<DOMFloat32Array> phase_response) {
   DCHECK(frequency_hz);
   DCHECK(mag_response);
   DCHECK(phase_response);
 
-  int n = std::min(frequency_hz->length(),
-                   std::min(mag_response->length(), phase_response->length()));
-  if (n)
-    GetBiquadProcessor()->GetFrequencyResponse(
-        n, frequency_hz->Data(), mag_response->Data(), phase_response->Data());
+  int n = std::min(
+      frequency_hz.View()->length(),
+      std::min(mag_response.View()->length(), phase_response.View()->length()));
+  if (n) {
+    GetBiquadProcessor()->GetFrequencyResponse(n, frequency_hz.View()->Data(),
+                                               mag_response.View()->Data(),
+                                               phase_response.View()->Data());
+  }
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/webaudio/BiquadFilterNode.h b/third_party/WebKit/Source/modules/webaudio/BiquadFilterNode.h
index 4ca7a35..b83c5c7 100644
--- a/third_party/WebKit/Source/modules/webaudio/BiquadFilterNode.h
+++ b/third_party/WebKit/Source/modules/webaudio/BiquadFilterNode.h
@@ -27,6 +27,7 @@
 #define BiquadFilterNode_h
 
 #include "core/dom/DOMTypedArray.h"
+#include "core/dom/NotShared.h"
 #include "modules/webaudio/AudioNode.h"
 #include "modules/webaudio/BiquadProcessor.h"
 
@@ -70,9 +71,9 @@
 
   // Get the magnitude and phase response of the filter at the given
   // set of frequencies (in Hz). The phase response is in radians.
-  void getFrequencyResponse(const DOMFloat32Array* frequency_hz,
-                            DOMFloat32Array* mag_response,
-                            DOMFloat32Array* phase_response);
+  void getFrequencyResponse(NotShared<const DOMFloat32Array> frequency_hz,
+                            NotShared<DOMFloat32Array> mag_response,
+                            NotShared<DOMFloat32Array> phase_response);
 
  private:
   BiquadFilterNode(BaseAudioContext&);
diff --git a/third_party/WebKit/Source/modules/webaudio/ConvolverNode.cpp b/third_party/WebKit/Source/modules/webaudio/ConvolverNode.cpp
index cf4b8155..c59976b 100644
--- a/third_party/WebKit/Source/modules/webaudio/ConvolverNode.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/ConvolverNode.cpp
@@ -131,7 +131,7 @@
   RefPtr<AudioBus> buffer_bus =
       AudioBus::Create(number_of_channels, buffer_length, false);
   for (unsigned i = 0; i < number_of_channels; ++i)
-    buffer_bus->SetChannelMemory(i, buffer->getChannelData(i)->Data(),
+    buffer_bus->SetChannelMemory(i, buffer->getChannelData(i).View()->Data(),
                                  buffer_length);
 
   buffer_bus->SetSampleRate(buffer->sampleRate());
diff --git a/third_party/WebKit/Source/modules/webaudio/IIRFilterNode.cpp b/third_party/WebKit/Source/modules/webaudio/IIRFilterNode.cpp
index d2f05e9..42adea1 100644
--- a/third_party/WebKit/Source/modules/webaudio/IIRFilterNode.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/IIRFilterNode.cpp
@@ -126,49 +126,52 @@
       static_cast<AudioBasicProcessorHandler&>(Handler()).Processor());
 }
 
-void IIRFilterNode::getFrequencyResponse(const DOMFloat32Array* frequency_hz,
-                                         DOMFloat32Array* mag_response,
-                                         DOMFloat32Array* phase_response,
-                                         ExceptionState& exception_state) {
-  if (!frequency_hz) {
+void IIRFilterNode::getFrequencyResponse(
+    NotShared<const DOMFloat32Array> frequency_hz,
+    NotShared<DOMFloat32Array> mag_response,
+    NotShared<DOMFloat32Array> phase_response,
+    ExceptionState& exception_state) {
+  if (!frequency_hz.View()) {
     exception_state.ThrowDOMException(kNotSupportedError,
                                       "frequencyHz array cannot be null");
     return;
   }
 
-  if (!mag_response) {
+  if (!mag_response.View()) {
     exception_state.ThrowDOMException(kNotSupportedError,
                                       "magResponse array cannot be null");
     return;
   }
 
-  if (!phase_response) {
+  if (!phase_response.View()) {
     exception_state.ThrowDOMException(kNotSupportedError,
                                       "phaseResponse array cannot be null");
     return;
   }
 
-  unsigned frequency_hz_length = frequency_hz->length();
+  unsigned frequency_hz_length = frequency_hz.View()->length();
 
-  if (mag_response->length() < frequency_hz_length) {
+  if (mag_response.View()->length() < frequency_hz_length) {
     exception_state.ThrowDOMException(
         kNotSupportedError,
         ExceptionMessages::IndexExceedsMinimumBound(
-            "magResponse length", mag_response->length(), frequency_hz_length));
+            "magResponse length", mag_response.View()->length(),
+            frequency_hz_length));
     return;
   }
 
-  if (phase_response->length() < frequency_hz_length) {
+  if (phase_response.View()->length() < frequency_hz_length) {
     exception_state.ThrowDOMException(
-        kNotSupportedError, ExceptionMessages::IndexExceedsMinimumBound(
-                                "phaseResponse length",
-                                phase_response->length(), frequency_hz_length));
+        kNotSupportedError,
+        ExceptionMessages::IndexExceedsMinimumBound(
+            "phaseResponse length", phase_response.View()->length(),
+            frequency_hz_length));
     return;
   }
 
   IirProcessor()->GetFrequencyResponse(
-      frequency_hz_length, frequency_hz->Data(), mag_response->Data(),
-      phase_response->Data());
+      frequency_hz_length, frequency_hz.View()->Data(),
+      mag_response.View()->Data(), phase_response.View()->Data());
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/webaudio/IIRFilterNode.h b/third_party/WebKit/Source/modules/webaudio/IIRFilterNode.h
index e16b8db..826bd67 100644
--- a/third_party/WebKit/Source/modules/webaudio/IIRFilterNode.h
+++ b/third_party/WebKit/Source/modules/webaudio/IIRFilterNode.h
@@ -6,6 +6,7 @@
 #define IIRFilterNode_h
 
 #include "core/dom/DOMTypedArray.h"
+#include "core/dom/NotShared.h"
 #include "modules/webaudio/AudioNode.h"
 #include "modules/webaudio/IIRProcessor.h"
 
@@ -32,9 +33,9 @@
 
   // Get the magnitude and phase response of the filter at the given
   // set of frequencies (in Hz). The phase response is in radians.
-  void getFrequencyResponse(const DOMFloat32Array* frequency_hz,
-                            DOMFloat32Array* mag_response,
-                            DOMFloat32Array* phase_response,
+  void getFrequencyResponse(NotShared<const DOMFloat32Array> frequency_hz,
+                            NotShared<DOMFloat32Array> mag_response,
+                            NotShared<DOMFloat32Array> phase_response,
                             ExceptionState&);
 
  private:
diff --git a/third_party/WebKit/Source/modules/webaudio/OfflineAudioDestinationNode.cpp b/third_party/WebKit/Source/modules/webaudio/OfflineAudioDestinationNode.cpp
index 9458026..50e3d5b1 100644
--- a/third_party/WebKit/Source/modules/webaudio/OfflineAudioDestinationNode.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/OfflineAudioDestinationNode.cpp
@@ -201,7 +201,7 @@
          ++channel_index) {
       const float* source = render_bus_->Channel(channel_index)->Data();
       float* destination =
-          render_target_->getChannelData(channel_index)->Data();
+          render_target_->getChannelData(channel_index).View()->Data();
       memcpy(destination + frames_processed_, source,
              sizeof(float) * frames_available_to_copy);
     }
diff --git a/third_party/WebKit/Source/modules/webaudio/PeriodicWave.cpp b/third_party/WebKit/Source/modules/webaudio/PeriodicWave.cpp
index d1c45ca..fe93007 100644
--- a/third_party/WebKit/Source/modules/webaudio/PeriodicWave.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/PeriodicWave.cpp
@@ -83,14 +83,15 @@
 }
 
 PeriodicWave* PeriodicWave::Create(BaseAudioContext& context,
-                                   DOMFloat32Array* real,
-                                   DOMFloat32Array* imag,
+                                   NotShared<DOMFloat32Array> real,
+                                   NotShared<DOMFloat32Array> imag,
                                    bool disable_normalization,
                                    ExceptionState& exception_state) {
   DCHECK(IsMainThread());
 
-  return Create(context, real->length(), real->Data(), imag->length(),
-                imag->Data(), disable_normalization, exception_state);
+  return Create(context, real.View()->length(), real.View()->Data(),
+                imag.View()->length(), imag.View()->Data(),
+                disable_normalization, exception_state);
 }
 
 PeriodicWave* PeriodicWave::Create(BaseAudioContext* context,
diff --git a/third_party/WebKit/Source/modules/webaudio/PeriodicWave.h b/third_party/WebKit/Source/modules/webaudio/PeriodicWave.h
index 0a3e261..1e44125 100644
--- a/third_party/WebKit/Source/modules/webaudio/PeriodicWave.h
+++ b/third_party/WebKit/Source/modules/webaudio/PeriodicWave.h
@@ -32,6 +32,7 @@
 #include <memory>
 #include "bindings/core/v8/ScriptWrappable.h"
 #include "core/dom/DOMTypedArray.h"
+#include "core/dom/NotShared.h"
 #include "platform/audio/AudioArray.h"
 #include "platform/wtf/Forward.h"
 #include "platform/wtf/Vector.h"
@@ -63,8 +64,8 @@
                               ExceptionState&);
 
   static PeriodicWave* Create(BaseAudioContext&,
-                              DOMFloat32Array* real,
-                              DOMFloat32Array* imag,
+                              NotShared<DOMFloat32Array> real,
+                              NotShared<DOMFloat32Array> imag,
                               bool normalize,
                               ExceptionState&);
 
diff --git a/third_party/WebKit/Source/modules/webaudio/ScriptProcessorNode.cpp b/third_party/WebKit/Source/modules/webaudio/ScriptProcessorNode.cpp
index b2d7256..4fbd133 100644
--- a/third_party/WebKit/Source/modules/webaudio/ScriptProcessorNode.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/ScriptProcessorNode.cpp
@@ -174,17 +174,21 @@
 
   for (unsigned i = 0; i < number_of_input_channels; ++i)
     internal_input_bus_->SetChannelMemory(
-        i, input_buffer->getChannelData(i)->Data() + buffer_read_write_index_,
+        i,
+        input_buffer->getChannelData(i).View()->Data() +
+            buffer_read_write_index_,
         frames_to_process);
 
   if (number_of_input_channels)
     internal_input_bus_->CopyFrom(*input_bus);
 
   // Copy from the output buffer to the output.
-  for (unsigned i = 0; i < number_of_output_channels; ++i)
+  for (unsigned i = 0; i < number_of_output_channels; ++i) {
     memcpy(output_bus->Channel(i)->MutableData(),
-           output_buffer->getChannelData(i)->Data() + buffer_read_write_index_,
+           output_buffer->getChannelData(i).View()->Data() +
+               buffer_read_write_index_,
            sizeof(float) * frames_to_process);
+  }
 
   // Update the buffering index.
   buffer_read_write_index_ =
diff --git a/third_party/WebKit/Source/modules/webaudio/WaveShaperNode.cpp b/third_party/WebKit/Source/modules/webaudio/WaveShaperNode.cpp
index 8712b4b..994ef204 100644
--- a/third_party/WebKit/Source/modules/webaudio/WaveShaperNode.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/WaveShaperNode.cpp
@@ -92,12 +92,12 @@
   GetWaveShaperProcessor()->SetCurve(curve_data, curve_length);
 }
 
-void WaveShaperNode::setCurve(DOMFloat32Array* curve,
+void WaveShaperNode::setCurve(NotShared<DOMFloat32Array> curve,
                               ExceptionState& exception_state) {
   DCHECK(IsMainThread());
 
   if (curve)
-    SetCurveImpl(curve->Data(), curve->length(), exception_state);
+    SetCurveImpl(curve.View()->Data(), curve.View()->length(), exception_state);
   else
     SetCurveImpl(nullptr, 0, exception_state);
 }
@@ -109,17 +109,18 @@
   SetCurveImpl(curve.Data(), curve.size(), exception_state);
 }
 
-DOMFloat32Array* WaveShaperNode::curve() {
+NotShared<DOMFloat32Array> WaveShaperNode::curve() {
   Vector<float>* curve = GetWaveShaperProcessor()->Curve();
   if (!curve)
-    return nullptr;
+    return NotShared<DOMFloat32Array>(nullptr);
 
   unsigned size = curve->size();
   RefPtr<WTF::Float32Array> new_curve = WTF::Float32Array::Create(size);
 
   memcpy(new_curve->Data(), curve->Data(), sizeof(float) * size);
 
-  return DOMFloat32Array::Create(new_curve.Release());
+  return NotShared<DOMFloat32Array>(
+      DOMFloat32Array::Create(new_curve.Release()));
 }
 
 void WaveShaperNode::setOversample(const String& type) {
diff --git a/third_party/WebKit/Source/modules/webaudio/WaveShaperNode.h b/third_party/WebKit/Source/modules/webaudio/WaveShaperNode.h
index 6fd05b2..3773781 100644
--- a/third_party/WebKit/Source/modules/webaudio/WaveShaperNode.h
+++ b/third_party/WebKit/Source/modules/webaudio/WaveShaperNode.h
@@ -27,6 +27,7 @@
 #define WaveShaperNode_h
 
 #include "core/dom/DOMTypedArray.h"
+#include "core/dom/NotShared.h"
 #include "modules/webaudio/AudioNode.h"
 #include "modules/webaudio/WaveShaperProcessor.h"
 
@@ -46,9 +47,9 @@
                                 ExceptionState&);
 
   // setCurve() is called on the main thread.
-  void setCurve(DOMFloat32Array*, ExceptionState&);
+  void setCurve(NotShared<DOMFloat32Array>, ExceptionState&);
   void setCurve(const Vector<float>&, ExceptionState&);
-  DOMFloat32Array* curve();
+  NotShared<DOMFloat32Array> curve();
 
   void setOversample(const String&);
   String oversample() const;
diff --git a/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp b/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp
index 21ee642..9a90200 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp
@@ -248,16 +248,17 @@
   WebGLRenderingContextBase::InitializeNewContext();
 }
 
-void WebGL2RenderingContextBase::bufferData(GLenum target,
-                                            DOMArrayBufferView* src_data,
-                                            GLenum usage,
-                                            GLuint src_offset,
-                                            GLuint length) {
+void WebGL2RenderingContextBase::bufferData(
+    GLenum target,
+    NotShared<DOMArrayBufferView> src_data,
+    GLenum usage,
+    GLuint src_offset,
+    GLuint length) {
   if (isContextLost())
     return;
   void* sub_base_address = nullptr;
   long long sub_byte_length = 0;
-  if (!ValidateSubSourceAndGetData(src_data, src_offset, length,
+  if (!ValidateSubSourceAndGetData(src_data.View(), src_offset, length,
                                    &sub_base_address, &sub_byte_length)) {
     SynthesizeGLError(GL_INVALID_VALUE, "bufferData",
                       "srcOffset + length too large");
@@ -279,21 +280,22 @@
 }
 
 void WebGL2RenderingContextBase::bufferData(GLenum target,
-                                            DOMArrayBufferView* data,
+                                            NotShared<DOMArrayBufferView> data,
                                             GLenum usage) {
   WebGLRenderingContextBase::bufferData(target, data, usage);
 }
 
-void WebGL2RenderingContextBase::bufferSubData(GLenum target,
-                                               GLintptr dst_byte_offset,
-                                               DOMArrayBufferView* src_data,
-                                               GLuint src_offset,
-                                               GLuint length) {
+void WebGL2RenderingContextBase::bufferSubData(
+    GLenum target,
+    GLintptr dst_byte_offset,
+    NotShared<DOMArrayBufferView> src_data,
+    GLuint src_offset,
+    GLuint length) {
   if (isContextLost())
     return;
   void* sub_base_address = nullptr;
   long long sub_byte_length = 0;
-  if (!ValidateSubSourceAndGetData(src_data, src_offset, length,
+  if (!ValidateSubSourceAndGetData(src_data.View(), src_offset, length,
                                    &sub_base_address, &sub_byte_length)) {
     SynthesizeGLError(GL_INVALID_VALUE, "bufferSubData",
                       "srcOffset + length too large");
@@ -365,17 +367,18 @@
       static_cast<GLintptr>(write_offset), static_cast<GLsizeiptr>(size));
 }
 
-void WebGL2RenderingContextBase::getBufferSubData(GLenum target,
-                                                  long long src_byte_offset,
-                                                  DOMArrayBufferView* dst_data,
-                                                  GLuint dst_offset,
-                                                  GLuint length) {
+void WebGL2RenderingContextBase::getBufferSubData(
+    GLenum target,
+    long long src_byte_offset,
+    NotShared<DOMArrayBufferView> dst_data,
+    GLuint dst_offset,
+    GLuint length) {
   WebGLBuffer* source_buffer = nullptr;
   void* destination_data_ptr = nullptr;
   long long destination_byte_length = 0;
   const char* message = ValidateGetBufferSubData(
-      __FUNCTION__, target, src_byte_offset, dst_data, dst_offset, length,
-      &source_buffer, &destination_data_ptr, &destination_byte_length);
+      __FUNCTION__, target, src_byte_offset, dst_data.View(), dst_offset,
+      length, &source_buffer, &destination_data_ptr, &destination_byte_length);
   if (message) {
     // If there was a GL error, it was already synthesized in
     // validateGetBufferSubData, so it's not done here.
@@ -749,13 +752,14 @@
   ContextGL()->PixelStorei(pname, param);
 }
 
-void WebGL2RenderingContextBase::readPixels(GLint x,
-                                            GLint y,
-                                            GLsizei width,
-                                            GLsizei height,
-                                            GLenum format,
-                                            GLenum type,
-                                            DOMArrayBufferView* pixels) {
+void WebGL2RenderingContextBase::readPixels(
+    GLint x,
+    GLint y,
+    GLsizei width,
+    GLsizei height,
+    GLenum format,
+    GLenum type,
+    NotShared<DOMArrayBufferView> pixels) {
   if (isContextLost())
     return;
   if (bound_pixel_pack_buffer_.Get()) {
@@ -764,17 +768,18 @@
     return;
   }
 
-  ReadPixelsHelper(x, y, width, height, format, type, pixels, 0);
+  ReadPixelsHelper(x, y, width, height, format, type, pixels.View(), 0);
 }
 
-void WebGL2RenderingContextBase::readPixels(GLint x,
-                                            GLint y,
-                                            GLsizei width,
-                                            GLsizei height,
-                                            GLenum format,
-                                            GLenum type,
-                                            DOMArrayBufferView* pixels,
-                                            GLuint offset) {
+void WebGL2RenderingContextBase::readPixels(
+    GLint x,
+    GLint y,
+    GLsizei width,
+    GLsizei height,
+    GLenum format,
+    GLenum type,
+    NotShared<DOMArrayBufferView> pixels,
+    GLuint offset) {
   if (isContextLost())
     return;
   if (bound_pixel_pack_buffer_.Get()) {
@@ -783,7 +788,7 @@
     return;
   }
 
-  ReadPixelsHelper(x, y, width, height, format, type, pixels, offset);
+  ReadPixelsHelper(x, y, width, height, format, type, pixels.View(), offset);
 }
 
 void WebGL2RenderingContextBase::readPixels(GLint x,
@@ -1125,15 +1130,16 @@
                              reinterpret_cast<const void*>(offset));
 }
 
-void WebGL2RenderingContextBase::texImage2D(GLenum target,
-                                            GLint level,
-                                            GLint internalformat,
-                                            GLsizei width,
-                                            GLsizei height,
-                                            GLint border,
-                                            GLenum format,
-                                            GLenum type,
-                                            DOMArrayBufferView* data) {
+void WebGL2RenderingContextBase::texImage2D(
+    GLenum target,
+    GLint level,
+    GLint internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLint border,
+    GLenum format,
+    GLenum type,
+    NotShared<DOMArrayBufferView> data) {
   if (isContextLost())
     return;
   if (bound_pixel_unpack_buffer_) {
@@ -1153,7 +1159,7 @@
                                             GLint border,
                                             GLenum format,
                                             GLenum type,
-                                            DOMArrayBufferView* data,
+                                            NotShared<DOMArrayBufferView> data,
                                             GLuint src_offset) {
   if (isContextLost())
     return;
@@ -1162,9 +1168,9 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-  TexImageHelperDOMArrayBufferView(kTexImage2D, target, level, internalformat,
-                                   width, height, 1, border, format, type, 0, 0,
-                                   0, data, kNullNotReachable, src_offset);
+  TexImageHelperDOMArrayBufferView(
+      kTexImage2D, target, level, internalformat, width, height, 1, border,
+      format, type, 0, 0, 0, data.View(), kNullNotReachable, src_offset);
 }
 
 void WebGL2RenderingContextBase::texImage2D(GLenum target,
@@ -1368,15 +1374,16 @@
                                         type, image_bit_map, exception_state);
 }
 
-void WebGL2RenderingContextBase::texSubImage2D(GLenum target,
-                                               GLint level,
-                                               GLint xoffset,
-                                               GLint yoffset,
-                                               GLsizei width,
-                                               GLsizei height,
-                                               GLenum format,
-                                               GLenum type,
-                                               DOMArrayBufferView* pixels) {
+void WebGL2RenderingContextBase::texSubImage2D(
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLsizei width,
+    GLsizei height,
+    GLenum format,
+    GLenum type,
+    NotShared<DOMArrayBufferView> pixels) {
   if (isContextLost())
     return;
   if (bound_pixel_unpack_buffer_) {
@@ -1388,16 +1395,17 @@
                                            width, height, format, type, pixels);
 }
 
-void WebGL2RenderingContextBase::texSubImage2D(GLenum target,
-                                               GLint level,
-                                               GLint xoffset,
-                                               GLint yoffset,
-                                               GLsizei width,
-                                               GLsizei height,
-                                               GLenum format,
-                                               GLenum type,
-                                               DOMArrayBufferView* pixels,
-                                               GLuint src_offset) {
+void WebGL2RenderingContextBase::texSubImage2D(
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLsizei width,
+    GLsizei height,
+    GLenum format,
+    GLenum type,
+    NotShared<DOMArrayBufferView> pixels,
+    GLuint src_offset) {
   if (isContextLost())
     return;
   if (bound_pixel_unpack_buffer_) {
@@ -1405,9 +1413,9 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-  TexImageHelperDOMArrayBufferView(kTexSubImage2D, target, level, 0, width,
-                                   height, 1, 0, format, type, xoffset, yoffset,
-                                   0, pixels, kNullNotReachable, src_offset);
+  TexImageHelperDOMArrayBufferView(
+      kTexSubImage2D, target, level, 0, width, height, 1, 0, format, type,
+      xoffset, yoffset, 0, pixels.View(), kNullNotReachable, src_offset);
 }
 
 void WebGL2RenderingContextBase::texSubImage2D(GLenum target,
@@ -1648,32 +1656,34 @@
                             depth);
 }
 
-void WebGL2RenderingContextBase::texImage3D(GLenum target,
-                                            GLint level,
-                                            GLint internalformat,
-                                            GLsizei width,
-                                            GLsizei height,
-                                            GLsizei depth,
-                                            GLint border,
-                                            GLenum format,
-                                            GLenum type,
-                                            DOMArrayBufferView* pixels) {
+void WebGL2RenderingContextBase::texImage3D(
+    GLenum target,
+    GLint level,
+    GLint internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLint border,
+    GLenum format,
+    GLenum type,
+    NotShared<DOMArrayBufferView> pixels) {
   TexImageHelperDOMArrayBufferView(kTexImage3D, target, level, internalformat,
                                    width, height, depth, border, format, type,
-                                   0, 0, 0, pixels, kNullAllowed, 0);
+                                   0, 0, 0, pixels.View(), kNullAllowed, 0);
 }
 
-void WebGL2RenderingContextBase::texImage3D(GLenum target,
-                                            GLint level,
-                                            GLint internalformat,
-                                            GLsizei width,
-                                            GLsizei height,
-                                            GLsizei depth,
-                                            GLint border,
-                                            GLenum format,
-                                            GLenum type,
-                                            DOMArrayBufferView* pixels,
-                                            GLuint src_offset) {
+void WebGL2RenderingContextBase::texImage3D(
+    GLenum target,
+    GLint level,
+    GLint internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLint border,
+    GLenum format,
+    GLenum type,
+    NotShared<DOMArrayBufferView> pixels,
+    GLuint src_offset) {
   if (isContextLost())
     return;
   if (bound_pixel_unpack_buffer_) {
@@ -1683,7 +1693,7 @@
   }
   TexImageHelperDOMArrayBufferView(
       kTexImage3D, target, level, internalformat, width, height, depth, border,
-      format, type, 0, 0, 0, pixels, kNullNotReachable, src_offset);
+      format, type, 0, 0, 0, pixels.View(), kNullNotReachable, src_offset);
 }
 
 void WebGL2RenderingContextBase::texImage3D(GLenum target,
@@ -1834,18 +1844,19 @@
                             unpack_image_height_, exception_state);
 }
 
-void WebGL2RenderingContextBase::texSubImage3D(GLenum target,
-                                               GLint level,
-                                               GLint xoffset,
-                                               GLint yoffset,
-                                               GLint zoffset,
-                                               GLsizei width,
-                                               GLsizei height,
-                                               GLsizei depth,
-                                               GLenum format,
-                                               GLenum type,
-                                               DOMArrayBufferView* pixels,
-                                               GLuint src_offset) {
+void WebGL2RenderingContextBase::texSubImage3D(
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLint zoffset,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLenum format,
+    GLenum type,
+    NotShared<DOMArrayBufferView> pixels,
+    GLuint src_offset) {
   if (isContextLost())
     return;
   if (bound_pixel_unpack_buffer_) {
@@ -1855,7 +1866,7 @@
   }
   TexImageHelperDOMArrayBufferView(
       kTexSubImage3D, target, level, 0, width, height, depth, 0, format, type,
-      xoffset, yoffset, zoffset, pixels, kNullNotReachable, src_offset);
+      xoffset, yoffset, zoffset, pixels.View(), kNullNotReachable, src_offset);
 }
 
 void WebGL2RenderingContextBase::texSubImage3D(GLenum target,
@@ -2050,7 +2061,7 @@
     GLsizei width,
     GLsizei height,
     GLint border,
-    DOMArrayBufferView* data) {
+    NotShared<DOMArrayBufferView> data) {
   if (isContextLost())
     return;
   if (bound_pixel_unpack_buffer_) {
@@ -2069,7 +2080,7 @@
     GLsizei width,
     GLsizei height,
     GLint border,
-    DOMArrayBufferView* data,
+    NotShared<DOMArrayBufferView> data,
     GLuint src_offset,
     GLuint src_length_override) {
   if (isContextLost())
@@ -2083,21 +2094,21 @@
     return;
   if (!ValidateCompressedTexFormat("compressedTexImage2D", internalformat))
     return;
-  if (src_offset > data->byteLength()) {
+  if (src_offset > data.View()->byteLength()) {
     SynthesizeGLError(GL_INVALID_VALUE, "compressedTexImage2D",
                       "srcOffset is out of range");
     return;
   }
   if (src_length_override == 0) {
-    src_length_override = data->byteLength() - src_offset;
-  } else if (src_length_override > data->byteLength() - src_offset) {
+    src_length_override = data.View()->byteLength() - src_offset;
+  } else if (src_length_override > data.View()->byteLength() - src_offset) {
     SynthesizeGLError(GL_INVALID_VALUE, "compressedTexImage2D",
                       "srcLengthOverride is out of range");
     return;
   }
   ContextGL()->CompressedTexImage2D(
       target, level, internalformat, width, height, border, src_length_override,
-      static_cast<uint8_t*>(data->BaseAddress()) + src_offset);
+      static_cast<uint8_t*>(data.View()->BaseAddress()) + src_offset);
 }
 
 void WebGL2RenderingContextBase::compressedTexImage2D(GLenum target,
@@ -2128,7 +2139,7 @@
     GLsizei width,
     GLsizei height,
     GLenum format,
-    DOMArrayBufferView* data) {
+    NotShared<DOMArrayBufferView> data) {
   if (isContextLost())
     return;
   if (bound_pixel_unpack_buffer_) {
@@ -2148,7 +2159,7 @@
     GLsizei width,
     GLsizei height,
     GLenum format,
-    DOMArrayBufferView* data,
+    NotShared<DOMArrayBufferView> data,
     GLuint src_offset,
     GLuint src_length_override) {
   if (isContextLost())
@@ -2162,14 +2173,14 @@
     return;
   if (!ValidateCompressedTexFormat("compressedTexSubImage2D", format))
     return;
-  if (src_offset > data->byteLength()) {
+  if (src_offset > data.View()->byteLength()) {
     SynthesizeGLError(GL_INVALID_VALUE, "compressedTexSubImage2D",
                       "srcOffset is out of range");
     return;
   }
   if (src_length_override == 0) {
-    src_length_override = data->byteLength() - src_offset;
-  } else if (src_length_override > data->byteLength() - src_offset) {
+    src_length_override = data.View()->byteLength() - src_offset;
+  } else if (src_length_override > data.View()->byteLength() - src_offset) {
     SynthesizeGLError(GL_INVALID_VALUE, "compressedTexImage2D",
                       "srcLengthOverride is out of range");
     return;
@@ -2177,7 +2188,7 @@
   ContextGL()->CompressedTexSubImage2D(
       target, level, xoffset, yoffset, width, height, format,
       src_length_override,
-      static_cast<uint8_t*>(data->BaseAddress()) + src_offset);
+      static_cast<uint8_t*>(data.View()->BaseAddress()) + src_offset);
 }
 
 void WebGL2RenderingContextBase::compressedTexSubImage2D(GLenum target,
@@ -2209,7 +2220,7 @@
     GLsizei height,
     GLsizei depth,
     GLint border,
-    DOMArrayBufferView* data,
+    NotShared<DOMArrayBufferView> data,
     GLuint src_offset,
     GLuint src_length_override) {
   if (isContextLost())
@@ -2223,14 +2234,14 @@
     return;
   if (!ValidateCompressedTexFormat("compressedTexImage3D", internalformat))
     return;
-  if (src_offset > data->byteLength()) {
+  if (src_offset > data.View()->byteLength()) {
     SynthesizeGLError(GL_INVALID_VALUE, "compressedTexImage3D",
                       "srcOffset is out of range");
     return;
   }
   if (src_length_override == 0) {
-    src_length_override = data->byteLength() - src_offset;
-  } else if (src_length_override > data->byteLength() - src_offset) {
+    src_length_override = data.View()->byteLength() - src_offset;
+  } else if (src_length_override > data.View()->byteLength() - src_offset) {
     SynthesizeGLError(GL_INVALID_VALUE, "compressedTexImage3D",
                       "srcLengthOverride is out of range");
     return;
@@ -2238,7 +2249,7 @@
   ContextGL()->CompressedTexImage3D(
       target, level, internalformat, width, height, depth, border,
       src_length_override,
-      static_cast<uint8_t*>(data->BaseAddress()) + src_offset);
+      static_cast<uint8_t*>(data.View()->BaseAddress()) + src_offset);
 }
 
 void WebGL2RenderingContextBase::compressedTexImage3D(GLenum target,
@@ -2272,7 +2283,7 @@
     GLsizei height,
     GLsizei depth,
     GLenum format,
-    DOMArrayBufferView* data,
+    NotShared<DOMArrayBufferView> data,
     GLuint src_offset,
     GLuint src_length_override) {
   if (isContextLost())
@@ -2286,14 +2297,14 @@
     return;
   if (!ValidateCompressedTexFormat("compressedTexSubImage3D", format))
     return;
-  if (src_offset > data->byteLength()) {
+  if (src_offset > data.View()->byteLength()) {
     SynthesizeGLError(GL_INVALID_VALUE, "compressedTexSubImage3D",
                       "srcOffset is out of range");
     return;
   }
   if (src_length_override == 0) {
-    src_length_override = data->byteLength() - src_offset;
-  } else if (src_length_override > data->byteLength() - src_offset) {
+    src_length_override = data.View()->byteLength() - src_offset;
+  } else if (src_length_override > data.View()->byteLength() - src_offset) {
     SynthesizeGLError(GL_INVALID_VALUE, "compressedTexSubImage3D",
                       "srcLengthOverride is out of range");
     return;
@@ -2301,7 +2312,7 @@
   ContextGL()->CompressedTexSubImage3D(
       target, level, xoffset, yoffset, zoffset, width, height, depth, format,
       src_length_override,
-      static_cast<uint8_t*>(data->BaseAddress()) + src_offset);
+      static_cast<uint8_t*>(data.View()->BaseAddress()) + src_offset);
 }
 
 void WebGL2RenderingContextBase::compressedTexSubImage3D(GLenum target,
@@ -2784,17 +2795,17 @@
 void WebGL2RenderingContextBase::uniformMatrix2fv(
     const WebGLUniformLocation* location,
     GLboolean transpose,
-    DOMFloat32Array* v,
+    NotShared<DOMFloat32Array> v,
     GLuint src_offset,
     GLuint src_length) {
   if (isContextLost() ||
       !ValidateUniformMatrixParameters("uniformMatrix2fv", location, transpose,
-                                       v, 4, src_offset, src_length))
+                                       v.View(), 4, src_offset, src_length))
     return;
   ContextGL()->UniformMatrix2fv(
       location->Location(),
-      (src_length ? src_length : (v->length() - src_offset)) >> 2, transpose,
-      v->Data() + src_offset);
+      (src_length ? src_length : (v.View()->length() - src_offset)) >> 2,
+      transpose, v.View()->Data() + src_offset);
 }
 
 void WebGL2RenderingContextBase::uniformMatrix2fv(
@@ -2816,17 +2827,17 @@
 void WebGL2RenderingContextBase::uniformMatrix3fv(
     const WebGLUniformLocation* location,
     GLboolean transpose,
-    DOMFloat32Array* v,
+    NotShared<DOMFloat32Array> v,
     GLuint src_offset,
     GLuint src_length) {
   if (isContextLost() ||
       !ValidateUniformMatrixParameters("uniformMatrix3fv", location, transpose,
-                                       v, 9, src_offset, src_length))
+                                       v.View(), 9, src_offset, src_length))
     return;
   ContextGL()->UniformMatrix3fv(
       location->Location(),
-      (src_length ? src_length : (v->length() - src_offset)) / 9, transpose,
-      v->Data() + src_offset);
+      (src_length ? src_length : (v.View()->length() - src_offset)) / 9,
+      transpose, v.View()->Data() + src_offset);
 }
 
 void WebGL2RenderingContextBase::uniformMatrix3fv(
@@ -2848,17 +2859,17 @@
 void WebGL2RenderingContextBase::uniformMatrix4fv(
     const WebGLUniformLocation* location,
     GLboolean transpose,
-    DOMFloat32Array* v,
+    NotShared<DOMFloat32Array> v,
     GLuint src_offset,
     GLuint src_length) {
   if (isContextLost() ||
       !ValidateUniformMatrixParameters("uniformMatrix4fv", location, transpose,
-                                       v, 16, src_offset, src_length))
+                                       v.View(), 16, src_offset, src_length))
     return;
   ContextGL()->UniformMatrix4fv(
       location->Location(),
-      (src_length ? src_length : (v->length() - src_offset)) >> 4, transpose,
-      v->Data() + src_offset);
+      (src_length ? src_length : (v.View()->length() - src_offset)) >> 4,
+      transpose, v.View()->Data() + src_offset);
 }
 
 void WebGL2RenderingContextBase::uniformMatrix4fv(
@@ -2880,17 +2891,17 @@
 void WebGL2RenderingContextBase::uniformMatrix2x3fv(
     const WebGLUniformLocation* location,
     GLboolean transpose,
-    DOMFloat32Array* value,
+    NotShared<DOMFloat32Array> value,
     GLuint src_offset,
     GLuint src_length) {
   if (isContextLost() || !ValidateUniformMatrixParameters(
-                             "uniformMatrix2x3fv", location, transpose, value,
-                             6, src_offset, src_length))
+                             "uniformMatrix2x3fv", location, transpose,
+                             value.View(), 6, src_offset, src_length))
     return;
   ContextGL()->UniformMatrix2x3fv(
       location->Location(),
-      (src_length ? src_length : (value->length() - src_offset)) / 6, transpose,
-      value->Data() + src_offset);
+      (src_length ? src_length : (value.View()->length() - src_offset)) / 6,
+      transpose, value.View()->Data() + src_offset);
 }
 
 void WebGL2RenderingContextBase::uniformMatrix2x3fv(
@@ -2913,17 +2924,17 @@
 void WebGL2RenderingContextBase::uniformMatrix3x2fv(
     const WebGLUniformLocation* location,
     GLboolean transpose,
-    DOMFloat32Array* value,
+    NotShared<DOMFloat32Array> value,
     GLuint src_offset,
     GLuint src_length) {
   if (isContextLost() || !ValidateUniformMatrixParameters(
-                             "uniformMatrix3x2fv", location, transpose, value,
-                             6, src_offset, src_length))
+                             "uniformMatrix3x2fv", location, transpose,
+                             value.View(), 6, src_offset, src_length))
     return;
   ContextGL()->UniformMatrix3x2fv(
       location->Location(),
-      (src_length ? src_length : (value->length() - src_offset)) / 6, transpose,
-      value->Data() + src_offset);
+      (src_length ? src_length : (value.View()->length() - src_offset)) / 6,
+      transpose, value.View()->Data() + src_offset);
 }
 
 void WebGL2RenderingContextBase::uniformMatrix3x2fv(
@@ -2946,17 +2957,17 @@
 void WebGL2RenderingContextBase::uniformMatrix2x4fv(
     const WebGLUniformLocation* location,
     GLboolean transpose,
-    DOMFloat32Array* value,
+    NotShared<DOMFloat32Array> value,
     GLuint src_offset,
     GLuint src_length) {
   if (isContextLost() || !ValidateUniformMatrixParameters(
-                             "uniformMatrix2x4fv", location, transpose, value,
-                             8, src_offset, src_length))
+                             "uniformMatrix2x4fv", location, transpose,
+                             value.View(), 8, src_offset, src_length))
     return;
   ContextGL()->UniformMatrix2x4fv(
       location->Location(),
-      (src_length ? src_length : (value->length() - src_offset)) >> 3,
-      transpose, value->Data() + src_offset);
+      (src_length ? src_length : (value.View()->length() - src_offset)) >> 3,
+      transpose, value.View()->Data() + src_offset);
 }
 
 void WebGL2RenderingContextBase::uniformMatrix2x4fv(
@@ -2979,17 +2990,17 @@
 void WebGL2RenderingContextBase::uniformMatrix4x2fv(
     const WebGLUniformLocation* location,
     GLboolean transpose,
-    DOMFloat32Array* value,
+    NotShared<DOMFloat32Array> value,
     GLuint src_offset,
     GLuint src_length) {
   if (isContextLost() || !ValidateUniformMatrixParameters(
-                             "uniformMatrix4x2fv", location, transpose, value,
-                             8, src_offset, src_length))
+                             "uniformMatrix4x2fv", location, transpose,
+                             value.View(), 8, src_offset, src_length))
     return;
   ContextGL()->UniformMatrix4x2fv(
       location->Location(),
-      (src_length ? src_length : (value->length() - src_offset)) >> 3,
-      transpose, value->Data() + src_offset);
+      (src_length ? src_length : (value.View()->length() - src_offset)) >> 3,
+      transpose, value.View()->Data() + src_offset);
 }
 
 void WebGL2RenderingContextBase::uniformMatrix4x2fv(
@@ -3012,17 +3023,17 @@
 void WebGL2RenderingContextBase::uniformMatrix3x4fv(
     const WebGLUniformLocation* location,
     GLboolean transpose,
-    DOMFloat32Array* value,
+    NotShared<DOMFloat32Array> value,
     GLuint src_offset,
     GLuint src_length) {
   if (isContextLost() || !ValidateUniformMatrixParameters(
-                             "uniformMatrix3x4fv", location, transpose, value,
-                             12, src_offset, src_length))
+                             "uniformMatrix3x4fv", location, transpose,
+                             value.View(), 12, src_offset, src_length))
     return;
   ContextGL()->UniformMatrix3x4fv(
       location->Location(),
-      (src_length ? src_length : (value->length() - src_offset)) / 12,
-      transpose, value->Data() + src_offset);
+      (src_length ? src_length : (value.View()->length() - src_offset)) / 12,
+      transpose, value.View()->Data() + src_offset);
 }
 
 void WebGL2RenderingContextBase::uniformMatrix3x4fv(
@@ -3045,17 +3056,17 @@
 void WebGL2RenderingContextBase::uniformMatrix4x3fv(
     const WebGLUniformLocation* location,
     GLboolean transpose,
-    DOMFloat32Array* value,
+    NotShared<DOMFloat32Array> value,
     GLuint src_offset,
     GLuint src_length) {
   if (isContextLost() || !ValidateUniformMatrixParameters(
-                             "uniformMatrix4x3fv", location, transpose, value,
-                             12, src_offset, src_length))
+                             "uniformMatrix4x3fv", location, transpose,
+                             value.View(), 12, src_offset, src_length))
     return;
   ContextGL()->UniformMatrix4x3fv(
       location->Location(),
-      (src_length ? src_length : (value->length() - src_offset)) / 12,
-      transpose, value->Data() + src_offset);
+      (src_length ? src_length : (value.View()->length() - src_offset)) / 12,
+      transpose, value.View()->Data() + src_offset);
 }
 
 void WebGL2RenderingContextBase::uniformMatrix4x3fv(
@@ -3174,7 +3185,7 @@
 void WebGL2RenderingContextBase::uniformMatrix2fv(
     const WebGLUniformLocation* location,
     GLboolean transpose,
-    DOMFloat32Array* v) {
+    NotShared<DOMFloat32Array> v) {
   WebGLRenderingContextBase::uniformMatrix2fv(location, transpose, v);
 }
 
@@ -3188,7 +3199,7 @@
 void WebGL2RenderingContextBase::uniformMatrix3fv(
     const WebGLUniformLocation* location,
     GLboolean transpose,
-    DOMFloat32Array* v) {
+    NotShared<DOMFloat32Array> v) {
   WebGLRenderingContextBase::uniformMatrix3fv(location, transpose, v);
 }
 
@@ -3202,7 +3213,7 @@
 void WebGL2RenderingContextBase::uniformMatrix4fv(
     const WebGLUniformLocation* location,
     GLboolean transpose,
-    DOMFloat32Array* v) {
+    NotShared<DOMFloat32Array> v) {
   WebGLRenderingContextBase::uniformMatrix4fv(location, transpose, v);
 }
 
@@ -3224,15 +3235,16 @@
   SetVertexAttribType(index, kInt32ArrayType);
 }
 
-void WebGL2RenderingContextBase::vertexAttribI4iv(GLuint index,
-                                                  const DOMInt32Array* v) {
+void WebGL2RenderingContextBase::vertexAttribI4iv(
+    GLuint index,
+    NotShared<const DOMInt32Array> v) {
   if (isContextLost())
     return;
-  if (!v || v->length() < 4) {
+  if (!v.View() || v.View()->length() < 4) {
     SynthesizeGLError(GL_INVALID_VALUE, "vertexAttribI4iv", "invalid array");
     return;
   }
-  ContextGL()->VertexAttribI4iv(index, v->Data());
+  ContextGL()->VertexAttribI4iv(index, v.View()->Data());
   SetVertexAttribType(index, kInt32ArrayType);
 }
 
@@ -3259,15 +3271,16 @@
   SetVertexAttribType(index, kUint32ArrayType);
 }
 
-void WebGL2RenderingContextBase::vertexAttribI4uiv(GLuint index,
-                                                   const DOMUint32Array* v) {
+void WebGL2RenderingContextBase::vertexAttribI4uiv(
+    GLuint index,
+    NotShared<const DOMUint32Array> v) {
   if (isContextLost())
     return;
-  if (!v || v->length() < 4) {
+  if (!v.View() || v.View()->length() < 4) {
     SynthesizeGLError(GL_INVALID_VALUE, "vertexAttribI4uiv", "invalid array");
     return;
   }
-  ContextGL()->VertexAttribI4uiv(index, v->Data());
+  ContextGL()->VertexAttribI4uiv(index, v.View()->Data());
   SetVertexAttribType(index, kUint32ArrayType);
 }
 
@@ -3485,12 +3498,12 @@
 
 void WebGL2RenderingContextBase::clearBufferiv(GLenum buffer,
                                                GLint drawbuffer,
-                                               DOMInt32Array* value) {
+                                               NotShared<DOMInt32Array> value) {
   if (isContextLost() ||
-      !ValidateClearBuffer("clearBufferiv", buffer, value->length()))
+      !ValidateClearBuffer("clearBufferiv", buffer, value.View()->length()))
     return;
 
-  ContextGL()->ClearBufferiv(buffer, drawbuffer, value->Data());
+  ContextGL()->ClearBufferiv(buffer, drawbuffer, value.View()->Data());
 }
 
 void WebGL2RenderingContextBase::clearBufferiv(GLenum buffer,
@@ -3503,14 +3516,15 @@
   ContextGL()->ClearBufferiv(buffer, drawbuffer, value.Data());
 }
 
-void WebGL2RenderingContextBase::clearBufferuiv(GLenum buffer,
-                                                GLint drawbuffer,
-                                                DOMUint32Array* value) {
+void WebGL2RenderingContextBase::clearBufferuiv(
+    GLenum buffer,
+    GLint drawbuffer,
+    NotShared<DOMUint32Array> value) {
   if (isContextLost() ||
-      !ValidateClearBuffer("clearBufferuiv", buffer, value->length()))
+      !ValidateClearBuffer("clearBufferuiv", buffer, value.View()->length()))
     return;
 
-  ContextGL()->ClearBufferuiv(buffer, drawbuffer, value->Data());
+  ContextGL()->ClearBufferuiv(buffer, drawbuffer, value.View()->Data());
 }
 
 void WebGL2RenderingContextBase::clearBufferuiv(GLenum buffer,
@@ -3523,14 +3537,15 @@
   ContextGL()->ClearBufferuiv(buffer, drawbuffer, value.Data());
 }
 
-void WebGL2RenderingContextBase::clearBufferfv(GLenum buffer,
-                                               GLint drawbuffer,
-                                               DOMFloat32Array* value) {
+void WebGL2RenderingContextBase::clearBufferfv(
+    GLenum buffer,
+    GLint drawbuffer,
+    NotShared<DOMFloat32Array> value) {
   if (isContextLost() ||
-      !ValidateClearBuffer("clearBufferfv", buffer, value->length()))
+      !ValidateClearBuffer("clearBufferfv", buffer, value.View()->length()))
     return;
 
-  ContextGL()->ClearBufferfv(buffer, drawbuffer, value->Data());
+  ContextGL()->ClearBufferfv(buffer, drawbuffer, value.View()->Data());
 }
 
 void WebGL2RenderingContextBase::clearBufferfv(GLenum buffer,
diff --git a/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.h b/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.h
index 086ede6a..52b1d744 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.h
+++ b/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.h
@@ -31,21 +31,35 @@
   void DestroyContext() override;
 
   /* Buffer objects */
-  void bufferData(GLenum, DOMArrayBufferView*, GLenum, GLuint, GLuint);
-  void bufferSubData(GLenum, GLintptr, DOMArrayBufferView*, GLuint, GLuint);
+  void bufferData(GLenum,
+                  NotShared<DOMArrayBufferView>,
+                  GLenum,
+                  GLuint,
+                  GLuint);
+  void bufferSubData(GLenum,
+                     GLintptr,
+                     NotShared<DOMArrayBufferView>,
+                     GLuint,
+                     GLuint);
   // Have to re-declare/re-define the following buffer{Sub}Data functions from
   // base class.  This is because the above buffer{Sub}Data() hides the name
   // from base class.
   void bufferData(GLenum target, long long size, GLenum usage);
   void bufferData(GLenum target, DOMArrayBuffer* data, GLenum usage);
-  void bufferData(GLenum target, DOMArrayBufferView* data, GLenum usage);
+  void bufferData(GLenum target,
+                  NotShared<DOMArrayBufferView> data,
+                  GLenum usage);
   void bufferSubData(GLenum target, long long offset, DOMArrayBuffer* data);
   void bufferSubData(GLenum target,
                      long long offset,
                      const FlexibleArrayBufferView& data);
 
   void copyBufferSubData(GLenum, GLenum, long long, long long, long long);
-  void getBufferSubData(GLenum, long long, DOMArrayBufferView*, GLuint, GLuint);
+  void getBufferSubData(GLenum,
+                        long long,
+                        NotShared<DOMArrayBufferView>,
+                        GLuint,
+                        GLuint);
 
   void RegisterGetBufferSubDataAsyncCallback(
       WebGLGetBufferSubDataAsyncCallback*);
@@ -149,7 +163,7 @@
                   GLint,
                   GLenum,
                   GLenum,
-                  DOMArrayBufferView*,
+                  NotShared<DOMArrayBufferView>,
                   GLuint);
 
   void texSubImage2D(GLenum,
@@ -218,7 +232,7 @@
                      GLsizei,
                      GLenum,
                      GLenum,
-                     DOMArrayBufferView*,
+                     NotShared<DOMArrayBufferView>,
                      GLuint);
 
   // Have to re-declare/re-define the following tex{Sub}Image2D functions from
@@ -298,7 +312,7 @@
                   GLint,
                   GLenum,
                   GLenum,
-                  DOMArrayBufferView*);
+                  NotShared<DOMArrayBufferView>);
   void texImage3D(GLenum,
                   GLint,
                   GLint,
@@ -308,7 +322,7 @@
                   GLint,
                   GLenum,
                   GLenum,
-                  DOMArrayBufferView*,
+                  NotShared<DOMArrayBufferView>,
                   GLuint);
   void texImage3D(GLenum,
                   GLint,
@@ -384,7 +398,7 @@
                      GLsizei,
                      GLenum,
                      GLenum,
-                     DOMArrayBufferView*,
+                     NotShared<DOMArrayBufferView>,
                      GLuint);
   void texSubImage3D(GLenum,
                      GLint,
@@ -465,7 +479,7 @@
                   GLint,
                   GLenum,
                   GLenum,
-                  DOMArrayBufferView*);
+                  NotShared<DOMArrayBufferView>);
   void texSubImage2D(GLenum,
                      GLint,
                      GLint,
@@ -474,7 +488,7 @@
                      GLsizei,
                      GLenum,
                      GLenum,
-                     DOMArrayBufferView*);
+                     NotShared<DOMArrayBufferView>);
 
   void copyTexSubImage3D(GLenum,
                          GLint,
@@ -492,7 +506,7 @@
                             GLsizei width,
                             GLsizei height,
                             GLint border,
-                            DOMArrayBufferView* data,
+                            NotShared<DOMArrayBufferView> data,
                             GLuint src_offset,
                             GLuint src_length_override);
   void compressedTexSubImage2D(GLenum target,
@@ -502,7 +516,7 @@
                                GLsizei width,
                                GLsizei height,
                                GLenum format,
-                               DOMArrayBufferView* data,
+                               NotShared<DOMArrayBufferView> data,
                                GLuint src_offset,
                                GLuint src_length_override);
   void compressedTexImage3D(GLenum,
@@ -512,7 +526,7 @@
                             GLsizei,
                             GLsizei,
                             GLint,
-                            DOMArrayBufferView*,
+                            NotShared<DOMArrayBufferView>,
                             GLuint,
                             GLuint);
   void compressedTexSubImage3D(GLenum,
@@ -524,7 +538,7 @@
                                GLsizei,
                                GLsizei,
                                GLenum,
-                               DOMArrayBufferView*,
+                               NotShared<DOMArrayBufferView>,
                                GLuint,
                                GLuint);
   void compressedTexImage2D(GLenum target,
@@ -574,7 +588,7 @@
                             GLsizei width,
                             GLsizei height,
                             GLint border,
-                            DOMArrayBufferView* data);
+                            NotShared<DOMArrayBufferView> data);
   void compressedTexSubImage2D(GLenum target,
                                GLint level,
                                GLint xoffset,
@@ -582,7 +596,7 @@
                                GLsizei width,
                                GLsizei height,
                                GLenum format,
-                               DOMArrayBufferView* data);
+                               NotShared<DOMArrayBufferView> data);
 
   /* Programs and shaders */
   GLint getFragDataLocation(WebGLProgram*, const String&);
@@ -678,7 +692,7 @@
                    GLuint);
   void uniformMatrix2fv(const WebGLUniformLocation*,
                         GLboolean,
-                        DOMFloat32Array*,
+                        NotShared<DOMFloat32Array>,
                         GLuint,
                         GLuint);
   void uniformMatrix2fv(const WebGLUniformLocation*,
@@ -688,7 +702,7 @@
                         GLuint);
   void uniformMatrix3fv(const WebGLUniformLocation*,
                         GLboolean,
-                        DOMFloat32Array*,
+                        NotShared<DOMFloat32Array>,
                         GLuint,
                         GLuint);
   void uniformMatrix3fv(const WebGLUniformLocation*,
@@ -698,7 +712,7 @@
                         GLuint);
   void uniformMatrix4fv(const WebGLUniformLocation*,
                         GLboolean,
-                        DOMFloat32Array*,
+                        NotShared<DOMFloat32Array>,
                         GLuint,
                         GLuint);
   void uniformMatrix4fv(const WebGLUniformLocation*,
@@ -708,7 +722,7 @@
                         GLuint);
   void uniformMatrix2x3fv(const WebGLUniformLocation*,
                           GLboolean,
-                          DOMFloat32Array*,
+                          NotShared<DOMFloat32Array>,
                           GLuint,
                           GLuint);
   void uniformMatrix2x3fv(const WebGLUniformLocation*,
@@ -718,7 +732,7 @@
                           GLuint);
   void uniformMatrix3x2fv(const WebGLUniformLocation*,
                           GLboolean,
-                          DOMFloat32Array*,
+                          NotShared<DOMFloat32Array>,
                           GLuint,
                           GLuint);
   void uniformMatrix3x2fv(const WebGLUniformLocation*,
@@ -728,7 +742,7 @@
                           GLuint);
   void uniformMatrix2x4fv(const WebGLUniformLocation*,
                           GLboolean,
-                          DOMFloat32Array*,
+                          NotShared<DOMFloat32Array>,
                           GLuint,
                           GLuint);
   void uniformMatrix2x4fv(const WebGLUniformLocation*,
@@ -738,7 +752,7 @@
                           GLuint);
   void uniformMatrix4x2fv(const WebGLUniformLocation*,
                           GLboolean,
-                          DOMFloat32Array*,
+                          NotShared<DOMFloat32Array>,
                           GLuint,
                           GLuint);
   void uniformMatrix4x2fv(const WebGLUniformLocation*,
@@ -748,7 +762,7 @@
                           GLuint);
   void uniformMatrix3x4fv(const WebGLUniformLocation*,
                           GLboolean,
-                          DOMFloat32Array*,
+                          NotShared<DOMFloat32Array>,
                           GLuint,
                           GLuint);
   void uniformMatrix3x4fv(const WebGLUniformLocation*,
@@ -758,7 +772,7 @@
                           GLuint);
   void uniformMatrix4x3fv(const WebGLUniformLocation*,
                           GLboolean,
-                          DOMFloat32Array*,
+                          NotShared<DOMFloat32Array>,
                           GLuint,
                           GLuint);
   void uniformMatrix4x3fv(const WebGLUniformLocation*,
@@ -787,28 +801,28 @@
   void uniform4iv(const WebGLUniformLocation*, Vector<GLint>&);
   void uniformMatrix2fv(const WebGLUniformLocation*,
                         GLboolean transpose,
-                        DOMFloat32Array* value);
+                        NotShared<DOMFloat32Array> value);
   void uniformMatrix2fv(const WebGLUniformLocation*,
                         GLboolean transpose,
                         Vector<GLfloat>& value);
   void uniformMatrix3fv(const WebGLUniformLocation*,
                         GLboolean transpose,
-                        DOMFloat32Array* value);
+                        NotShared<DOMFloat32Array> value);
   void uniformMatrix3fv(const WebGLUniformLocation*,
                         GLboolean transpose,
                         Vector<GLfloat>& value);
   void uniformMatrix4fv(const WebGLUniformLocation*,
                         GLboolean transpose,
-                        DOMFloat32Array* value);
+                        NotShared<DOMFloat32Array> value);
   void uniformMatrix4fv(const WebGLUniformLocation*,
                         GLboolean transpose,
                         Vector<GLfloat>& value);
 
   void vertexAttribI4i(GLuint, GLint, GLint, GLint, GLint);
-  void vertexAttribI4iv(GLuint, const DOMInt32Array*);
+  void vertexAttribI4iv(GLuint, NotShared<const DOMInt32Array>);
   void vertexAttribI4iv(GLuint, const Vector<GLint>&);
   void vertexAttribI4ui(GLuint, GLuint, GLuint, GLuint, GLuint);
-  void vertexAttribI4uiv(GLuint, const DOMUint32Array*);
+  void vertexAttribI4uiv(GLuint, NotShared<const DOMUint32Array>);
   void vertexAttribI4uiv(GLuint, const Vector<GLuint>&);
   void vertexAttribIPointer(GLuint index,
                             GLint size,
@@ -829,11 +843,11 @@
 
   /* Multiple Render Targets */
   void drawBuffers(const Vector<GLenum>&);
-  void clearBufferiv(GLenum, GLint, DOMInt32Array*);
+  void clearBufferiv(GLenum, GLint, NotShared<DOMInt32Array>);
   void clearBufferiv(GLenum, GLint, const Vector<GLint>&);
-  void clearBufferuiv(GLenum, GLint, DOMUint32Array*);
+  void clearBufferuiv(GLenum, GLint, NotShared<DOMUint32Array>);
   void clearBufferuiv(GLenum, GLint, const Vector<GLuint>&);
-  void clearBufferfv(GLenum, GLint, DOMFloat32Array*);
+  void clearBufferfv(GLenum, GLint, NotShared<DOMFloat32Array>);
   void clearBufferfv(GLenum, GLint, const Vector<GLfloat>&);
   void clearBufferfi(GLenum, GLint, GLfloat, GLint);
 
@@ -908,7 +922,7 @@
                   GLsizei height,
                   GLenum format,
                   GLenum type,
-                  DOMArrayBufferView* pixels,
+                  NotShared<DOMArrayBufferView> pixels,
                   GLuint offset);
   void readPixels(GLint x,
                   GLint y,
@@ -937,7 +951,7 @@
                   GLsizei height,
                   GLenum format,
                   GLenum type,
-                  DOMArrayBufferView* pixels) override;
+                  NotShared<DOMArrayBufferView> pixels) override;
   void RestoreCurrentFramebuffer() override;
 
   DECLARE_VIRTUAL_TRACE();
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLGetBufferSubDataAsync.cpp b/third_party/WebKit/Source/modules/webgl/WebGLGetBufferSubDataAsync.cpp
index a55e8ee..268e507d 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLGetBufferSubDataAsync.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLGetBufferSubDataAsync.cpp
@@ -36,7 +36,7 @@
     ScriptState* script_state,
     GLenum target,
     GLintptr src_byte_offset,
-    DOMArrayBufferView* dst_data,
+    NotShared<DOMArrayBufferView> dst_data,
     GLuint dst_offset,
     GLuint length) {
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
@@ -61,8 +61,8 @@
   void* destination_data_ptr = nullptr;
   long long destination_byte_length = 0;
   const char* message = context->ValidateGetBufferSubData(
-      __FUNCTION__, target, src_byte_offset, dst_data, dst_offset, length,
-      &source_buffer, &destination_data_ptr, &destination_byte_length);
+      __FUNCTION__, target, src_byte_offset, dst_data.View(), dst_offset,
+      length, &source_buffer, &destination_data_ptr, &destination_byte_length);
   if (message) {
     // If there was a GL error, it was already synthesized in
     // validateGetBufferSubData, so it's not done here.
@@ -83,7 +83,7 @@
 
   // If the length of the copy is zero, this is a no-op.
   if (!destination_byte_length) {
-    resolver->Resolve(dst_data);
+    resolver->Resolve(dst_data.View());
     return promise;
   }
 
@@ -101,8 +101,8 @@
   }
 
   auto callback_object = new WebGLGetBufferSubDataAsyncCallback(
-      context, resolver, mapped_data, query_id, dst_data, destination_data_ptr,
-      destination_byte_length);
+      context, resolver, mapped_data, query_id, dst_data.View(),
+      destination_data_ptr, destination_byte_length);
   context->RegisterGetBufferSubDataAsyncCallback(callback_object);
   auto callback = WTF::Bind(&WebGLGetBufferSubDataAsyncCallback::Resolve,
                             WrapPersistent(callback_object));
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLGetBufferSubDataAsync.h b/third_party/WebKit/Source/modules/webgl/WebGLGetBufferSubDataAsync.h
index 82f738b..d0f8ec9 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLGetBufferSubDataAsync.h
+++ b/third_party/WebKit/Source/modules/webgl/WebGLGetBufferSubDataAsync.h
@@ -6,6 +6,7 @@
 #define WebGLGetBufferSubDataAsync_h
 
 #include "bindings/core/v8/ScriptPromise.h"
+#include "core/dom/NotShared.h"
 #include "modules/webgl/WebGLExtension.h"
 
 namespace blink {
@@ -25,7 +26,7 @@
   ScriptPromise getBufferSubDataAsync(ScriptState*,
                                       GLenum target,
                                       GLintptr src_byte_offset,
-                                      DOMArrayBufferView*,
+                                      NotShared<DOMArrayBufferView>,
                                       GLuint dst_offset,
                                       GLuint length);
 
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
index f6091bd..38c7333 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
@@ -1562,8 +1562,8 @@
 
   return ImageData::Create(
       IntSize(width, height),
-      DOMUint8ClampedArray::Create(image_data_pixels, 0,
-                                   image_data_pixels->ByteLength()));
+      NotShared<DOMUint8ClampedArray>(DOMUint8ClampedArray::Create(
+          image_data_pixels, 0, image_data_pixels->ByteLength())));
 }
 
 void WebGLRenderingContextBase::Reshape(int width, int height) {
@@ -1903,12 +1903,13 @@
 }
 
 void WebGLRenderingContextBase::bufferData(GLenum target,
-                                           DOMArrayBufferView* data,
+                                           NotShared<DOMArrayBufferView> data,
                                            GLenum usage) {
   if (isContextLost())
     return;
   DCHECK(data);
-  BufferDataImpl(target, data->byteLength(), data->BaseAddress(), usage);
+  BufferDataImpl(target, data.View()->byteLength(), data.View()->BaseAddress(),
+                 usage);
 }
 
 void WebGLRenderingContextBase::BufferSubDataImpl(GLenum target,
@@ -2075,13 +2076,14 @@
   ContextGL()->CompileShader(ObjectOrZero(shader));
 }
 
-void WebGLRenderingContextBase::compressedTexImage2D(GLenum target,
-                                                     GLint level,
-                                                     GLenum internalformat,
-                                                     GLsizei width,
-                                                     GLsizei height,
-                                                     GLint border,
-                                                     DOMArrayBufferView* data) {
+void WebGLRenderingContextBase::compressedTexImage2D(
+    GLenum target,
+    GLint level,
+    GLenum internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLint border,
+    NotShared<DOMArrayBufferView> data) {
   if (isContextLost())
     return;
   if (!ValidateTexture2DBinding("compressedTexImage2D", target))
@@ -2089,8 +2091,8 @@
   if (!ValidateCompressedTexFormat("compressedTexImage2D", internalformat))
     return;
   ContextGL()->CompressedTexImage2D(target, level, internalformat, width,
-                                    height, border, data->byteLength(),
-                                    data->BaseAddress());
+                                    height, border, data.View()->byteLength(),
+                                    data.View()->BaseAddress());
 }
 
 void WebGLRenderingContextBase::compressedTexSubImage2D(
@@ -2101,16 +2103,16 @@
     GLsizei width,
     GLsizei height,
     GLenum format,
-    DOMArrayBufferView* data) {
+    NotShared<DOMArrayBufferView> data) {
   if (isContextLost())
     return;
   if (!ValidateTexture2DBinding("compressedTexSubImage2D", target))
     return;
   if (!ValidateCompressedTexFormat("compressedTexSubImage2D", format))
     return;
-  ContextGL()->CompressedTexSubImage2D(target, level, xoffset, yoffset, width,
-                                       height, format, data->byteLength(),
-                                       data->BaseAddress());
+  ContextGL()->CompressedTexSubImage2D(
+      target, level, xoffset, yoffset, width, height, format,
+      data.View()->byteLength(), data.View()->BaseAddress());
 }
 
 bool WebGLRenderingContextBase::ValidateSettableTexFormat(
@@ -4106,14 +4108,15 @@
   return true;
 }
 
-void WebGLRenderingContextBase::readPixels(GLint x,
-                                           GLint y,
-                                           GLsizei width,
-                                           GLsizei height,
-                                           GLenum format,
-                                           GLenum type,
-                                           DOMArrayBufferView* pixels) {
-  ReadPixelsHelper(x, y, width, height, format, type, pixels, 0);
+void WebGLRenderingContextBase::readPixels(
+    GLint x,
+    GLint y,
+    GLsizei width,
+    GLsizei height,
+    GLenum format,
+    GLenum type,
+    NotShared<DOMArrayBufferView> pixels) {
+  ReadPixelsHelper(x, y, width, height, format, type, pixels.View(), 0);
 }
 
 void WebGLRenderingContextBase::ReadPixelsHelper(GLint x,
@@ -4720,18 +4723,19 @@
                                format, type, data);
 }
 
-void WebGLRenderingContextBase::texImage2D(GLenum target,
-                                           GLint level,
-                                           GLint internalformat,
-                                           GLsizei width,
-                                           GLsizei height,
-                                           GLint border,
-                                           GLenum format,
-                                           GLenum type,
-                                           DOMArrayBufferView* pixels) {
+void WebGLRenderingContextBase::texImage2D(
+    GLenum target,
+    GLint level,
+    GLint internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLint border,
+    GLenum format,
+    GLenum type,
+    NotShared<DOMArrayBufferView> pixels) {
   TexImageHelperDOMArrayBufferView(kTexImage2D, target, level, internalformat,
                                    width, height, 1, border, format, type, 0, 0,
-                                   0, pixels, kNullAllowed, 0);
+                                   0, pixels.View(), kNullAllowed, 0);
 }
 
 void WebGLRenderingContextBase::TexImageHelperImageData(
@@ -5554,18 +5558,19 @@
   TexParameter(target, pname, 0, param, false);
 }
 
-void WebGLRenderingContextBase::texSubImage2D(GLenum target,
-                                              GLint level,
-                                              GLint xoffset,
-                                              GLint yoffset,
-                                              GLsizei width,
-                                              GLsizei height,
-                                              GLenum format,
-                                              GLenum type,
-                                              DOMArrayBufferView* pixels) {
+void WebGLRenderingContextBase::texSubImage2D(
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLsizei width,
+    GLsizei height,
+    GLenum format,
+    GLenum type,
+    NotShared<DOMArrayBufferView> pixels) {
   TexImageHelperDOMArrayBufferView(kTexSubImage2D, target, level, 0, width,
                                    height, 1, 0, format, type, xoffset, yoffset,
-                                   0, pixels, kNullNotAllowed, 0);
+                                   0, pixels.View(), kNullNotAllowed, 0);
 }
 
 void WebGLRenderingContextBase::texSubImage2D(GLenum target,
@@ -5919,13 +5924,13 @@
 void WebGLRenderingContextBase::uniformMatrix2fv(
     const WebGLUniformLocation* location,
     GLboolean transpose,
-    DOMFloat32Array* v) {
+    NotShared<DOMFloat32Array> v) {
   if (isContextLost() ||
       !ValidateUniformMatrixParameters("uniformMatrix2fv", location, transpose,
-                                       v, 4, 0, v->length()))
+                                       v.View(), 4, 0, v.View()->length()))
     return;
-  ContextGL()->UniformMatrix2fv(location->Location(), v->length() >> 2,
-                                transpose, v->Data());
+  ContextGL()->UniformMatrix2fv(location->Location(), v.View()->length() >> 2,
+                                transpose, v.View()->Data());
 }
 
 void WebGLRenderingContextBase::uniformMatrix2fv(
@@ -5943,13 +5948,13 @@
 void WebGLRenderingContextBase::uniformMatrix3fv(
     const WebGLUniformLocation* location,
     GLboolean transpose,
-    DOMFloat32Array* v) {
+    NotShared<DOMFloat32Array> v) {
   if (isContextLost() ||
       !ValidateUniformMatrixParameters("uniformMatrix3fv", location, transpose,
-                                       v, 9, 0, v->length()))
+                                       v.View(), 9, 0, v.View()->length()))
     return;
-  ContextGL()->UniformMatrix3fv(location->Location(), v->length() / 9,
-                                transpose, v->Data());
+  ContextGL()->UniformMatrix3fv(location->Location(), v.View()->length() / 9,
+                                transpose, v.View()->Data());
 }
 
 void WebGLRenderingContextBase::uniformMatrix3fv(
@@ -5967,13 +5972,13 @@
 void WebGLRenderingContextBase::uniformMatrix4fv(
     const WebGLUniformLocation* location,
     GLboolean transpose,
-    DOMFloat32Array* v) {
+    NotShared<DOMFloat32Array> v) {
   if (isContextLost() ||
       !ValidateUniformMatrixParameters("uniformMatrix4fv", location, transpose,
-                                       v, 16, 0, v->length()))
+                                       v.View(), 16, 0, v.View()->length()))
     return;
-  ContextGL()->UniformMatrix4fv(location->Location(), v->length() >> 4,
-                                transpose, v->Data());
+  ContextGL()->UniformMatrix4fv(location->Location(), v.View()->length() >> 4,
+                                transpose, v.View()->Data());
 }
 
 void WebGLRenderingContextBase::uniformMatrix4fv(
@@ -6029,15 +6034,16 @@
   SetVertexAttribType(index, kFloat32ArrayType);
 }
 
-void WebGLRenderingContextBase::vertexAttrib1fv(GLuint index,
-                                                const DOMFloat32Array* v) {
+void WebGLRenderingContextBase::vertexAttrib1fv(
+    GLuint index,
+    NotShared<const DOMFloat32Array> v) {
   if (isContextLost())
     return;
-  if (!v || v->length() < 1) {
+  if (!v.View() || v.View()->length() < 1) {
     SynthesizeGLError(GL_INVALID_VALUE, "vertexAttrib1fv", "invalid array");
     return;
   }
-  ContextGL()->VertexAttrib1fv(index, v->Data());
+  ContextGL()->VertexAttrib1fv(index, v.View()->Data());
   SetVertexAttribType(index, kFloat32ArrayType);
 }
 
@@ -6062,15 +6068,16 @@
   SetVertexAttribType(index, kFloat32ArrayType);
 }
 
-void WebGLRenderingContextBase::vertexAttrib2fv(GLuint index,
-                                                const DOMFloat32Array* v) {
+void WebGLRenderingContextBase::vertexAttrib2fv(
+    GLuint index,
+    NotShared<const DOMFloat32Array> v) {
   if (isContextLost())
     return;
-  if (!v || v->length() < 2) {
+  if (!v.View() || v.View()->length() < 2) {
     SynthesizeGLError(GL_INVALID_VALUE, "vertexAttrib2fv", "invalid array");
     return;
   }
-  ContextGL()->VertexAttrib2fv(index, v->Data());
+  ContextGL()->VertexAttrib2fv(index, v.View()->Data());
   SetVertexAttribType(index, kFloat32ArrayType);
 }
 
@@ -6096,15 +6103,16 @@
   SetVertexAttribType(index, kFloat32ArrayType);
 }
 
-void WebGLRenderingContextBase::vertexAttrib3fv(GLuint index,
-                                                const DOMFloat32Array* v) {
+void WebGLRenderingContextBase::vertexAttrib3fv(
+    GLuint index,
+    NotShared<const DOMFloat32Array> v) {
   if (isContextLost())
     return;
-  if (!v || v->length() < 3) {
+  if (!v.View() || v.View()->length() < 3) {
     SynthesizeGLError(GL_INVALID_VALUE, "vertexAttrib3fv", "invalid array");
     return;
   }
-  ContextGL()->VertexAttrib3fv(index, v->Data());
+  ContextGL()->VertexAttrib3fv(index, v.View()->Data());
   SetVertexAttribType(index, kFloat32ArrayType);
 }
 
@@ -6131,15 +6139,16 @@
   SetVertexAttribType(index, kFloat32ArrayType);
 }
 
-void WebGLRenderingContextBase::vertexAttrib4fv(GLuint index,
-                                                const DOMFloat32Array* v) {
+void WebGLRenderingContextBase::vertexAttrib4fv(
+    GLuint index,
+    NotShared<const DOMFloat32Array> v) {
   if (isContextLost())
     return;
-  if (!v || v->length() < 4) {
+  if (!v.View() || v.View()->length() < 4) {
     SynthesizeGLError(GL_INVALID_VALUE, "vertexAttrib4fv", "invalid array");
     return;
   }
-  ContextGL()->VertexAttrib4fv(index, v->Data());
+  ContextGL()->VertexAttrib4fv(index, v.View()->Data());
   SetVertexAttribType(index, kFloat32ArrayType);
 }
 
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
index 61362ce..3f92cf2 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
@@ -35,6 +35,7 @@
 #include "bindings/core/v8/ScriptWrappableVisitor.h"
 #include "core/CoreExport.h"
 #include "core/dom/DOMTypedArray.h"
+#include "core/dom/NotShared.h"
 #include "core/dom/TypedFlexibleArrayBufferView.h"
 #include "core/html/canvas/CanvasContextCreationAttributes.h"
 #include "core/html/canvas/CanvasRenderingContext.h"
@@ -166,7 +167,9 @@
 
   void bufferData(GLenum target, long long size, GLenum usage);
   void bufferData(GLenum target, DOMArrayBuffer* data, GLenum usage);
-  void bufferData(GLenum target, DOMArrayBufferView* data, GLenum usage);
+  void bufferData(GLenum target,
+                  NotShared<DOMArrayBufferView> data,
+                  GLenum usage);
   void bufferSubData(GLenum target, long long offset, DOMArrayBuffer* data);
   void bufferSubData(GLenum target,
                      long long offset,
@@ -189,7 +192,7 @@
                             GLsizei width,
                             GLsizei height,
                             GLint border,
-                            DOMArrayBufferView* data);
+                            NotShared<DOMArrayBufferView> data);
   void compressedTexSubImage2D(GLenum target,
                                GLint level,
                                GLint xoffset,
@@ -197,7 +200,7 @@
                                GLsizei width,
                                GLsizei height,
                                GLenum format,
-                               DOMArrayBufferView* data);
+                               NotShared<DOMArrayBufferView> data);
 
   void copyTexImage2D(GLenum target,
                       GLint level,
@@ -322,7 +325,7 @@
                           GLsizei height,
                           GLenum format,
                           GLenum type,
-                          DOMArrayBufferView* pixels);
+                          NotShared<DOMArrayBufferView> pixels);
   void renderbufferStorage(GLenum target,
                            GLenum internalformat,
                            GLsizei width,
@@ -345,7 +348,7 @@
                   GLint border,
                   GLenum format,
                   GLenum type,
-                  DOMArrayBufferView*);
+                  NotShared<DOMArrayBufferView>);
   void texImage2D(GLenum target,
                   GLint level,
                   GLint internalformat,
@@ -392,7 +395,7 @@
                      GLsizei height,
                      GLenum format,
                      GLenum type,
-                     DOMArrayBufferView*);
+                     NotShared<DOMArrayBufferView>);
   void texSubImage2D(GLenum target,
                      GLint level,
                      GLint xoffset,
@@ -467,19 +470,19 @@
   void uniform4iv(const WebGLUniformLocation*, Vector<GLint>&);
   void uniformMatrix2fv(const WebGLUniformLocation*,
                         GLboolean transpose,
-                        DOMFloat32Array* value);
+                        NotShared<DOMFloat32Array> value);
   void uniformMatrix2fv(const WebGLUniformLocation*,
                         GLboolean transpose,
                         Vector<GLfloat>& value);
   void uniformMatrix3fv(const WebGLUniformLocation*,
                         GLboolean transpose,
-                        DOMFloat32Array* value);
+                        NotShared<DOMFloat32Array> value);
   void uniformMatrix3fv(const WebGLUniformLocation*,
                         GLboolean transpose,
                         Vector<GLfloat>& value);
   void uniformMatrix4fv(const WebGLUniformLocation*,
                         GLboolean transpose,
-                        DOMFloat32Array* value);
+                        NotShared<DOMFloat32Array> value);
   void uniformMatrix4fv(const WebGLUniformLocation*,
                         GLboolean transpose,
                         Vector<GLfloat>& value);
@@ -488,16 +491,16 @@
   void validateProgram(WebGLProgram*);
 
   void vertexAttrib1f(GLuint index, GLfloat x);
-  void vertexAttrib1fv(GLuint index, const DOMFloat32Array* values);
+  void vertexAttrib1fv(GLuint index, NotShared<const DOMFloat32Array> values);
   void vertexAttrib1fv(GLuint index, const Vector<GLfloat>& values);
   void vertexAttrib2f(GLuint index, GLfloat x, GLfloat y);
-  void vertexAttrib2fv(GLuint index, const DOMFloat32Array* values);
+  void vertexAttrib2fv(GLuint index, NotShared<const DOMFloat32Array> values);
   void vertexAttrib2fv(GLuint index, const Vector<GLfloat>& values);
   void vertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z);
-  void vertexAttrib3fv(GLuint index, const DOMFloat32Array* values);
+  void vertexAttrib3fv(GLuint index, NotShared<const DOMFloat32Array> values);
   void vertexAttrib3fv(GLuint index, const Vector<GLfloat>& values);
   void vertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
-  void vertexAttrib4fv(GLuint index, const DOMFloat32Array* values);
+  void vertexAttrib4fv(GLuint index, NotShared<const DOMFloat32Array> values);
   void vertexAttrib4fv(GLuint index, const Vector<GLfloat>& values);
   void vertexAttribPointer(GLuint index,
                            GLint size,
diff --git a/third_party/WebKit/Source/modules/webmidi/MIDIMessageEvent.cpp b/third_party/WebKit/Source/modules/webmidi/MIDIMessageEvent.cpp
index f4a1075..e12dd9a 100644
--- a/third_party/WebKit/Source/modules/webmidi/MIDIMessageEvent.cpp
+++ b/third_party/WebKit/Source/modules/webmidi/MIDIMessageEvent.cpp
@@ -12,7 +12,7 @@
                                    const MIDIMessageEventInit& initializer)
     : Event(type, initializer) {
   if (initializer.hasData())
-    data_ = initializer.data();
+    data_ = initializer.data().View();
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/webmidi/MIDIOutput.cpp b/third_party/WebKit/Source/modules/webmidi/MIDIOutput.cpp
index 9070fad..e287267 100644
--- a/third_party/WebKit/Source/modules/webmidi/MIDIOutput.cpp
+++ b/third_party/WebKit/Source/modules/webmidi/MIDIOutput.cpp
@@ -221,7 +221,7 @@
 
 MIDIOutput::~MIDIOutput() {}
 
-void MIDIOutput::send(DOMUint8Array* array,
+void MIDIOutput::send(NotShared<DOMUint8Array> array,
                       double timestamp,
                       ExceptionState& exception_state) {
   DCHECK(array);
@@ -233,10 +233,11 @@
   // This should be performed even if |array| is invalid.
   open();
 
-  if (MessageValidator::Validate(array, exception_state,
-                                 midiAccess()->sysexEnabled()))
-    midiAccess()->SendMIDIData(port_index_, array->Data(), array->length(),
-                               timestamp);
+  if (MessageValidator::Validate(array.View(), exception_state,
+                                 midiAccess()->sysexEnabled())) {
+    midiAccess()->SendMIDIData(port_index_, array.View()->Data(),
+                               array.View()->length(), timestamp);
+  }
 }
 
 void MIDIOutput::send(Vector<unsigned> unsigned_data,
@@ -260,10 +261,11 @@
       array_data[i] = unsigned_data[i] & 0xff;
   }
 
-  send(array, timestamp, exception_state);
+  send(NotShared<DOMUint8Array>(array), timestamp, exception_state);
 }
 
-void MIDIOutput::send(DOMUint8Array* data, ExceptionState& exception_state) {
+void MIDIOutput::send(NotShared<DOMUint8Array> data,
+                      ExceptionState& exception_state) {
   DCHECK(data);
   send(data, 0.0, exception_state);
 }
diff --git a/third_party/WebKit/Source/modules/webmidi/MIDIOutput.h b/third_party/WebKit/Source/modules/webmidi/MIDIOutput.h
index 3bd59ed..74d4fca 100644
--- a/third_party/WebKit/Source/modules/webmidi/MIDIOutput.h
+++ b/third_party/WebKit/Source/modules/webmidi/MIDIOutput.h
@@ -32,6 +32,7 @@
 #define MIDIOutput_h
 
 #include "core/dom/DOMTypedArray.h"
+#include "core/dom/NotShared.h"
 #include "modules/webmidi/MIDIPort.h"
 
 namespace blink {
@@ -52,11 +53,11 @@
                             midi::mojom::PortState);
   ~MIDIOutput() override;
 
-  void send(DOMUint8Array*, double timestamp, ExceptionState&);
+  void send(NotShared<DOMUint8Array>, double timestamp, ExceptionState&);
   void send(Vector<unsigned>, double timestamp, ExceptionState&);
 
   // send() without optional |timestamp|.
-  void send(DOMUint8Array*, ExceptionState&);
+  void send(NotShared<DOMUint8Array>, ExceptionState&);
   void send(Vector<unsigned>, ExceptionState&);
 
   DECLARE_VIRTUAL_TRACE();
diff --git a/third_party/WebKit/Source/modules/websockets/DOMWebSocket.cpp b/third_party/WebKit/Source/modules/websockets/DOMWebSocket.cpp
index 767e95e..00b6b25 100644
--- a/third_party/WebKit/Source/modules/websockets/DOMWebSocket.cpp
+++ b/third_party/WebKit/Source/modules/websockets/DOMWebSocket.cpp
@@ -477,26 +477,27 @@
   channel_->Send(*binary_data, 0, binary_data->ByteLength());
 }
 
-void DOMWebSocket::send(DOMArrayBufferView* array_buffer_view,
+void DOMWebSocket::send(NotShared<DOMArrayBufferView> array_buffer_view,
                         ExceptionState& exception_state) {
   NETWORK_DVLOG(1) << "WebSocket " << this << " send() Sending ArrayBufferView "
-                   << array_buffer_view;
+                   << array_buffer_view.View();
   DCHECK(array_buffer_view);
   if (state_ == kConnecting) {
     SetInvalidStateErrorForSendMethod(exception_state);
     return;
   }
   if (state_ == kClosing || state_ == kClosed) {
-    UpdateBufferedAmountAfterClose(array_buffer_view->byteLength());
+    UpdateBufferedAmountAfterClose(array_buffer_view.View()->byteLength());
     return;
   }
   RecordSendTypeHistogram(kWebSocketSendTypeArrayBufferView);
   RecordSendMessageSizeHistogram(kWebSocketSendTypeArrayBufferView,
-                                 array_buffer_view->byteLength());
+                                 array_buffer_view.View()->byteLength());
   DCHECK(channel_);
-  buffered_amount_ += array_buffer_view->byteLength();
-  channel_->Send(*array_buffer_view->buffer(), array_buffer_view->byteOffset(),
-                 array_buffer_view->byteLength());
+  buffered_amount_ += array_buffer_view.View()->byteLength();
+  channel_->Send(*array_buffer_view.View()->buffer(),
+                 array_buffer_view.View()->byteOffset(),
+                 array_buffer_view.View()->byteLength());
 }
 
 void DOMWebSocket::send(Blob* binary_data, ExceptionState& exception_state) {
diff --git a/third_party/WebKit/Source/modules/websockets/DOMWebSocket.h b/third_party/WebKit/Source/modules/websockets/DOMWebSocket.h
index 0f1cdff0..42c9a94b 100644
--- a/third_party/WebKit/Source/modules/websockets/DOMWebSocket.h
+++ b/third_party/WebKit/Source/modules/websockets/DOMWebSocket.h
@@ -36,6 +36,7 @@
 #include <memory>
 #include "bindings/core/v8/ActiveScriptWrappable.h"
 #include "bindings/core/v8/ScriptWrappable.h"
+#include "core/dom/NotShared.h"
 #include "core/dom/SuspendableObject.h"
 #include "core/events/EventListener.h"
 #include "core/events/EventTarget.h"
@@ -90,7 +91,7 @@
 
   void send(const String& message, ExceptionState&);
   void send(DOMArrayBuffer*, ExceptionState&);
-  void send(DOMArrayBufferView*, ExceptionState&);
+  void send(NotShared<DOMArrayBufferView>, ExceptionState&);
   void send(Blob*, ExceptionState&);
 
   // To distinguish close method call with the code parameter from one
diff --git a/third_party/WebKit/Source/modules/webusb/USBDevice.cpp b/third_party/WebKit/Source/modules/webusb/USBDevice.cpp
index bbe535f..e651ee3 100644
--- a/third_party/WebKit/Source/modules/webusb/USBDevice.cpp
+++ b/third_party/WebKit/Source/modules/webusb/USBDevice.cpp
@@ -85,9 +85,9 @@
     vector.Append(static_cast<uint8_t*>(buffer.getAsArrayBuffer()->Data()),
                   buffer.getAsArrayBuffer()->ByteLength());
   else
-    vector.Append(
-        static_cast<uint8_t*>(buffer.getAsArrayBufferView()->BaseAddress()),
-        buffer.getAsArrayBufferView()->byteLength());
+    vector.Append(static_cast<uint8_t*>(
+                      buffer.getAsArrayBufferView().View()->BaseAddress()),
+                  buffer.getAsArrayBufferView().View()->byteLength());
   return vector;
 }
 
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
index 08cb740..d4ff05f7 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
@@ -167,7 +167,7 @@
   EXPECT_THAT(
       child->GetPicture(),
       Pointee(DrawsRectangle(FloatRect(0, 0, 100, 100), Color::kWhite)));
-  EXPECT_EQ(Translation(50, -50), child->screen_space_transform());
+  EXPECT_EQ(Translation(50, -50), child->ScreenSpaceTransform());
   EXPECT_EQ(gfx::Size(100, 100), child->bounds());
 }
 
@@ -206,7 +206,7 @@
     EXPECT_THAT(layer->GetPicture(),
                 Pointee(DrawsRectangles(rects_with_color)));
     gfx::RectF mapped_rect(0, 0, 100, 100);
-    layer->screen_space_transform().TransformRect(&mapped_rect);
+    layer->ScreenSpaceTransform().TransformRect(&mapped_rect);
     EXPECT_EQ(gfx::RectF(100, 0, 100, 100), mapped_rect);
   }
   {
@@ -214,7 +214,7 @@
     EXPECT_THAT(
         layer->GetPicture(),
         Pointee(DrawsRectangle(FloatRect(0, 0, 100, 100), Color::kGray)));
-    EXPECT_EQ(gfx::Transform(), layer->screen_space_transform());
+    EXPECT_EQ(gfx::Transform(), layer->ScreenSpaceTransform());
   }
 }
 
@@ -247,7 +247,7 @@
         layer->GetPicture(),
         Pointee(DrawsRectangle(FloatRect(0, 0, 300, 200), Color::kWhite)));
     gfx::RectF mapped_rect(0, 0, 300, 200);
-    layer->screen_space_transform().TransformRect(&mapped_rect);
+    layer->ScreenSpaceTransform().TransformRect(&mapped_rect);
     EXPECT_EQ(gfx::RectF(-10, -10, 600, 400), mapped_rect);
   }
   {
@@ -256,7 +256,7 @@
         layer->GetPicture(),
         Pointee(DrawsRectangle(FloatRect(0, 0, 300, 200), Color::kBlack)));
     gfx::RectF mapped_rect(0, 0, 300, 200);
-    layer->screen_space_transform().TransformRect(&mapped_rect);
+    layer->ScreenSpaceTransform().TransformRect(&mapped_rect);
     EXPECT_EQ(gfx::RectF(0, 0, 600, 400), mapped_rect);
   }
   EXPECT_NE(ContentLayerAt(0)->transform_tree_index(),
@@ -313,7 +313,7 @@
     // empty rectangle (as the total 90 degree rotation makes it
     // perpendicular to the viewport).
     gfx::RectF rect(0, 0, 100, 100);
-    layer->screen_space_transform().TransformRect(&rect);
+    layer->ScreenSpaceTransform().TransformRect(&rect);
     if (transform_is_flattened)
       EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 0, 50, 100), rect);
     else
@@ -424,7 +424,7 @@
   EXPECT_THAT(
       layer->GetPicture(),
       Pointee(DrawsRectangle(FloatRect(0, 0, 300, 200), Color::kBlack)));
-  EXPECT_EQ(Translation(220, 80), layer->screen_space_transform());
+  EXPECT_EQ(Translation(220, 80), layer->ScreenSpaceTransform());
 
   const cc::ClipNode* clip_node =
       GetPropertyTrees().clip_tree.Node(layer->clip_tree_index());
@@ -467,25 +467,25 @@
   EXPECT_THAT(
       white_layer->GetPicture(),
       Pointee(DrawsRectangle(FloatRect(0, 0, 100, 100), Color::kWhite)));
-  EXPECT_EQ(Translation(300, 350), white_layer->screen_space_transform());
+  EXPECT_EQ(Translation(300, 350), white_layer->ScreenSpaceTransform());
 
   const cc::Layer* light_gray_layer = ContentLayerAt(1);
   EXPECT_THAT(
       light_gray_layer->GetPicture(),
       Pointee(DrawsRectangle(FloatRect(0, 0, 100, 100), Color::kLightGray)));
-  EXPECT_EQ(Translation(300, 350), light_gray_layer->screen_space_transform());
+  EXPECT_EQ(Translation(300, 350), light_gray_layer->ScreenSpaceTransform());
 
   const cc::Layer* dark_gray_layer = ContentLayerAt(2);
   EXPECT_THAT(
       dark_gray_layer->GetPicture(),
       Pointee(DrawsRectangle(FloatRect(0, 0, 100, 100), Color::kDarkGray)));
-  EXPECT_EQ(Translation(300, 350), dark_gray_layer->screen_space_transform());
+  EXPECT_EQ(Translation(300, 350), dark_gray_layer->ScreenSpaceTransform());
 
   const cc::Layer* black_layer = ContentLayerAt(3);
   EXPECT_THAT(
       black_layer->GetPicture(),
       Pointee(DrawsRectangle(FloatRect(0, 0, 100, 100), Color::kBlack)));
-  EXPECT_EQ(Translation(300, 350), black_layer->screen_space_transform());
+  EXPECT_EQ(Translation(300, 350), black_layer->ScreenSpaceTransform());
 
   EXPECT_EQ(white_layer->clip_tree_index(), dark_gray_layer->clip_tree_index());
   const cc::ClipNode* outer_clip =
@@ -524,7 +524,7 @@
   EXPECT_THAT(
       drawing_layer->GetPicture(),
       Pointee(DrawsRectangle(FloatRect(0, 0, 200, 200), Color::kWhite)));
-  EXPECT_EQ(gfx::Transform(), drawing_layer->screen_space_transform());
+  EXPECT_EQ(gfx::Transform(), drawing_layer->ScreenSpaceTransform());
 
   // Check the clip nodes.
   const cc::ClipNode* clip_node =
@@ -564,7 +564,7 @@
   EXPECT_THAT(
       white_layer->GetPicture(),
       Pointee(DrawsRectangle(FloatRect(0, 0, 640, 480), Color::kWhite)));
-  EXPECT_EQ(gfx::Transform(), white_layer->screen_space_transform());
+  EXPECT_EQ(gfx::Transform(), white_layer->ScreenSpaceTransform());
   const cc::ClipNode* white_clip =
       GetPropertyTrees().clip_tree.Node(white_layer->clip_tree_index());
   EXPECT_EQ(cc::ClipNode::ClipType::APPLIES_LOCAL_CLIP, white_clip->clip_type);
@@ -574,7 +574,7 @@
   EXPECT_THAT(
       black_layer->GetPicture(),
       Pointee(DrawsRectangle(FloatRect(0, 0, 640, 480), Color::kBlack)));
-  EXPECT_EQ(gfx::Transform(), black_layer->screen_space_transform());
+  EXPECT_EQ(gfx::Transform(), black_layer->ScreenSpaceTransform());
   const cc::ClipNode* black_clip =
       GetPropertyTrees().clip_tree.Node(black_layer->clip_tree_index());
   EXPECT_EQ(cc::ClipNode::ClipType::APPLIES_LOCAL_CLIP, black_clip->clip_type);
@@ -611,7 +611,7 @@
   ASSERT_EQ(3u, ContentLayerCount());
   EXPECT_EQ(layer, ContentLayerAt(1));
   EXPECT_EQ(gfx::Size(400, 300), layer->bounds());
-  EXPECT_EQ(Translation(50, 60), layer->screen_space_transform());
+  EXPECT_EQ(Translation(50, 60), layer->ScreenSpaceTransform());
 }
 
 TEST_F(PaintArtifactCompositorTestWithPropertyTrees, EffectTreeConversion) {
@@ -1719,7 +1719,7 @@
   const cc::Layer* masked_layer = ContentLayerAt(0);
   EXPECT_THAT(masked_layer->GetPicture(),
               Pointee(DrawsRectangle(FloatRect(0, 0, 200, 200), Color::kGray)));
-  EXPECT_EQ(Translation(100, 100), masked_layer->screen_space_transform());
+  EXPECT_EQ(Translation(100, 100), masked_layer->ScreenSpaceTransform());
   EXPECT_EQ(gfx::Size(200, 200), masked_layer->bounds());
   const cc::EffectNode* masked_group =
       GetPropertyTrees().effect_tree.Node(masked_layer->effect_tree_index());
@@ -1729,7 +1729,7 @@
   EXPECT_THAT(
       masking_layer->GetPicture(),
       Pointee(DrawsRectangle(FloatRect(0, 0, 100, 100), Color::kWhite)));
-  EXPECT_EQ(Translation(150, 150), masking_layer->screen_space_transform());
+  EXPECT_EQ(Translation(150, 150), masking_layer->ScreenSpaceTransform());
   EXPECT_EQ(gfx::Size(100, 100), masking_layer->bounds());
   const cc::EffectNode* masking_group =
       GetPropertyTrees().effect_tree.Node(masking_layer->effect_tree_index());
diff --git a/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.cc b/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.cc
index ac1cb052..802e56f6 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.cc
@@ -13,15 +13,7 @@
 namespace scheduler {
 
 RealTimeDomain::RealTimeDomain(const char* tracing_category)
-    : TimeDomain(nullptr),
-      tracing_category_(tracing_category),
-      task_queue_manager_(nullptr) {}
-
-RealTimeDomain::RealTimeDomain(TimeDomain::Observer* observer,
-                               const char* tracing_category)
-    : TimeDomain(observer),
-      tracing_category_(tracing_category),
-      task_queue_manager_(nullptr) {}
+    : tracing_category_(tracing_category), task_queue_manager_(nullptr) {}
 
 RealTimeDomain::~RealTimeDomain() {}
 
diff --git a/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.h b/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.h
index b8a5bd5..872cde9 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.h
@@ -17,7 +17,6 @@
 class BLINK_PLATFORM_EXPORT RealTimeDomain : public TimeDomain {
  public:
   explicit RealTimeDomain(const char* tracing_category);
-  RealTimeDomain(TimeDomain::Observer* observer, const char* tracing_category);
   ~RealTimeDomain() override;
 
   // TimeDomain implementation:
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.cc
index 1006ce0..1ff9b277a 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.cc
@@ -102,7 +102,6 @@
   // destructor calls UnregisterTaskQueue on all task queues.
   DCHECK(any_thread().task_queue_manager == nullptr)
       << "UnregisterTaskQueue must be called first!";
-
 #endif
 }
 
@@ -147,7 +146,9 @@
 
 TaskQueueImpl::AnyThread::AnyThread(TaskQueueManager* task_queue_manager,
                                     TimeDomain* time_domain)
-    : task_queue_manager(task_queue_manager), time_domain(time_domain) {}
+    : task_queue_manager(task_queue_manager),
+      time_domain(time_domain),
+      observer(nullptr) {}
 
 TaskQueueImpl::AnyThread::~AnyThread() {}
 
@@ -157,6 +158,7 @@
     TimeDomain* time_domain)
     : task_queue_manager(task_queue_manager),
       time_domain(time_domain),
+      observer(nullptr),
       delayed_work_queue(
           new WorkQueue(task_queue, "delayed", WorkQueue::QueueType::DELAYED)),
       immediate_work_queue(new WorkQueue(task_queue,
@@ -183,6 +185,8 @@
 
   any_thread().task_queue_manager = nullptr;
   main_thread_only().task_queue_manager = nullptr;
+  any_thread().observer = nullptr;
+  main_thread_only().observer = nullptr;
   main_thread_only().delayed_incoming_queue = std::priority_queue<Task>();
   immediate_incoming_queue().Clear();
   main_thread_only().immediate_work_queue.reset();
@@ -277,7 +281,7 @@
 
 void TaskQueueImpl::PushOntoDelayedIncomingQueueFromMainThread(
     Task pending_task, base::TimeTicks now) {
-  base::TimeTicks delayed_run_time = pending_task.delayed_run_time;
+  DelayedWakeUp wake_up = pending_task.delayed_wake_up();
   main_thread_only().task_queue_manager->DidQueueTask(pending_task);
   main_thread_only().delayed_incoming_queue.push(std::move(pending_task));
 
@@ -285,11 +289,11 @@
   // is requested if the queue is enabled.  Note we still want to schedule a
   // wake-up even if blocked by a fence, because we'd break throttling logic
   // otherwise.
-  base::TimeTicks next_delayed_task =
-      main_thread_only().delayed_incoming_queue.top().delayed_run_time;
-  if (next_delayed_task == delayed_run_time && IsQueueEnabled()) {
-    main_thread_only().time_domain->ScheduleDelayedWork(
-        this, {delayed_run_time, pending_task.sequence_num}, now);
+  DelayedWakeUp new_wake_up =
+      main_thread_only().delayed_incoming_queue.top().delayed_wake_up();
+  if (wake_up.time == new_wake_up.time &&
+      wake_up.sequence_num == new_wake_up.sequence_num) {
+    ScheduleDelayedWorkInTimeDomain(now);
   }
 
   TraceQueueSize();
@@ -358,7 +362,8 @@
         (!IsQueueEnabled() || main_thread_only().current_fence);
     any_thread().task_queue_manager->OnQueueHasIncomingImmediateWork(
         this, sequence_number, queue_is_blocked);
-    any_thread().time_domain->OnQueueHasImmediateWork(this);
+    if (any_thread().observer)
+      any_thread().observer->OnQueueNextWakeUpChanged(this, desired_run_time);
   }
 
   TraceQueueSize();
@@ -449,9 +454,7 @@
 
   // Make sure the next wake up is scheduled.
   if (!main_thread_only().delayed_incoming_queue.empty()) {
-    return DelayedWakeUp{
-        main_thread_only().delayed_incoming_queue.top().delayed_run_time,
-        main_thread_only().delayed_incoming_queue.top().sequence_num};
+    return main_thread_only().delayed_incoming_queue.top().delayed_wake_up();
   }
 
   return base::nullopt;
@@ -598,13 +601,7 @@
   main_thread_only().time_domain = time_domain;
   time_domain->RegisterQueue(this);
 
-  if (IsQueueEnabled() && !main_thread_only().delayed_incoming_queue.empty()) {
-    time_domain->ScheduleDelayedWork(
-        this,
-        {main_thread_only().delayed_incoming_queue.top().delayed_run_time,
-         main_thread_only().delayed_incoming_queue.top().sequence_num},
-        time_domain->Now());
-  }
+  ScheduleDelayedWorkInTimeDomain(time_domain->Now());
 }
 
 TimeDomain* TaskQueueImpl::GetTimeDomain() const {
@@ -816,24 +813,11 @@
     return;
 
   if (enable) {
-    // Check if there's any immediate work on either queue.
-    bool immediate_queues_empty =
-        main_thread_only().immediate_work_queue->Empty();
-    if (immediate_queues_empty) {
-      base::AutoLock lock(immediate_incoming_queue_lock_);
-      immediate_queues_empty = immediate_incoming_queue().empty();
-    }
-    // Avoid holding the lock while we fire the notification.
-    if (!immediate_queues_empty)
-      main_thread_only().time_domain->OnQueueHasImmediateWork(this);
+    if (HasPendingImmediateWork())
+      NotifyWakeUpChangedOnMainThread(base::TimeTicks());
 
-    if (!main_thread_only().delayed_incoming_queue.empty()) {
-      main_thread_only().time_domain->ScheduleDelayedWork(
-          this,
-          {main_thread_only().delayed_incoming_queue.top().delayed_run_time,
-           main_thread_only().delayed_incoming_queue.top().sequence_num},
-          main_thread_only().time_domain->Now());
-    }
+    ScheduleDelayedWorkInTimeDomain(main_thread_only().time_domain->Now());
+
     // Note the selector calls TaskQueueManager::OnTaskQueueEnabled which posts
     // a DoWork if needed.
     main_thread_only().task_queue_manager->selector_.EnableQueue(this);
@@ -875,13 +859,7 @@
     main_thread_only().time_domain->CancelDelayedWork(this);
   } else if (first_task_runtime !=
              main_thread_only().delayed_incoming_queue.top().delayed_run_time) {
-    if (IsQueueEnabled()) {
-      main_thread_only().time_domain->ScheduleDelayedWork(
-          this,
-          {main_thread_only().delayed_incoming_queue.top().delayed_run_time,
-           main_thread_only().delayed_incoming_queue.top().sequence_num},
-          main_thread_only().time_domain->Now());
-    }
+    ScheduleDelayedWorkInTimeDomain(main_thread_only().time_domain->Now());
   }
 }
 
@@ -891,6 +869,40 @@
   immediate_incoming_queue().push_back(std::move(task));
 }
 
+void TaskQueueImpl::SetObserver(Observer* observer) {
+#if DCHECK_IS_ON()
+  if (observer) {
+    DCHECK(!main_thread_only().observer) << "Can't assign two different "
+                                            "observers to "
+                                            "blink::scheduler::TaskQueue";
+  }
+#endif
+  base::AutoLock lock(any_thread_lock_);
+  any_thread().observer = observer;
+  main_thread_only().observer = observer;
+}
+
+void TaskQueueImpl::ScheduleDelayedWorkInTimeDomain(base::TimeTicks now) {
+  if (!IsQueueEnabled())
+    return;
+  if (main_thread_only().delayed_incoming_queue.empty())
+    return;
+
+  main_thread_only().time_domain->ScheduleDelayedWork(
+      this, main_thread_only().delayed_incoming_queue.top().delayed_wake_up(),
+      now);
+
+  if (!HasPendingImmediateWork()) {
+    NotifyWakeUpChangedOnMainThread(
+        main_thread_only().delayed_incoming_queue.top().delayed_run_time);
+  }
+}
+
+void TaskQueueImpl::NotifyWakeUpChangedOnMainThread(base::TimeTicks wake_up) {
+  if (main_thread_only().observer)
+    main_thread_only().observer->OnQueueNextWakeUpChanged(this, wake_up);
+}
+
 }  // namespace internal
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.h b/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.h
index e1c3a85..02be214e 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.h
@@ -65,6 +65,21 @@
                 const char* disabled_by_default_tracing_category,
                 const char* disabled_by_default_verbose_tracing_category);
 
+  // Represents a time at which a task wants to run. Tasks scheduled for the
+  // same point in time will be ordered by their sequence numbers.
+  struct DelayedWakeUp {
+    base::TimeTicks time;
+    int sequence_num;
+
+    bool operator<=(const DelayedWakeUp& other) const {
+      if (time == other.time) {
+        DCHECK_NE(sequence_num, other.sequence_num);
+        return (sequence_num - other.sequence_num) < 0;
+      }
+      return time < other.time;
+    }
+  };
+
   class BLINK_PLATFORM_EXPORT Task : public base::PendingTask {
    public:
     Task();
@@ -81,6 +96,10 @@
          bool nestable,
          EnqueueOrder enqueue_order);
 
+    DelayedWakeUp delayed_wake_up() const {
+      return DelayedWakeUp{delayed_run_time, sequence_num};
+    }
+
     EnqueueOrder enqueue_order() const {
 #ifndef NDEBUG
       DCHECK(enqueue_order_set_);
@@ -112,21 +131,6 @@
     EnqueueOrder enqueue_order_;
   };
 
-  // Represents a time at which a task wants to run. Tasks scheduled for the
-  // same point in time will be ordered by their sequence numbers.
-  struct DelayedWakeUp {
-    base::TimeTicks time;
-    int sequence_num;
-
-    bool operator<=(const DelayedWakeUp& other) const {
-      if (time == other.time) {
-        DCHECK_NE(sequence_num, other.sequence_num);
-        return (sequence_num - other.sequence_num) < 0;
-      }
-      return time < other.time;
-    }
-  };
-
   // TaskQueue implementation.
   void UnregisterTaskQueue() override;
   bool RunsTasksOnCurrentThread() const override;
@@ -155,6 +159,7 @@
   bool BlockedByFence() const override;
   const char* GetName() const override;
   QueueType GetQueueType() const override;
+  void SetObserver(Observer* observer) override;
 
   // Returns true if a (potentially hypothetical) task with the specified
   // |enqueue_order| could run on the queue. Must be called from the main
@@ -249,11 +254,12 @@
     AnyThread(TaskQueueManager* task_queue_manager, TimeDomain* time_domain);
     ~AnyThread();
 
-    // TaskQueueManager and TimeDomain are maintained in two copies:
+    // TaskQueueManager, TimeDomain and Observer are maintained in two copies:
     // inside AnyThread and inside MainThreadOnly. They can be changed only from
     // main thread, so it should be locked before accessing from other threads.
     TaskQueueManager* task_queue_manager;
     TimeDomain* time_domain;
+    Observer* observer;
   };
 
   struct MainThreadOnly {
@@ -262,10 +268,12 @@
                    TimeDomain* time_domain);
     ~MainThreadOnly();
 
-    // Another copy of TaskQueueManager and TimeDomain for lock-free access from
-    // the main thread. See description inside struct AnyThread for details.
+    // Another copy of TaskQueueManager, TimeDomain and Observer
+    // for lock-free access from the main thread.
+    // See description inside struct AnyThread for details.
     TaskQueueManager* task_queue_manager;
     TimeDomain* time_domain;
+    Observer* observer;
 
     std::unique_ptr<WorkQueue> delayed_work_queue;
     std::unique_ptr<WorkQueue> immediate_work_queue;
@@ -329,6 +337,11 @@
   void OnQueueEnabledVoteChanged(bool enabled);
   void EnableOrDisableWithSelector(bool enable);
 
+  // Schedules delayed work on time domain and calls the observer.
+  void ScheduleDelayedWorkInTimeDomain(base::TimeTicks now);
+
+  void NotifyWakeUpChangedOnMainThread(base::TimeTicks wake_up);
+
   const base::PlatformThreadId thread_id_;
 
   mutable base::Lock any_thread_lock_;
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_perftest.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_perftest.cc
index a0e4922cb..8a9138e 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_perftest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_perftest.cc
@@ -29,7 +29,7 @@
 // fast forward the timers.
 class PerfTestTimeDomain : public VirtualTimeDomain {
  public:
-  PerfTestTimeDomain() : VirtualTimeDomain(nullptr, base::TimeTicks::Now()) {}
+  PerfTestTimeDomain() : VirtualTimeDomain(base::TimeTicks::Now()) {}
   ~PerfTestTimeDomain() override {}
 
   base::Optional<base::TimeDelta> DelayTillNextTask(
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc
index b797052..52fa223 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc
@@ -1545,9 +1545,9 @@
 
   base::TimeTicks start_time = manager_->Delegate()->NowTicks();
   std::unique_ptr<VirtualTimeDomain> domain_a(
-      new VirtualTimeDomain(nullptr, start_time));
+      new VirtualTimeDomain(start_time));
   std::unique_ptr<VirtualTimeDomain> domain_b(
-      new VirtualTimeDomain(nullptr, start_time));
+      new VirtualTimeDomain(start_time));
   manager_->RegisterTimeDomain(domain_a.get());
   manager_->RegisterTimeDomain(domain_b.get());
   runners_[0]->SetTimeDomain(domain_a.get());
@@ -1592,7 +1592,7 @@
 
   base::TimeTicks start_time = manager_->Delegate()->NowTicks();
   std::unique_ptr<VirtualTimeDomain> domain_a(
-      new VirtualTimeDomain(nullptr, start_time));
+      new VirtualTimeDomain(start_time));
   manager_->RegisterTimeDomain(domain_a.get());
   runners_[0]->SetTimeDomain(domain_a.get());
 
@@ -1612,7 +1612,7 @@
   EXPECT_THAT(run_order, ElementsAre(1, 2));
 
   std::unique_ptr<VirtualTimeDomain> domain_b(
-      new VirtualTimeDomain(nullptr, start_time));
+      new VirtualTimeDomain(start_time));
   manager_->RegisterTimeDomain(domain_b.get());
   runners_[0]->SetTimeDomain(domain_b.get());
 
@@ -1633,9 +1633,9 @@
 
   base::TimeTicks start_time = manager_->Delegate()->NowTicks();
   std::unique_ptr<VirtualTimeDomain> domain_a(
-      new VirtualTimeDomain(nullptr, start_time));
+      new VirtualTimeDomain(start_time));
   std::unique_ptr<VirtualTimeDomain> domain_b(
-      new VirtualTimeDomain(nullptr, start_time));
+      new VirtualTimeDomain(start_time));
   manager_->RegisterTimeDomain(domain_a.get());
   manager_->RegisterTimeDomain(domain_b.get());
 
@@ -1690,68 +1690,64 @@
 }
 
 namespace {
-class MockTimeDomainObserver : public TimeDomain::Observer {
- public:
-  ~MockTimeDomainObserver() override {}
 
-  MOCK_METHOD1(OnTimeDomainHasImmediateWork, void(TaskQueue*));
-  MOCK_METHOD1(OnTimeDomainHasDelayedWork, void(TaskQueue*));
+class MockTaskQueueObserver : public TaskQueue::Observer {
+ public:
+  ~MockTaskQueueObserver() override {}
+
+  MOCK_METHOD2(OnQueueNextWakeUpChanged, void(TaskQueue*, base::TimeTicks));
 };
+
 }  // namespace
 
-TEST_F(TaskQueueManagerTest, TimeDomainObserver_ImmediateTask) {
+TEST_F(TaskQueueManagerTest, TaskQueueObserver_ImmediateTask) {
   Initialize(1u);
 
-  MockTimeDomainObserver observer;
-  std::unique_ptr<VirtualTimeDomain> domain(
-      new VirtualTimeDomain(&observer, manager_->Delegate()->NowTicks()));
-  manager_->RegisterTimeDomain(domain.get());
-  runners_[0]->SetTimeDomain(domain.get());
+  MockTaskQueueObserver observer;
+  runners_[0]->SetObserver(&observer);
 
   // We should get a notification when a task is posted on an empty queue.
-  EXPECT_CALL(observer, OnTimeDomainHasImmediateWork(runners_[0].get()));
+  EXPECT_CALL(observer,
+              OnQueueNextWakeUpChanged(runners_[0].get(), base::TimeTicks()));
   runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask));
   Mock::VerifyAndClearExpectations(&observer);
 
   // But not subsequently.
-  EXPECT_CALL(observer, OnTimeDomainHasImmediateWork(_)).Times(0);
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
   runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask));
   Mock::VerifyAndClearExpectations(&observer);
 
   // Unless the immediate work queue is emptied.
   runners_[0]->ReloadImmediateWorkQueueIfEmpty();
-  EXPECT_CALL(observer, OnTimeDomainHasImmediateWork(runners_[0].get()));
+  EXPECT_CALL(observer,
+              OnQueueNextWakeUpChanged(runners_[0].get(), base::TimeTicks()));
   runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask));
 
   // Tidy up.
   runners_[0]->UnregisterTaskQueue();
-  manager_->UnregisterTimeDomain(domain.get());
 }
 
-TEST_F(TaskQueueManagerTest, TimeDomainObserver_DelayedTask) {
+TEST_F(TaskQueueManagerTest, TaskQueueObserver_DelayedTask) {
   Initialize(1u);
 
-  MockTimeDomainObserver observer;
-  std::unique_ptr<VirtualTimeDomain> domain(
-      new VirtualTimeDomain(&observer, manager_->Delegate()->NowTicks()));
-  manager_->RegisterTimeDomain(domain.get());
-  runners_[0]->SetTimeDomain(domain.get());
+  MockTaskQueueObserver observer;
+  runners_[0]->SetObserver(&observer);
 
   // We should get a notification when a delayed task is posted on an empty
   // queue.
-  EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(runners_[0].get()));
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(), _));
   runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask),
                                base::TimeDelta::FromSeconds(10));
   Mock::VerifyAndClearExpectations(&observer);
 
   // We should not get a notification for a longer delay.
-  EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(_)).Times(0);
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
   runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask),
                                base::TimeDelta::FromSeconds(100));
   Mock::VerifyAndClearExpectations(&observer);
 
   // We should get a notification for a shorter delay.
-  EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(runners_[0].get()));
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(), _));
   runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask),
                                base::TimeDelta::FromSeconds(1));
   Mock::VerifyAndClearExpectations(&observer);
@@ -1762,26 +1758,24 @@
 
   // When a queue has been enabled, we may get a notification if the
   // TimeDomain's next scheduled wake-up has changed.
-  EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(runners_[0].get()));
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(), _));
   voter->SetQueueEnabled(true);
 
   // Tidy up.
   runners_[0]->UnregisterTaskQueue();
-  manager_->UnregisterTimeDomain(domain.get());
 }
 
-TEST_F(TaskQueueManagerTest, TimeDomainObserver_DelayedTaskMultipleQueues) {
+TEST_F(TaskQueueManagerTest, TaskQueueObserver_DelayedTaskMultipleQueues) {
   Initialize(2u);
 
-  MockTimeDomainObserver observer;
-  std::unique_ptr<VirtualTimeDomain> domain(
-      new VirtualTimeDomain(&observer, manager_->Delegate()->NowTicks()));
-  manager_->RegisterTimeDomain(domain.get());
-  runners_[0]->SetTimeDomain(domain.get());
-  runners_[1]->SetTimeDomain(domain.get());
+  MockTaskQueueObserver observer;
+  runners_[0]->SetObserver(&observer);
+  runners_[1]->SetObserver(&observer);
 
-  EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(runners_[0].get())).Times(1);
-  EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(runners_[1].get())).Times(1);
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(), _))
+      .Times(1);
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[1].get(), _))
+      .Times(1);
   runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask),
                                base::TimeDelta::FromSeconds(1));
   runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask),
@@ -1794,30 +1788,29 @@
       runners_[1]->CreateQueueEnabledVoter();
 
   // Disabling a queue should not trigger a notification.
-  EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(_)).Times(0);
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
   voter0->SetQueueEnabled(false);
   Mock::VerifyAndClearExpectations(&observer);
 
   // Re-enabling it should should also trigger a notification.
-  EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(runners_[0].get()));
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(), _));
   voter0->SetQueueEnabled(true);
   Mock::VerifyAndClearExpectations(&observer);
 
   // Disabling a queue should not trigger a notification.
-  EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(_)).Times(0);
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
   voter1->SetQueueEnabled(false);
   Mock::VerifyAndClearExpectations(&observer);
 
   // Re-enabling it should should trigger a notification.
-  EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(runners_[1].get()));
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[1].get(), _));
   voter1->SetQueueEnabled(true);
   Mock::VerifyAndClearExpectations(&observer);
 
   // Tidy up.
-  EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(_)).Times(AnyNumber());
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(AnyNumber());
   runners_[0]->UnregisterTaskQueue();
   runners_[1]->UnregisterTaskQueue();
-  manager_->UnregisterTimeDomain(domain.get());
 }
 
 namespace {
@@ -2860,6 +2853,9 @@
 TEST_F(TaskQueueManagerTest, SetTimeDomainForDisabledQueue) {
   Initialize(1u);
 
+  MockTaskQueueObserver observer;
+  runners_[0]->SetObserver(&observer);
+
   runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask),
                                base::TimeDelta::FromMilliseconds(1));
 
@@ -2867,12 +2863,11 @@
       runners_[0]->CreateQueueEnabledVoter();
   voter->SetQueueEnabled(false);
 
-  MockTimeDomainObserver observer;
   // We should not get a notification for a disabled queue.
-  EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(_)).Times(0);
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
 
   std::unique_ptr<VirtualTimeDomain> domain(
-      new VirtualTimeDomain(&observer, manager_->Delegate()->NowTicks()));
+      new VirtualTimeDomain(manager_->Delegate()->NowTicks()));
   manager_->RegisterTimeDomain(domain.get());
   runners_[0]->SetTimeDomain(domain.get());
 
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_selector_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_selector_unittest.cc
index 9d5a353..08c5b0d 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_selector_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_selector_unittest.cc
@@ -120,7 +120,7 @@
  protected:
   void SetUp() final {
     virtual_time_domain_ = base::WrapUnique<VirtualTimeDomain>(
-        new VirtualTimeDomain(nullptr, base::TimeTicks()));
+        new VirtualTimeDomain(base::TimeTicks()));
     for (size_t i = 0; i < kTaskQueueCount; i++) {
       scoped_refptr<TaskQueueImpl> task_queue =
           make_scoped_refptr(new TaskQueueImpl(
diff --git a/third_party/WebKit/Source/platform/scheduler/base/time_domain.cc b/third_party/WebKit/Source/platform/scheduler/base/time_domain.cc
index d581f93..bdf885b 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/time_domain.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/time_domain.cc
@@ -13,7 +13,7 @@
 namespace blink {
 namespace scheduler {
 
-TimeDomain::TimeDomain(Observer* observer) : observer_(observer) {}
+TimeDomain::TimeDomain() {}
 
 TimeDomain::~TimeDomain() {
   DCHECK(main_thread_checker_.CalledOnValidThread());
@@ -55,14 +55,6 @@
   // If |queue| is the first wake-up then request the wake-up.
   if (delayed_wake_up_queue_.Min().queue == queue)
     RequestWakeUpAt(now, wake_up.time);
-
-  if (observer_)
-    observer_->OnTimeDomainHasDelayedWork(queue);
-}
-
-void TimeDomain::OnQueueHasImmediateWork(internal::TaskQueueImpl* queue) {
-  if (observer_)
-    observer_->OnTimeDomainHasImmediateWork(queue);
 }
 
 void TimeDomain::CancelDelayedWork(internal::TaskQueueImpl* queue) {
diff --git a/third_party/WebKit/Source/platform/scheduler/base/time_domain.h b/third_party/WebKit/Source/platform/scheduler/base/time_domain.h
index 6179d01..82db58b 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/time_domain.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/time_domain.h
@@ -36,22 +36,7 @@
 // changes.
 class BLINK_PLATFORM_EXPORT TimeDomain {
  public:
-  class BLINK_PLATFORM_EXPORT Observer {
-   public:
-    virtual ~Observer() {}
-
-    // Called when an empty TaskQueue registered with this TimeDomain has a task
-    // enqueued.
-    // |task_queue| - task queue which has immediate work scheduled.
-    virtual void OnTimeDomainHasImmediateWork(TaskQueue* task_queue) = 0;
-
-    // Called when a TaskQueue registered with this TimeDomain has a delayed
-    // task enqueued.
-    // |task_queue| - task queue which has delayed work scheduled.
-    virtual void OnTimeDomainHasDelayedWork(TaskQueue* task_queue) = 0;
-  };
-
-  explicit TimeDomain(Observer* observer);
+  TimeDomain();
   virtual ~TimeDomain();
 
   // Returns a LazyNow that evaluate this TimeDomain's Now.  Can be called from
@@ -86,10 +71,6 @@
   // the next task was posted to and it returns true.  Returns false otherwise.
   bool NextScheduledTaskQueue(TaskQueue** out_task_queue) const;
 
-  // Notifies the time domain observer (if any) that |queue| has incoming
-  // immediate work.
-  void OnQueueHasImmediateWork(internal::TaskQueueImpl* queue);
-
   // Schedules a call to TaskQueueImpl::WakeUpForDelayedWork when this
   // TimeDomain reaches |delayed_run_time|.  This supersedes any previously
   // registered wake-up for |queue|.
@@ -161,8 +142,6 @@
 
   IntrusiveHeap<ScheduledDelayedWakeUp> delayed_wake_up_queue_;
 
-  Observer* const observer_;  // NOT OWNED.
-
   base::ThreadChecker main_thread_checker_;
 
   DISALLOW_COPY_AND_ASSIGN(TimeDomain);
diff --git a/third_party/WebKit/Source/platform/scheduler/base/time_domain_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/time_domain_unittest.cc
index bc62249b..0e6fc28 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/time_domain_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/time_domain_unittest.cc
@@ -24,16 +24,14 @@
 
 class MockTimeDomain : public TimeDomain {
  public:
-  explicit MockTimeDomain(TimeDomain::Observer* observer)
-      : TimeDomain(observer),
-        now_(base::TimeTicks() + base::TimeDelta::FromSeconds(1)) {}
+  MockTimeDomain()
+      : now_(base::TimeTicks() + base::TimeDelta::FromSeconds(1)) {}
 
   ~MockTimeDomain() override {}
 
   using TimeDomain::CancelDelayedWork;
   using TimeDomain::NextScheduledRunTime;
   using TimeDomain::NextScheduledTaskQueue;
-  using TimeDomain::OnQueueHasImmediateWork;
   using TimeDomain::ScheduleDelayedWork;
   using TimeDomain::UnregisterQueue;
   using TimeDomain::WakeUpReadyDelayedQueues;
@@ -82,7 +80,7 @@
   }
 
   virtual MockTimeDomain* CreateMockTimeDomain() {
-    return new MockTimeDomain(nullptr);
+    return new MockTimeDomain();
   }
 
   std::unique_ptr<MockTimeDomain> time_domain_;
@@ -124,7 +122,7 @@
 
   Mock::VerifyAndClearExpectations(time_domain_.get());
 
-  // Now scheduler a later wake-up, which should replace the previously
+  // Now schedule a later wake_up, which should replace the previously
   // requested one.
   EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, delayed_runtime2));
   time_domain_->ScheduleDelayedWork(task_queue_.get(), {delayed_runtime2, 0},
@@ -330,38 +328,5 @@
   task_queue2->UnregisterTaskQueue();
 }
 
-namespace {
-class MockObserver : public TimeDomain::Observer {
- public:
-  ~MockObserver() override {}
-
-  MOCK_METHOD1(OnTimeDomainHasImmediateWork, void(TaskQueue*));
-  MOCK_METHOD1(OnTimeDomainHasDelayedWork, void(TaskQueue*));
-};
-}  // namespace
-
-class TimeDomainWithObserverTest : public TimeDomainTest {
- public:
-  MockTimeDomain* CreateMockTimeDomain() override {
-    observer_.reset(new MockObserver());
-    return new MockTimeDomain(observer_.get());
-  }
-
-  std::unique_ptr<MockObserver> observer_;
-};
-
-TEST_F(TimeDomainWithObserverTest, OnTimeDomainHasImmediateWork) {
-  EXPECT_CALL(*observer_, OnTimeDomainHasImmediateWork(task_queue_.get()));
-  time_domain_->OnQueueHasImmediateWork(task_queue_.get());
-}
-
-TEST_F(TimeDomainWithObserverTest, OnTimeDomainHasDelayedWork) {
-  EXPECT_CALL(*observer_, OnTimeDomainHasDelayedWork(task_queue_.get()));
-  EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, _));
-  base::TimeTicks now = time_domain_->Now();
-  time_domain_->ScheduleDelayedWork(
-      task_queue_.get(), {now + base::TimeDelta::FromMilliseconds(10), 0}, now);
-}
-
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.cc b/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.cc
index 2da1c7c..6e3b285 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.cc
@@ -12,9 +12,8 @@
 namespace blink {
 namespace scheduler {
 
-VirtualTimeDomain::VirtualTimeDomain(TimeDomain::Observer* observer,
-                                     base::TimeTicks initial_time)
-    : TimeDomain(observer), now_(initial_time), task_queue_manager_(nullptr) {}
+VirtualTimeDomain::VirtualTimeDomain(base::TimeTicks initial_time)
+    : now_(initial_time), task_queue_manager_(nullptr) {}
 
 VirtualTimeDomain::~VirtualTimeDomain() {}
 
diff --git a/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.h b/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.h
index 67aaa4fb..050c0ba 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.h
@@ -14,8 +14,7 @@
 
 class BLINK_PLATFORM_EXPORT VirtualTimeDomain : public TimeDomain {
  public:
-  VirtualTimeDomain(TimeDomain::Observer* observer,
-                    base::TimeTicks initial_time);
+  VirtualTimeDomain(base::TimeTicks initial_time);
   ~VirtualTimeDomain() override;
 
   // TimeDomain implementation:
diff --git a/third_party/WebKit/Source/platform/scheduler/child/compositor_worker_scheduler.cc b/third_party/WebKit/Source/platform/scheduler/child/compositor_worker_scheduler.cc
index a86fafd6..7cf0752 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/compositor_worker_scheduler.cc
+++ b/third_party/WebKit/Source/platform/scheduler/child/compositor_worker_scheduler.cc
@@ -117,6 +117,8 @@
     NOTREACHED();
   }
 
+  void SetObserver(Observer* observer) override { NOTREACHED(); }
+
  private:
   ~CompositorWorkerTaskRunnerWrapper() override {}
 
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc b/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc
index 4da9eb5..fcd38cd 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc
@@ -9,8 +9,7 @@
 
 AutoAdvancingVirtualTimeDomain::AutoAdvancingVirtualTimeDomain(
     base::TimeTicks initial_time)
-    : VirtualTimeDomain(nullptr, initial_time),
-      can_advance_virtual_time_(true) {}
+    : VirtualTimeDomain(initial_time), can_advance_virtual_time_(true) {}
 
 AutoAdvancingVirtualTimeDomain::~AutoAdvancingVirtualTimeDomain() {}
 
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.cc b/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.cc
index 289893c..e9744e2 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.cc
@@ -79,13 +79,13 @@
       renderer_scheduler_(renderer_scheduler),
       tick_clock_(renderer_scheduler->tick_clock()),
       tracing_category_(tracing_category),
-      time_domain_(new ThrottledTimeDomain(this, tracing_category)),
+      time_domain_(new ThrottledTimeDomain(tracing_category)),
       allow_throttling_(true),
       weak_factory_(this) {
   pump_throttled_tasks_closure_.Reset(base::Bind(
       &TaskQueueThrottler::PumpThrottledTasks, weak_factory_.GetWeakPtr()));
   forward_immediate_work_callback_ =
-      base::Bind(&TaskQueueThrottler::OnTimeDomainHasImmediateWork,
+      base::Bind(&TaskQueueThrottler::OnQueueNextWakeUpChanged,
                  weak_factory_.GetWeakPtr());
 
   renderer_scheduler_->RegisterTimeDomain(time_domain_.get());
@@ -100,6 +100,8 @@
       task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain());
       task_queue->RemoveFence();
     }
+    if (map_entry.second.throttling_ref_count != 0)
+      task_queue->SetObserver(nullptr);
   }
 
   renderer_scheduler_->UnregisterTimeDomain(time_domain_.get());
@@ -119,6 +121,8 @@
   TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueThrottled",
                "task_queue", task_queue);
 
+  task_queue->SetObserver(this);
+
   if (!allow_throttling_)
     return;
 
@@ -131,11 +135,9 @@
     return;
 
   if (!task_queue->IsEmpty()) {
-    if (task_queue->HasPendingImmediateWork()) {
-      OnTimeDomainHasImmediateWork(task_queue);
-    } else {
-      OnTimeDomainHasDelayedWork(task_queue);
-    }
+    LazyNow lazy_now(tick_clock_);
+    OnQueueNextWakeUpChanged(task_queue,
+                             NextTaskRunTime(&lazy_now, task_queue).value());
   }
 }
 
@@ -150,6 +152,8 @@
   TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueUnthrottled",
                "task_queue", task_queue);
 
+  task_queue->SetObserver(nullptr);
+
   MaybeDeleteQueueMetadata(iter);
 
   if (!allow_throttling_)
@@ -183,17 +187,22 @@
   // Iterator may have been deleted by BudgetPool::RemoveQueue, so don't
   // use it here.
   queue_details_.erase(task_queue);
+
+  // NOTE: Observer is automatically unregistered when unregistering task queue.
 }
 
-void TaskQueueThrottler::OnTimeDomainHasImmediateWork(TaskQueue* queue) {
-  // Forward to the main thread if called from another thread
+void TaskQueueThrottler::OnQueueNextWakeUpChanged(
+    TaskQueue* queue,
+    base::TimeTicks next_wake_up) {
   if (!task_runner_->RunsTasksOnCurrentThread()) {
-    task_runner_->PostTask(FROM_HERE,
-                           base::Bind(forward_immediate_work_callback_, queue));
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(forward_immediate_work_callback_, queue, next_wake_up));
     return;
   }
+
   TRACE_EVENT0(tracing_category_,
-               "TaskQueueThrottler::OnTimeDomainHasImmediateWork");
+               "TaskQueueThrottler::OnQueueNextWakeUpChanged");
 
   // We don't expect this to get called for disabled queues, but we can't DCHECK
   // because of the above thread hop.  Just bail out if the queue is disabled.
@@ -201,22 +210,9 @@
     return;
 
   base::TimeTicks now = tick_clock_->NowTicks();
-  base::TimeTicks next_allowed_run_time = GetNextAllowedRunTime(now, queue);
-  MaybeSchedulePumpThrottledTasks(FROM_HERE, now, next_allowed_run_time);
-}
-
-void TaskQueueThrottler::OnTimeDomainHasDelayedWork(TaskQueue* queue) {
-  TRACE_EVENT0(tracing_category_,
-               "TaskQueueThrottler::OnTimeDomainHasDelayedWork");
-  DCHECK(queue->IsQueueEnabled());
-  base::TimeTicks now = tick_clock_->NowTicks();
-  LazyNow lazy_now(now);
-
-  base::Optional<base::TimeTicks> next_scheduled_delayed_task =
-      NextTaskRunTime(&lazy_now, queue);
-  DCHECK(next_scheduled_delayed_task);
-  MaybeSchedulePumpThrottledTasks(FROM_HERE, now,
-                                  next_scheduled_delayed_task.value());
+  MaybeSchedulePumpThrottledTasks(
+      FROM_HERE, now,
+      std::max(GetNextAllowedRunTime(now, queue), next_wake_up));
 }
 
 void TaskQueueThrottler::PumpThrottledTasks() {
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.h b/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.h
index 2c63547..9a8dd3f 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.h
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.h
@@ -80,7 +80,7 @@
 // See IncreaseThrottleRefCount & DecreaseThrottleRefCount.
 //
 // This class is main-thread only.
-class BLINK_PLATFORM_EXPORT TaskQueueThrottler : public TimeDomain::Observer,
+class BLINK_PLATFORM_EXPORT TaskQueueThrottler : public TaskQueue::Observer,
                                                  public BudgetPoolController {
  public:
   // TODO(altimin): Do not pass tracing category as const char*,
@@ -90,9 +90,9 @@
 
   ~TaskQueueThrottler() override;
 
-  // TimeDomain::Observer implementation:
-  void OnTimeDomainHasImmediateWork(TaskQueue*) override;
-  void OnTimeDomainHasDelayedWork(TaskQueue*) override;
+  // TaskQueue::Observer implementation:
+  void OnQueueNextWakeUpChanged(TaskQueue* queue,
+                                base::TimeTicks wake_up) override;
 
   // BudgetPoolController implementation:
   void AddQueueToBudgetPool(TaskQueue* queue, BudgetPool* budget_pool) override;
@@ -172,7 +172,8 @@
                          TaskQueue* queue);
 
   TaskQueueMap queue_details_;
-  base::Callback<void(TaskQueue*)> forward_immediate_work_callback_;
+  base::Callback<void(TaskQueue*, base::TimeTicks)>
+      forward_immediate_work_callback_;
   scoped_refptr<TaskQueue> task_runner_;
   RendererSchedulerImpl* renderer_scheduler_;  // NOT OWNED
   base::TickClock* tick_clock_;                // NOT OWNED
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler_unittest.cc
index 3f75e5c..277ff6ab 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler_unittest.cc
@@ -275,7 +275,8 @@
 }
 
 TEST_F(TaskQueueThrottlerTest, OnTimeDomainHasImmediateWork_EnabledQueue) {
-  task_queue_throttler_->OnTimeDomainHasImmediateWork(timer_queue_.get());
+  task_queue_throttler_->OnQueueNextWakeUpChanged(timer_queue_.get(),
+                                                  base::TimeTicks());
   // Check PostPumpThrottledTasksLocked was called.
   EXPECT_FALSE(task_queue_throttler_->task_runner()->IsEmpty());
 }
@@ -285,7 +286,8 @@
       timer_queue_->CreateQueueEnabledVoter();
   voter->SetQueueEnabled(false);
 
-  task_queue_throttler_->OnTimeDomainHasImmediateWork(timer_queue_.get());
+  task_queue_throttler_->OnQueueNextWakeUpChanged(timer_queue_.get(),
+                                                  base::TimeTicks());
   // Check PostPumpThrottledTasksLocked was not called.
   EXPECT_TRUE(task_queue_throttler_->task_runner()->IsEmpty());
 }
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.cc b/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.cc
index e5179dd..9126c6f0 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.cc
@@ -7,9 +7,8 @@
 namespace blink {
 namespace scheduler {
 
-ThrottledTimeDomain::ThrottledTimeDomain(TimeDomain::Observer* observer,
-                                         const char* tracing_category)
-    : RealTimeDomain(observer, tracing_category) {}
+ThrottledTimeDomain::ThrottledTimeDomain(const char* tracing_category)
+    : RealTimeDomain(tracing_category) {}
 
 ThrottledTimeDomain::~ThrottledTimeDomain() {}
 
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.h b/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.h
index 74a654f0..53b4514 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.h
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.h
@@ -15,8 +15,7 @@
 // relies on the owner (TaskQueueThrottler) to schedule wake-ups.
 class BLINK_PLATFORM_EXPORT ThrottledTimeDomain : public RealTimeDomain {
  public:
-  ThrottledTimeDomain(TimeDomain::Observer* observer,
-                      const char* tracing_category);
+  ThrottledTimeDomain(const char* tracing_category);
   ~ThrottledTimeDomain() override;
 
   // TimeDomain implementation:
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py
index cba10f9..fecac2d 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py
@@ -368,7 +368,7 @@
                                       tests_to_skip, num_workers, retry_attempt)
 
     def _start_servers(self, tests_to_run):
-        if any(self._port.is_wptserve_test(test) for test in tests_to_run):
+        if any(self._port.is_wpt_test(test) for test in tests_to_run):
             self._printer.write_update('Starting WPTServe ...')
             self._port.start_wptserve()
             self._wptserve_started = True
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
index 215d589..1a2c3057 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
@@ -1140,12 +1140,12 @@
         self._websocket_server = server
 
     @staticmethod
-    def is_wptserve_test(test):
-        """Whether wptserve should be used for a given test if enabled."""
+    def is_wpt_test(test):
+        """Whether a test is considered a web-platform-tests test."""
         return re.match(r'(virtual/[^/]+/)?external/wpt/', test)
 
     def should_use_wptserve(self, test):
-        return self.is_wptserve_test(test)
+        return self.is_wpt_test(test)
 
     def start_wptserve(self):
         """Starts a WPT web server.
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py
index 84c95d7..3532200 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py
@@ -490,13 +490,13 @@
         port = self.make_port()
         self.assertRaises(AssertionError, port.virtual_test_suites)
 
-    def test_is_wptserve_test(self):
+    def test_is_wpt_test(self):
         port = self.make_port()
-        self.assertTrue(port.is_wptserve_test('external/wpt/foo/bar.html'))
-        self.assertTrue(port.is_wptserve_test('virtual/a-name/external/wpt/baz/qux.htm'))
-        self.assertFalse(port.is_wptserve_test('http/wpt/foo.html'))
-        self.assertFalse(port.is_wptserve_test('virtual/external/wpt/baz/qux.htm'))
-        self.assertFalse(port.is_wptserve_test('not-virtual/a-name/external/wpt/baz/qux.htm'))
+        self.assertTrue(port.is_wpt_test('external/wpt/foo/bar.html'))
+        self.assertTrue(port.is_wpt_test('virtual/a-name/external/wpt/baz/qux.htm'))
+        self.assertFalse(port.is_wpt_test('http/wpt/foo.html'))
+        self.assertFalse(port.is_wpt_test('virtual/external/wpt/baz/qux.htm'))
+        self.assertFalse(port.is_wpt_test('not-virtual/a-name/external/wpt/baz/qux.htm'))
 
     def test_default_results_directory(self):
         port = self.make_port(options=optparse.Values({'target': 'Default', 'configuration': 'Release'}))
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_expectations_updater.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_expectations_updater.py
index 6eaf99ef..86df73e 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_expectations_updater.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_expectations_updater.py
@@ -28,6 +28,7 @@
 
     def __init__(self, host):
         self.host = host
+        self.port = self.host.port_factory.get()
         self.finder = WebKitFinder(self.host.filesystem)
         self.port = self.host.port_factory.get()
 
@@ -255,7 +256,7 @@
         """
         line_list = []
         for test_name, port_results in sorted(merged_results.iteritems()):
-            if not test_name.startswith('external'):
+            if not self.port.is_wpt_test(test_name):
                 continue
             for port_names, results in sorted(port_results.iteritems()):
                 line_list.append(self._create_line(test_name, port_names, results))
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_expectations_updater_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_expectations_updater_unittest.py
index ba7b41d..9b09e6f 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_expectations_updater_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_expectations_updater_unittest.py
@@ -198,24 +198,27 @@
         self.assertEqual(updater.create_line_list(results), [])
 
     def test_create_line_list_new_tests(self):
-        # In this example, there are three unexpected results. The new
-        # test expectation lines are sorted by test, and then specifier.
+        # In this example, there are three unexpected results for wpt tests.
+        # The new test expectation lines are sorted by test, and then specifier.
         updater = WPTExpectationsUpdater(self.mock_host())
         results = {
             'external/wpt/test/zzzz.html': {
                 'test-mac-mac10.10': {'expected': 'PASS', 'actual': 'TEXT', 'bug': 'crbug.com/test'},
             },
-            'external/wpt/test/path.html': {
+            'virtual/foo/external/wpt/test/zzzz.html': {
                 'test-linux-trusty': {'expected': 'FAIL', 'actual': 'PASS', 'bug': 'crbug.com/test'},
                 'test-mac-mac10.11': {'expected': 'FAIL', 'actual': 'TIMEOUT', 'bug': 'crbug.com/test'},
             },
+            'unrelated/test.html': {
+                'test-linux-trusty': {'expected': 'FAIL', 'actual': 'PASS', 'bug': 'crbug.com/test'},
+            },
         }
         self.assertEqual(
             updater.create_line_list(results),
             [
-                'crbug.com/test [ Trusty ] external/wpt/test/path.html [ Pass ]',
-                'crbug.com/test [ Mac10.11 ] external/wpt/test/path.html [ Timeout ]',
                 'crbug.com/test [ Mac10.10 ] external/wpt/test/zzzz.html [ Failure ]',
+                'crbug.com/test [ Trusty ] virtual/foo/external/wpt/test/zzzz.html [ Pass ]',
+                'crbug.com/test [ Mac10.11 ] virtual/foo/external/wpt/test/zzzz.html [ Timeout ]',
             ])
 
     def test_specifier_part(self):
diff --git a/third_party/WebKit/public/platform/scheduler/base/task_queue.h b/third_party/WebKit/public/platform/scheduler/base/task_queue.h
index da4102e4..de227cc 100644
--- a/third_party/WebKit/public/platform/scheduler/base/task_queue.h
+++ b/third_party/WebKit/public/platform/scheduler/base/task_queue.h
@@ -27,6 +27,20 @@
  public:
   TaskQueue() {}
 
+  class BLINK_PLATFORM_EXPORT Observer {
+   public:
+    virtual ~Observer() {}
+
+    // Notify observer that the time at which this queue wants to run
+    // the next task has changed. |next_wakeup| can be in the past
+    // (e.g. base::TimeTicks() can be used to notify about immediate work).
+    // Can be called on any thread
+    // All methods but SetObserver, SetTimeDomain and GetTimeDomain can be
+    // called on |queue|.
+    virtual void OnQueueNextWakeUpChanged(TaskQueue* queue,
+                                          base::TimeTicks next_wake_up) = 0;
+  };
+
   // Unregisters the task queue after which no tasks posted to it will run and
   // the TaskQueueManager's reference to it will be released soon.
   virtual void UnregisterTaskQueue() = 0;
@@ -208,6 +222,8 @@
 
   virtual bool BlockedByFence() const = 0;
 
+  virtual void SetObserver(Observer* observer) = 0;
+
  protected:
   ~TaskQueue() override {}
 
diff --git a/third_party/espresso/BUILD.gn b/third_party/espresso/BUILD.gn
index 7387e593..105ef88 100644
--- a/third_party/espresso/BUILD.gn
+++ b/third_party/espresso/BUILD.gn
@@ -17,7 +17,7 @@
 
 android_java_prebuilt("espresso_contrib_java") {
   testonly = true
-  jar_path = "lib/espresso-contrib-2.2-release-no-dep.jar"
+  jar_path = "lib/espresso-contrib-2.2.1-release-no-dep.jar"
   deps = [
     ":espresso_core_java",
     "//third_party/guava:guava_java",
@@ -27,7 +27,7 @@
 
 android_java_prebuilt("espresso_core_java") {
   testonly = true
-  jar_path = "lib/espresso-core-2.2-release-no-dep.jar"
+  jar_path = "lib/espresso-core-2.2.1-release-no-dep.jar"
   deps = [
     "//third_party/android_tools:android_support_annotations_java",
     "//third_party/guava:guava_java",
@@ -38,12 +38,12 @@
 
 android_java_prebuilt("espresso_idling_java") {
   testonly = true
-  jar_path = "lib/espresso-idling-resource-2.2-release-no-dep.jar"
+  jar_path = "lib/espresso-idling-resource-2.2.1-release-no-dep.jar"
 }
 
 android_java_prebuilt("espresso_intents_java") {
   testonly = true
-  jar_path = "lib/espresso-intents-2.2-release-no-dep.jar"
+  jar_path = "lib/espresso-intents-2.2.1-release-no-dep.jar"
   deps = [
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/guava:guava_java",
@@ -53,7 +53,7 @@
 
 android_java_prebuilt("espresso_web_java") {
   testonly = true
-  jar_path = "lib/espresso-web-2.2-release-no-dep.jar"
+  jar_path = "lib/espresso-web-2.2.1-release-no-dep.jar"
   deps = [
     ":espresso_core_java",
     "//third_party/guava:guava_java",
diff --git a/third_party/espresso/README.chromium b/third_party/espresso/README.chromium
index 98accad..0bf0132 100644
--- a/third_party/espresso/README.chromium
+++ b/third_party/espresso/README.chromium
@@ -12,4 +12,9 @@
 IMPORTANT: There should only be one version of espresso library
 (crbug.com/622057)
 
-Local Modifications: None
+Local Modifications:
+- Version 2.2.1:
+  - Change Futures.transform(preparedScript, RAW_EVALUATOR) calls to use Futures.transformAsync
+    in android/support/test/espresso/web/action/JavascriptEvaluation.java for Guava 20.0
+    compatibility
+  - Change all Guava dependencies in Espresso build.gradle files from 18.0 to 20.0
diff --git a/third_party/espresso/lib/espresso-contrib-2.2-release-no-dep.jar.sha1 b/third_party/espresso/lib/espresso-contrib-2.2-release-no-dep.jar.sha1
deleted file mode 100644
index 9bafe15..0000000
--- a/third_party/espresso/lib/espresso-contrib-2.2-release-no-dep.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-6a6d035553e64ef79f7e960faeaefb64b2020461
\ No newline at end of file
diff --git a/third_party/espresso/lib/espresso-contrib-2.2.1-release-no-dep.jar.sha1 b/third_party/espresso/lib/espresso-contrib-2.2.1-release-no-dep.jar.sha1
new file mode 100644
index 0000000..1f523df
--- /dev/null
+++ b/third_party/espresso/lib/espresso-contrib-2.2.1-release-no-dep.jar.sha1
@@ -0,0 +1 @@
+72cf04ecaab0bb92a6a18b0264e0684aa3d1881b
\ No newline at end of file
diff --git a/third_party/espresso/lib/espresso-core-2.2-release-no-dep.jar.sha1 b/third_party/espresso/lib/espresso-core-2.2-release-no-dep.jar.sha1
deleted file mode 100644
index dcaec9e0..0000000
--- a/third_party/espresso/lib/espresso-core-2.2-release-no-dep.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-ad9632f6066aba08fa7906e8ba5997aa9b5d3e50
\ No newline at end of file
diff --git a/third_party/espresso/lib/espresso-core-2.2.1-release-no-dep.jar.sha1 b/third_party/espresso/lib/espresso-core-2.2.1-release-no-dep.jar.sha1
new file mode 100644
index 0000000..201772ba
--- /dev/null
+++ b/third_party/espresso/lib/espresso-core-2.2.1-release-no-dep.jar.sha1
@@ -0,0 +1 @@
+06ea68f7818866197247aebb1aa87db2101678ed
\ No newline at end of file
diff --git a/third_party/espresso/lib/espresso-idling-resource-2.2-release-no-dep.jar.sha1 b/third_party/espresso/lib/espresso-idling-resource-2.2-release-no-dep.jar.sha1
deleted file mode 100644
index 2bc9dbf..0000000
--- a/third_party/espresso/lib/espresso-idling-resource-2.2-release-no-dep.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-1e97deb6027e673ab85fb4953c72676a7a2eb958
\ No newline at end of file
diff --git a/third_party/espresso/lib/espresso-idling-resource-2.2.1-release-no-dep.jar.sha1 b/third_party/espresso/lib/espresso-idling-resource-2.2.1-release-no-dep.jar.sha1
new file mode 100644
index 0000000..c78578d
--- /dev/null
+++ b/third_party/espresso/lib/espresso-idling-resource-2.2.1-release-no-dep.jar.sha1
@@ -0,0 +1 @@
+600daa034b62e193487208e28bde17cd3de06525
\ No newline at end of file
diff --git a/third_party/espresso/lib/espresso-intents-2.2-release-no-dep.jar.sha1 b/third_party/espresso/lib/espresso-intents-2.2-release-no-dep.jar.sha1
deleted file mode 100644
index bc8df45..0000000
--- a/third_party/espresso/lib/espresso-intents-2.2-release-no-dep.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-d022911fd525712d9f2c17a91ac325b245a30557
\ No newline at end of file
diff --git a/third_party/espresso/lib/espresso-intents-2.2.1-release-no-dep.jar.sha1 b/third_party/espresso/lib/espresso-intents-2.2.1-release-no-dep.jar.sha1
new file mode 100644
index 0000000..1cff499
--- /dev/null
+++ b/third_party/espresso/lib/espresso-intents-2.2.1-release-no-dep.jar.sha1
@@ -0,0 +1 @@
+24a6808324bba17ae852fe68dea927b0219da19e
\ No newline at end of file
diff --git a/third_party/espresso/lib/espresso-web-2.2-release-no-dep.jar.sha1 b/third_party/espresso/lib/espresso-web-2.2-release-no-dep.jar.sha1
deleted file mode 100644
index 18324b89..0000000
--- a/third_party/espresso/lib/espresso-web-2.2-release-no-dep.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-83ac4b2b331ece89add05dfac0c362f9a0d768f8
\ No newline at end of file
diff --git a/third_party/espresso/lib/espresso-web-2.2.1-release-no-dep.jar.sha1 b/third_party/espresso/lib/espresso-web-2.2.1-release-no-dep.jar.sha1
new file mode 100644
index 0000000..2b9fddc7
--- /dev/null
+++ b/third_party/espresso/lib/espresso-web-2.2.1-release-no-dep.jar.sha1
@@ -0,0 +1 @@
+b4c1ae773ef917a9421a3d616dbd632478aa63b8
\ No newline at end of file
diff --git a/tools/binary_size/OWNERS b/tools/binary_size/OWNERS
index 5ffdf6a..22475b2b 100644
--- a/tools/binary_size/OWNERS
+++ b/tools/binary_size/OWNERS
@@ -1,5 +1,4 @@
 agrieve@chromium.org
-bratell@opera.com
-primiano@chromium.org
+estevenson@chromium.org
 
 # COMPONENT: Tools
diff --git a/tools/binary_size/PRESUBMIT.py b/tools/binary_size/PRESUBMIT.py
index 5177786..69eafbe 100644
--- a/tools/binary_size/PRESUBMIT.py
+++ b/tools/binary_size/PRESUBMIT.py
@@ -11,11 +11,11 @@
 def CommonChecks(input_api, output_api):
   output = []
   output.extend(input_api.canned_checks.RunPylint(input_api, output_api))
-  output.extend(
-      input_api.canned_checks.RunUnitTestsInDirectory(
-          input_api, output_api,
-          input_api.PresubmitLocalPath(),
-          whitelist=[r'.+_test\.py$']))
+  py_tests = input_api.canned_checks.GetUnitTestsRecursively(
+      input_api, output_api, input_api.PresubmitLocalPath(),
+      whitelist=[r'.+_test\.py$'], blacklist=[])
+
+  output.extend(input_api.RunTests(py_tests, False))
 
   if input_api.is_committing:
     output.extend(input_api.canned_checks.PanProjectChecks(input_api,
diff --git a/tools/binary_size/README.md b/tools/binary_size/README.md
index 54ddf40..4bc66ea 100644
--- a/tools/binary_size/README.md
+++ b/tools/binary_size/README.md
@@ -1,44 +1,52 @@
-# map2size.py
+# Tools for analyzing Chrome's binary size
 
-Parses a linker .map file and outputs the result as a .size file.
+# Super Size
 
-## Example Usage:
+Collect, archive, and analyze Chrome's binary size.
+
+## "archive"
+
+Collect size information and dump it into a `.size` file. Mainly consists of
+symbol information parsed from a linker .map file.
+
+### Example Usage:
 
     # Android:
     gn gen out/Release --args='target_os="android" is_official_build=true'
     ninja -C out/Release -j 1000 libchrome.so
-    tools/binary_size/map2size.py out/Release/lib.unstripped/libchrome.so chrome.size -v
+    tools/binary_size/supersize archive chrome.size --elf-file out/Release/lib.unstripped/libchrome.so -v
     # Linux:
     gn gen out/Release --args='is_official_build=true'
     ninja -C out/Release -j 1000 chrome
-    tools/binary_size/map2size.py out/Release/chrome chrome.size -v
+    tools/binary_size/supersize archive chrome.size --elf-file out/Release/chrome -v
 
-# create_html_breakdown.py
+## "html_report"
 
-Creates an interactive size breakdown as a stand-alone html report.
+Creates an interactive size breakdown (by source path) as a stand-alone html
+report.
 
 ## Example Usage:
 
-    tools/binary_size/create_html_breakdown.py chrome.size --report-dir size-report -v
+    tools/binary_size/supersize html_report chrome.size --report-dir size-report -v
     xdg-open size-report/index.html
 
-# console.py
+## "console"
 
 Starts a Python interpreter where you can run custom queries.
 
 ## Example Usage:
 
     # Runs a single diff and exits (does not enter interactive mode).
-    tools/binary_size/console.py without_patch.size with_patch.size --query='Diff(size_info2, size_info1)'
+    tools/binary_size/supersize console without_patch.size with_patch.size --query='Diff(size_info2, size_info1)'
 
     # Enters a Python REPL (it will print more guidance).
-    tools/binary_size/console.py chrome.size
+    tools/binary_size/supersize console chrome.size
 
-# diagnose_apk_bloat.py
+## diagnose_apk_bloat.py
 
 Determine the cause of binary size bloat for a patch.
 
-## Example Usage:
+### Example Usage:
 
     # Sync, build, and store MonochromePublic.apk for HEAD and HEAD^.
     tools/binary_size/diagnose_apk_bloat.py -v
@@ -46,28 +54,39 @@
     # Display detailed usage info (there are many options).
     tools/binary_size/diagnose_apk_bloat.py -h
 
-# Roadmap:
+# Roadmap for Super Size:
 
-  Tracked in https://crbug.com/681694
+Tracked in https://crbug.com/681694
 
-  1. More console.py features:
-      * Template Symbols - shows when templates lead to code bloat.
-      * Duplicate Symbols - shows when statics in headers are an issue.
-      * Overloaded Symbols - shows when overloads are excessive.
-      * Per-class / namespace size (no way to distinguish class vs namespace).
-      * Per-Chrome package (Chrome-specific grouping. e.g. name prefixes).
-      * CSV output (for pasting into a spreadsheet).
-  1. More create_html_breakdown.py features:
-      * Break down by other groupings (e.g. create from nested `SymbolGroups`)
-  1. More `map2size.py` features:
-      * Find out more about 0xffffffffffffffff addresses, and why such large
-        gaps exist after them.
-      * Use nm to get the full list of symbols that share the same address.
-  1. More diagnose_apk_bloat.py features:
-      * Add diffing functionality to see diff stats for two commits.
-      * Add --cloud option for using artifacts from perf builders.
-  1. Integrate with `resource_sizes.py` so that it tracks size of major
-     components separately: chrome vs blink vs skia vs v8.
-  1. Speed up some steps (like normalizing names) via multiprocessing.
-  1. Use resource whitelist information to attribute .pak file size to .o files.
-  1. Add dependency graph info, perhaps just on a per-file basis.
+1. More `archive` features:
+
+  * Find out more about 0xffffffffffffffff addresses, and why such large
+    gaps exist after them.
+  * Use nm to get the full list of symbols that share the same address.
+  * Collect java symbol information
+  * Collect .pak file information (using .o.whitelist files)
+  * Collect .apk entry information
+
+1. More `console` features:
+
+  * Template Symbols - shows when templates lead to code bloat.
+  * Duplicate Symbols - shows when statics in headers are an issue.
+  * Overloaded Symbols - shows when overloads are excessive.
+  * Per-class / namespace size (no way to distinguish class vs namespace).
+  * Per-Chrome package (Chrome-specific grouping. e.g. name prefixes).
+  * CSV output (for pasting into a spreadsheet).
+
+1. More `html_report` features:
+
+  * Break down by other groupings (e.g. create from nested `SymbolGroups`)
+
+1. Integrate with `resource_sizes.py` so that it tracks size of major
+   components separately: chrome vs blink vs skia vs v8.
+1. Speed up some steps (like normalizing names) via multiprocessing.
+1. Add dependency graph info, perhaps just on a per-file basis.
+
+# Roadmap for diagnose_apk_bloat.py:
+1. More `diagnose_apk_bloat.py` features:
+
+  * Add diffing functionality to see diff stats for two commits.
+  * Add --cloud option for using artifacts from perf builders.
diff --git a/tools/binary_size/helpers.py b/tools/binary_size/helpers.py
deleted file mode 100644
index c82a6ba..0000000
--- a/tools/binary_size/helpers.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Utility methods."""
-
-import atexit
-import distutils.spawn
-import logging
-import os
-import platform
-import resource
-import sys
-
-
-SRC_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
-
-
-def AddCommonOptionsAndParseArgs(parser, argv, pypy_warn=True):
-  parser.add_argument('--no-pypy', action='store_true',
-                      help='Do not automatically switch to pypy when available')
-  parser.add_argument('-v',
-                      '--verbose',
-                      default=0,
-                      action='count',
-                      help='Verbose level (multiple times for more)')
-  args = parser.parse_args(argv[1:])
-
-  logging.basicConfig(level=logging.WARNING - args.verbose * 10,
-                      format='%(levelname).1s %(relativeCreated)6d %(message)s')
-
-  if not args.no_pypy and platform.python_implementation() == 'CPython':
-    # Switch to pypy if it's available.
-    pypy_path = distutils.spawn.find_executable('pypy')
-    if pypy_path:
-      logging.debug('Switching to pypy.')
-      os.execv(pypy_path, [pypy_path] + sys.argv)
-    # NOTE! Running with python: 6s. Running with pypy: 3s
-    if pypy_warn:
-      logging.warning(
-          'This script runs more than 2x faster if you install pypy.')
-
-  if logging.getLogger().isEnabledFor(logging.DEBUG):
-    atexit.register(_LogPeakRamUsage)
-  return args
-
-
-def _LogPeakRamUsage():
-  peak_ram_usage = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
-  peak_ram_usage += resource.getrusage(resource.RUSAGE_CHILDREN).ru_maxrss
-  logging.info('Peak RAM usage was %d MB.', peak_ram_usage / 1024)
diff --git a/tools/binary_size/map2size.py b/tools/binary_size/libsupersize/archive.py
old mode 100755
new mode 100644
similarity index 96%
rename from tools/binary_size/map2size.py
rename to tools/binary_size/libsupersize/archive.py
index 95310c3..7524094
--- a/tools/binary_size/map2size.py
+++ b/tools/binary_size/libsupersize/archive.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 # 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.
@@ -400,24 +399,27 @@
   return ["%s=%s" % x for x in sorted(args.iteritems())]
 
 
-def main(argv):
-  parser = argparse.ArgumentParser(argv)
+def AddArguments(parser):
+  parser.add_argument('size_file', help='Path to output .size file.')
   parser.add_argument('--elf-file', required=True,
                       help='Path to input ELF file. Currently used for '
-                           'capturing metadata. Pass "" to skip metadata '
-                           'collection.')
+                           'capturing metadata. Pass "" to skip '
+                           'metadata collection.')
   parser.add_argument('--map-file',
                       help='Path to input .map(.gz) file. Defaults to '
                            '{{elf_file}}.map(.gz)?')
-  parser.add_argument('--output-file', required=True,
-                      help='Path to output .size file.')
   parser.add_argument('--no-source-paths', action='store_true',
                       help='Do not use .ninja files to map '
                            'object_path -> source_path')
-  paths.AddOptions(parser)
-  args = helpers.AddCommonOptionsAndParseArgs(parser, argv)
-  if not args.output_file.endswith('.size'):
-    parser.error('output_file must end with .size')
+  parser.add_argument('--tool-prefix', default='',
+                      help='Path prefix for c++filt.')
+  parser.add_argument('--output-directory',
+                      help='Path to the root build directory.')
+
+
+def Run(args, parser):
+  if not args.size_file.endswith('.size'):
+    parser.error('size_file must end with .size')
 
   if args.map_file:
     if (not args.map_file.endswith('.map')
@@ -469,10 +471,6 @@
 
   logging.info('Recording metadata: \n  %s',
                '\n  '.join(describe.DescribeMetadata(size_info.metadata)))
-  logging.info('Saving result to %s', args.output_file)
-  file_format.SaveSizeInfo(size_info, args.output_file)
+  logging.info('Saving result to %s', args.size_file)
+  file_format.SaveSizeInfo(size_info, args.size_file)
   logging.info('Done')
-
-
-if __name__ == '__main__':
-  sys.exit(main(sys.argv))
diff --git a/tools/binary_size/console.py b/tools/binary_size/libsupersize/console.py
old mode 100755
new mode 100644
similarity index 85%
rename from tools/binary_size/console.py
rename to tools/binary_size/libsupersize/console.py
index 53f7606..34de184
--- a/tools/binary_size/console.py
+++ b/tools/binary_size/libsupersize/console.py
@@ -1,17 +1,8 @@
-#!/usr/bin/env python
 # 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.
 
-"""Tool for analyzing binary size of executables using nm or linker map files.
-
-Map files can be created by passing "-Map Foo.map" to the linker. If a map file
-is unavailable, this tool can also be pointed at an unstripped executable, but
-the information does not seem to be as accurate in this case.
-
-Inspired by SymbolSort for Windows:
-  https://github.com/adrianstone55/SymbolSort
-"""
+"""An interactive console for looking analyzing .size files."""
 
 import argparse
 import atexit
@@ -24,10 +15,9 @@
 import subprocess
 import sys
 
+import archive
 import describe
 import file_format
-import helpers
-import map2size
 import match_util
 import models
 import paths
@@ -120,8 +110,7 @@
     output_dir = self._lazy_paths.output_directory or ''
     path = os.path.normpath(os.path.join(output_dir, filename))
 
-    found_build_id = map2size.BuildIdFromElf(
-        path, self._lazy_paths.tool_prefix)
+    found_build_id = archive.BuildIdFromElf(path, self._lazy_paths.tool_prefix)
     expected_build_id = size_info.metadata.get(models.METADATA_ELF_BUILD_ID)
     assert found_build_id == expected_build_id, (
         'Build ID does not match for %s' % path)
@@ -228,26 +217,30 @@
     code.InteractiveConsole(self._variables).interact(self._CreateBanner())
 
 
-def main(argv):
-  parser = argparse.ArgumentParser()
-  parser.add_argument('inputs', nargs='+',
-                      help='Input .size files to load. For a single file, '
-                           'it will be mapped to variables as: size_info & '
-                           'symbols (where symbols = size_info.symbols). For '
-                           'multiple inputs, the names will be size_info1, '
-                           'symbols1, etc.')
-  parser.add_argument('--query',
-                      help='Print the result of the given snippet. Example: '
-                           'symbols.WhereInSection("d").'
-                           'WhereBiggerThan(100)')
-  paths.AddOptions(parser)
-  args = helpers.AddCommonOptionsAndParseArgs(parser, argv)
+def AddArguments(parser):
+  parser.add_argument(
+      'inputs', nargs='+',
+      help='Input .size files to load. For a single file, it will be mapped to '
+           'the variable "size_info". For multiple inputs, the names will be '
+           'size_info1, size_info2, etc.')
+  parser.add_argument(
+      '--query', help='Print the result of the given snippet. Example: '
+                      'size_info.symbols.WhereInSection("d")'
+                      '.WhereBiggerThan(100)')
+  parser.add_argument('--tool-prefix', default='',
+                      help='Path prefix for objdump. Required only for '
+                           'Disassemble().')
+  parser.add_argument('--output-directory',
+                      help='Path to the root build directory. Used only for '
+                           'Disassemble().')
 
+
+def Run(args, parser):
   for path in args.inputs:
     if not path.endswith('.size'):
       parser.error('All inputs must end with ".size"')
 
-  size_infos = [map2size.LoadAndPostProcessSizeInfo(p) for p in args.inputs]
+  size_infos = [archive.LoadAndPostProcessSizeInfo(p) for p in args.inputs]
   lazy_paths = paths.LazyPaths(args=args, input_file=args.inputs[0])
   session = _Session(size_infos, lazy_paths)
 
@@ -257,7 +250,3 @@
   else:
     logging.info('Entering interactive console.')
     session.GoInteractive()
-
-
-if __name__ == '__main__':
-  sys.exit(main(sys.argv))
diff --git a/tools/binary_size/describe.py b/tools/binary_size/libsupersize/describe.py
similarity index 100%
rename from tools/binary_size/describe.py
rename to tools/binary_size/libsupersize/describe.py
diff --git a/tools/binary_size/file_format.py b/tools/binary_size/libsupersize/file_format.py
similarity index 99%
rename from tools/binary_size/file_format.py
rename to tools/binary_size/libsupersize/file_format.py
index c9ef495..3e8baaf2 100644
--- a/tools/binary_size/file_format.py
+++ b/tools/binary_size/libsupersize/file_format.py
@@ -10,11 +10,12 @@
 import datetime
 import gzip
 import json
-import models
 import logging
 import os
 import shutil
 
+import models
+
 
 # File format version for .size files.
 _SERIALIZATION_VERSION = 'Size File Format v1'
diff --git a/tools/binary_size/function_signature.py b/tools/binary_size/libsupersize/function_signature.py
similarity index 100%
rename from tools/binary_size/function_signature.py
rename to tools/binary_size/libsupersize/function_signature.py
diff --git a/tools/binary_size/function_signature_test.py b/tools/binary_size/libsupersize/function_signature_test.py
similarity index 100%
rename from tools/binary_size/function_signature_test.py
rename to tools/binary_size/libsupersize/function_signature_test.py
diff --git a/tools/binary_size/libsupersize/helpers.py b/tools/binary_size/libsupersize/helpers.py
new file mode 100644
index 0000000..bddcd764
--- /dev/null
+++ b/tools/binary_size/libsupersize/helpers.py
@@ -0,0 +1,11 @@
+# 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.
+
+"""Utility methods."""
+
+import os
+
+
+SRC_ROOT = os.path.dirname(
+    os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
diff --git a/tools/binary_size/create_html_breakdown.py b/tools/binary_size/libsupersize/html_report.py
old mode 100755
new mode 100644
similarity index 94%
rename from tools/binary_size/create_html_breakdown.py
rename to tools/binary_size/libsupersize/html_report.py
index ad278b2..ac21349
--- a/tools/binary_size/create_html_breakdown.py
+++ b/tools/binary_size/libsupersize/html_report.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 # 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.
@@ -12,8 +11,8 @@
 import shutil
 import sys
 
+import archive
 import helpers
-import map2size
 
 
 # Node dictionary keys. These are output in json read by the webapp so
@@ -163,8 +162,7 @@
   shutil.copy(os.path.join(template_src, 'D3SymbolTreeMap.js'), dest_dir)
 
 
-def main(argv):
-  parser = argparse.ArgumentParser()
+def AddArguments(parser):
   parser.add_argument('input_file',
                       help='Path to input .size file.')
   parser.add_argument('--report-dir', metavar='PATH', required=True,
@@ -175,9 +173,14 @@
                            'space)')
   parser.add_argument('--include-symbols', action='store_true',
                       help='Use per-symbol granularity rather than per-file.')
-  args = helpers.AddCommonOptionsAndParseArgs(parser, argv)
 
-  size_info = map2size.LoadAndPostProcessSizeInfo(args.input_file)
+
+def Run(args, parser):
+  if not args.input_file.endswith('.size'):
+    parser.error('Input must end with ".size"')
+
+  logging.info('Reading .size file')
+  size_info = archive.LoadAndPostProcessSizeInfo(args.input_file)
   symbols = size_info.symbols
   if not args.include_bss:
     symbols = symbols.WhereInSection('b').Inverted()
@@ -191,15 +194,11 @@
   logging.info('Creating JSON objects')
   tree_root = _MakeCompactTree(symbols, args.include_symbols)
 
-  logging.info('Serializing')
+  logging.info('Serializing JSON')
   with open(os.path.join(args.report_dir, 'data.js'), 'w') as out_file:
     out_file.write('var tree_data=')
     # Use separators without whitespace to get a smaller file.
     json.dump(tree_root, out_file, ensure_ascii=False, check_circular=False,
               separators=(',', ':'))
 
-  print 'Report saved to ' + args.report_dir + '/index.html'
-
-
-if __name__ == '__main__':
-  sys.exit(main(sys.argv))
+  logging.warning('Report saved to %s/index.html', args.report_dir)
diff --git a/tools/binary_size/integration_test.py b/tools/binary_size/libsupersize/integration_test.py
similarity index 85%
rename from tools/binary_size/integration_test.py
rename to tools/binary_size/libsupersize/integration_test.py
index 40c3d6e..830c335 100755
--- a/tools/binary_size/integration_test.py
+++ b/tools/binary_size/libsupersize/integration_test.py
@@ -13,9 +13,9 @@
 import sys
 import tempfile
 
+import archive
 import describe
 import file_format
-import map2size
 import models
 import paths
 
@@ -52,7 +52,7 @@
 
 
 def _RunApp(name, *args):
-  argv = [os.path.join(_SCRIPT_DIR, name), '--no-pypy']
+  argv = [os.path.join(_SCRIPT_DIR, 'main.py'), name, '--no-pypy']
   argv.extend(args)
   return subprocess.check_output(argv).splitlines()
 
@@ -64,16 +64,15 @@
     if not IntegrationTest.size_info:
       lazy_paths = paths.LazyPaths(output_directory=_TEST_DATA_DIR)
       IntegrationTest.size_info = (
-          map2size.CreateSizeInfo(_TEST_MAP_PATH, lazy_paths))
+          archive.CreateSizeInfo(_TEST_MAP_PATH, lazy_paths))
     return copy.deepcopy(IntegrationTest.size_info)
 
   @_CompareWithGolden
-  def test_Map2Size(self):
+  def test_Archive(self):
     with tempfile.NamedTemporaryFile(suffix='.size') as temp_file:
-      _RunApp('map2size.py', '--output-directory', _TEST_DATA_DIR,
-              '--map-file', _TEST_MAP_PATH, '--elf-file', '',
-              '--output-file', temp_file.name)
-      size_info = map2size.LoadAndPostProcessSizeInfo(temp_file.name)
+      _RunApp('archive', temp_file.name, '--output-directory', _TEST_DATA_DIR,
+              '--map-file', _TEST_MAP_PATH, '--elf-file', '')
+      size_info = archive.LoadAndPostProcessSizeInfo(temp_file.name)
     # Check that saving & loading is the same as directly parsing the .map.
     expected_size_info = self._CloneSizeInfo()
     self.assertEquals(expected_size_info.metadata, size_info.metadata)
@@ -87,19 +86,18 @@
     stats = describe.DescribeSizeInfoCoverage(size_info)
     return itertools.chain(stats, sym_strs)
 
-  def test_Map2Size_NoSourcePaths(self):
+  def test_Archive_NoSourcePaths(self):
     # Just tests that it doesn't crash.
     with tempfile.NamedTemporaryFile(suffix='.size') as temp_file:
-      _RunApp('map2size.py', '--no-source-paths',
-              '--map-file', _TEST_MAP_PATH, '--elf-file', '',
-              '--output-file', temp_file.name)
-      map2size.LoadAndPostProcessSizeInfo(temp_file.name)
+      _RunApp('archive', temp_file.name, '--no-source-paths',
+              '--map-file', _TEST_MAP_PATH, '--elf-file', '')
+      archive.LoadAndPostProcessSizeInfo(temp_file.name)
 
   @_CompareWithGolden
   def test_ConsoleNullDiff(self):
     with tempfile.NamedTemporaryFile(suffix='.size') as temp_file:
       file_format.SaveSizeInfo(self._CloneSizeInfo(), temp_file.name)
-      return _RunApp('console.py', '--query', 'Diff(size_info1, size_info2)',
+      return _RunApp('console', '--query', 'Diff(size_info1, size_info2)',
                      temp_file.name, temp_file.name)
 
   @_CompareWithGolden
diff --git a/tools/binary_size/linker_map_parser.py b/tools/binary_size/libsupersize/linker_map_parser.py
similarity index 100%
rename from tools/binary_size/linker_map_parser.py
rename to tools/binary_size/libsupersize/linker_map_parser.py
diff --git a/tools/binary_size/libsupersize/main.py b/tools/binary_size/libsupersize/main.py
new file mode 100755
index 0000000..4eeb894
--- /dev/null
+++ b/tools/binary_size/libsupersize/main.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+# 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.
+
+"""Collect, archive, and analyze Chrome's binary size."""
+
+import argparse
+import atexit
+import collections
+import distutils.spawn
+import logging
+import os
+import platform
+import resource
+import sys
+
+import archive
+import console
+import html_report
+
+
+def _LogPeakRamUsage():
+  peak_ram_usage = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
+  peak_ram_usage += resource.getrusage(resource.RUSAGE_CHILDREN).ru_maxrss
+  logging.info('Peak RAM usage was %d MB.', peak_ram_usage / 1024)
+
+
+def _AddCommonArguments(parser):
+  parser.add_argument('--no-pypy', action='store_true',
+                      help='Do not automatically switch to pypy when available')
+  parser.add_argument('-v',
+                      '--verbose',
+                      default=0,
+                      action='count',
+                      help='Verbose level (multiple times for more)')
+
+
+def main():
+  parser = argparse.ArgumentParser(description=__doc__)
+  sub_parsers = parser.add_subparsers()
+  actions = collections.OrderedDict()
+  actions['archive'] = (archive, 'Create a .size file')
+  actions['html_report'] = (
+      html_report, 'Create a stand-alone html report from a .size file.')
+  actions['console'] = (
+      console,
+      'Starts an interactive Python console for analyzing .size files.')
+
+  for name, tup in actions.iteritems():
+    sub_parser = sub_parsers.add_parser(name, help=tup[1])
+    _AddCommonArguments(sub_parser)
+    tup[0].AddArguments(sub_parser)
+    sub_parser.set_defaults(func=tup[0].Run)
+
+  if len(sys.argv) == 1:
+    parser.print_help()
+    sys.exit(1)
+  elif len(sys.argv) == 2 and sys.argv[1] in actions:
+    parser.parse_args(sys.argv[1:] + ['-h'])
+    sys.exit(1)
+
+  args = parser.parse_args()
+  logging.basicConfig(level=logging.WARNING - args.verbose * 10,
+                      format='%(levelname).1s %(relativeCreated)6d %(message)s')
+
+  if not args.no_pypy and platform.python_implementation() == 'CPython':
+    # Switch to pypy if it's available.
+    pypy_path = distutils.spawn.find_executable('pypy')
+    if pypy_path:
+      logging.debug('Switching to pypy.')
+      os.execv(pypy_path, [pypy_path] + sys.argv)
+    # Running with python: 6s. Running with pypy: 3s
+    logging.warning('This script runs more than 2x faster if you install pypy.')
+
+  if logging.getLogger().isEnabledFor(logging.DEBUG):
+    atexit.register(_LogPeakRamUsage)
+
+  args.func(args, parser)
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/tools/binary_size/match_util.py b/tools/binary_size/libsupersize/match_util.py
similarity index 100%
rename from tools/binary_size/match_util.py
rename to tools/binary_size/libsupersize/match_util.py
diff --git a/tools/binary_size/match_util_test.py b/tools/binary_size/libsupersize/match_util_test.py
similarity index 99%
rename from tools/binary_size/match_util_test.py
rename to tools/binary_size/libsupersize/match_util_test.py
index 15053990..eb5349a 100755
--- a/tools/binary_size/match_util_test.py
+++ b/tools/binary_size/libsupersize/match_util_test.py
@@ -3,10 +3,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import re
 import unittest
 
 import match_util
-import re
 
 
 class MatchHelperTest(unittest.TestCase):
diff --git a/tools/binary_size/models.py b/tools/binary_size/libsupersize/models.py
similarity index 100%
rename from tools/binary_size/models.py
rename to tools/binary_size/libsupersize/models.py
diff --git a/tools/binary_size/ninja_parser.py b/tools/binary_size/libsupersize/ninja_parser.py
similarity index 100%
rename from tools/binary_size/ninja_parser.py
rename to tools/binary_size/libsupersize/ninja_parser.py
diff --git a/tools/binary_size/paths.py b/tools/binary_size/libsupersize/paths.py
similarity index 93%
rename from tools/binary_size/paths.py
rename to tools/binary_size/libsupersize/paths.py
index 360ab32d..bf877cb6 100644
--- a/tools/binary_size/paths.py
+++ b/tools/binary_size/libsupersize/paths.py
@@ -12,13 +12,6 @@
 _STATUS_VERIFIED = 2
 
 
-def AddOptions(parser):
-  parser.add_argument('--tool-prefix', default='',
-                      help='Path prefix for c++filt.')
-  parser.add_argument('--output-directory',
-                      help='Path to the root build directory.')
-
-
 class LazyPaths(object):
   def __init__(self, args=None, tool_prefix=None, output_directory=None,
                input_file=None):
diff --git a/tools/binary_size/template/D3SymbolTreeMap.js b/tools/binary_size/libsupersize/template/D3SymbolTreeMap.js
similarity index 100%
rename from tools/binary_size/template/D3SymbolTreeMap.js
rename to tools/binary_size/libsupersize/template/D3SymbolTreeMap.js
diff --git a/tools/binary_size/template/index.html b/tools/binary_size/libsupersize/template/index.html
similarity index 100%
rename from tools/binary_size/template/index.html
rename to tools/binary_size/libsupersize/template/index.html
diff --git a/tools/binary_size/template/test-data-generator.html b/tools/binary_size/libsupersize/template/test-data-generator.html
similarity index 100%
rename from tools/binary_size/template/test-data-generator.html
rename to tools/binary_size/libsupersize/template/test-data-generator.html
diff --git a/tools/binary_size/testdata/ActualDiff.golden b/tools/binary_size/libsupersize/testdata/ActualDiff.golden
similarity index 100%
rename from tools/binary_size/testdata/ActualDiff.golden
rename to tools/binary_size/libsupersize/testdata/ActualDiff.golden
diff --git a/tools/binary_size/testdata/Map2Size.golden b/tools/binary_size/libsupersize/testdata/Archive.golden
similarity index 100%
rename from tools/binary_size/testdata/Map2Size.golden
rename to tools/binary_size/libsupersize/testdata/Archive.golden
diff --git a/tools/binary_size/testdata/ConsoleNullDiff.golden b/tools/binary_size/libsupersize/testdata/ConsoleNullDiff.golden
similarity index 100%
rename from tools/binary_size/testdata/ConsoleNullDiff.golden
rename to tools/binary_size/libsupersize/testdata/ConsoleNullDiff.golden
diff --git a/tools/binary_size/testdata/FullDescription.golden b/tools/binary_size/libsupersize/testdata/FullDescription.golden
similarity index 100%
rename from tools/binary_size/testdata/FullDescription.golden
rename to tools/binary_size/libsupersize/testdata/FullDescription.golden
diff --git a/tools/binary_size/testdata/SymbolGroupMethods.golden b/tools/binary_size/libsupersize/testdata/SymbolGroupMethods.golden
similarity index 100%
rename from tools/binary_size/testdata/SymbolGroupMethods.golden
rename to tools/binary_size/libsupersize/testdata/SymbolGroupMethods.golden
diff --git a/tools/binary_size/testdata/build.ninja b/tools/binary_size/libsupersize/testdata/build.ninja
similarity index 100%
rename from tools/binary_size/testdata/build.ninja
rename to tools/binary_size/libsupersize/testdata/build.ninja
diff --git a/tools/binary_size/testdata/sub.ninja b/tools/binary_size/libsupersize/testdata/sub.ninja
similarity index 100%
rename from tools/binary_size/testdata/sub.ninja
rename to tools/binary_size/libsupersize/testdata/sub.ninja
diff --git a/tools/binary_size/testdata/test.map b/tools/binary_size/libsupersize/testdata/test.map
similarity index 100%
rename from tools/binary_size/testdata/test.map
rename to tools/binary_size/libsupersize/testdata/test.map
diff --git a/tools/binary_size/supersize b/tools/binary_size/supersize
new file mode 100755
index 0000000..87a5630
--- /dev/null
+++ b/tools/binary_size/supersize
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+# 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.
+
+base_dir=$(dirname "$0")
+
+exec python "$base_dir/libsupersize/main.py" "$@"
diff --git a/tools/clang/plugins/ChromeClassTester.cpp b/tools/clang/plugins/ChromeClassTester.cpp
index b0cc3db..c938806 100644
--- a/tools/clang/plugins/ChromeClassTester.cpp
+++ b/tools/clang/plugins/ChromeClassTester.cpp
@@ -48,15 +48,15 @@
   // We handle class types here where we have semantic information. We can only
   // check structs/classes/enums here, but we get a bunch of nice semantic
   // information instead of just parsing information.
+  if (InBannedNamespace(tag))
+    return;
+
+  SourceLocation location = tag->getInnerLocStart();
+  LocationType location_type = ClassifyLocation(location);
+  if (location_type == LocationType::kThirdParty)
+    return;
 
   if (CXXRecordDecl* record = dyn_cast<CXXRecordDecl>(tag)) {
-    if (InBannedNamespace(record))
-      return;
-
-    SourceLocation record_location = record->getInnerLocStart();
-    if (InBannedDirectory(record_location))
-      return;
-
     // We sadly need to maintain a blacklist of types that violate these
     // rules, but do so for good reason or due to limitations of this
     // checker (i.e., we don't handle extern templates very well).
@@ -69,17 +69,14 @@
     if (ends_with(base_name, "Matcher"))
         return;
 
-    CheckChromeClass(record_location, record);
+    CheckChromeClass(location_type, location, record);
   } else if (EnumDecl* enum_decl = dyn_cast<EnumDecl>(tag)) {
-    SourceLocation enum_location = enum_decl->getInnerLocStart();
-    if (InBannedDirectory(enum_location))
-      return;
-
     std::string base_name = enum_decl->getNameAsString();
+    // TODO(dcheng): This should probably consult a separate list.
     if (IsIgnoredType(base_name))
       return;
 
-    CheckChromeEnum(enum_location, enum_decl);
+    CheckChromeEnum(location_type, location, enum_decl);
   }
 }
 
@@ -98,27 +95,27 @@
 
 }
 
-bool ChromeClassTester::InBannedDirectory(SourceLocation loc) {
+ChromeClassTester::LocationType ChromeClassTester::ClassifyLocation(
+    SourceLocation loc) {
   if (instance().getSourceManager().isInSystemHeader(loc))
-    return true;
+    return LocationType::kThirdParty;
 
   std::string filename;
   if (!GetFilename(loc, &filename)) {
     // If the filename cannot be determined, simply treat this as a banned
     // location, instead of going through the full lookup process.
-    return true;
+    return LocationType::kThirdParty;
   }
 
   // We need to special case scratch space; which is where clang does its
   // macro expansion. We explicitly want to allow people to do otherwise bad
   // things through macros that were defined due to third party libraries.
   if (filename == "<scratch space>")
-    return true;
+    return LocationType::kThirdParty;
 
   // Don't complain about autogenerated protobuf files.
-  if (ends_with(filename, ".pb.h")) {
-    return true;
-  }
+  if (ends_with(filename, ".pb.h"))
+    return LocationType::kThirdParty;
 
 #if defined(LLVM_ON_UNIX)
   // Resolve the symlinktastic relative path and make it absolute.
@@ -160,14 +157,14 @@
   std::replace(filename.begin(), filename.end(), '\\', '/');
 #endif
 
-  for (const std::string& allowed_dir : allowed_directories_) {
+  for (const std::string& blink_dir : blink_directories_) {
     // If any of the allowed directories occur as a component in filename,
     // this file is allowed.
-    assert(allowed_dir.front() == '/' && "Allowed dir must start with '/'");
-    assert(allowed_dir.back() == '/' && "Allowed dir must end with '/'");
+    assert(blink_dir.front() == '/' && "Allowed dir must start with '/'");
+    assert(blink_dir.back() == '/' && "Allowed dir must end with '/'");
 
-    if (filename.find(allowed_dir) != std::string::npos)
-      return false;
+    if (filename.find(blink_dir) != std::string::npos)
+      return LocationType::kBlink;
   }
 
   for (const std::string& banned_dir : banned_directories_) {
@@ -177,24 +174,22 @@
     assert(banned_dir.back() == '/' && "Banned dir must end with '/'");
 
     if (filename.find(banned_dir) != std::string::npos)
-      return true;
+      return LocationType::kThirdParty;
   }
 
-  return false;
+  return LocationType::kChrome;
 }
 
 bool ChromeClassTester::InBannedNamespace(const Decl* record) {
   std::string n = GetNamespace(record);
-  if (!n.empty()) {
-    return std::find(banned_namespaces_.begin(), banned_namespaces_.end(), n)
-        != banned_namespaces_.end();
-  }
+  if (!n.empty())
+    return banned_namespaces_.find(n) != banned_namespaces_.end();
 
   return false;
 }
 
 std::string ChromeClassTester::GetNamespace(const Decl* record) {
-  return GetNamespaceImpl(record->getDeclContext(), "");
+  return GetNamespaceImpl(record->getDeclContext(), std::string());
 }
 
 bool ChromeClassTester::HasIgnoredBases(const CXXRecordDecl* record) {
@@ -238,9 +233,7 @@
   banned_namespaces_.emplace("std");
   banned_namespaces_.emplace("__gnu_cxx");
 
-  if (options_.enforce_in_thirdparty_webkit) {
-    allowed_directories_.emplace("/third_party/WebKit/");
-  }
+  blink_directories_.emplace("/third_party/WebKit/");
 
   banned_directories_.emplace("/third_party/");
   banned_directories_.emplace("/native_client/");
diff --git a/tools/clang/plugins/ChromeClassTester.h b/tools/clang/plugins/ChromeClassTester.h
index 22af158..03bc2bd 100644
--- a/tools/clang/plugins/ChromeClassTester.h
+++ b/tools/clang/plugins/ChromeClassTester.h
@@ -32,15 +32,27 @@
 
   // Emits a simple warning; this shouldn't be used if you require printf-style
   // printing.
+  // TODO(dcheng): This will be removed. Do not add new usage.
   void emitWarning(clang::SourceLocation loc, const char* error);
 
   // Utility method for subclasses to check if this class is in a banned
   // namespace.
   bool InBannedNamespace(const clang::Decl* record);
 
-  // Utility method for subclasses to check if the source location is in a
-  // directory the plugin should ignore.
-  bool InBannedDirectory(clang::SourceLocation loc);
+  // Utility method for subclasses to check how a certain SourceLocation should
+  // be handled. The main criteria for classification is the SourceLocation's
+  // path (e.g. whether it's in //third_party).
+  enum class LocationType {
+    // Enforce all default checks.
+    kChrome,
+    // Enforces a subset of checks for Blink code. This is hopefully a
+    // transitional stage, as more plugin checks are gradually enabled in Blink.
+    kBlink,
+    // Skip all checks. Typically, this is third-party or generated code where
+    // it doesn't make sense to enforce Chrome's custom diagnostics.
+    kThirdParty,
+  };
+  LocationType ClassifyLocation(clang::SourceLocation loc);
 
   // Utility method for subclasses to determine the namespace of the
   // specified record, if any. Unnamed namespaces will be identified as
@@ -63,14 +75,15 @@
 
   // Filtered versions of tags that are only called with things defined in
   // chrome header files.
-  virtual void CheckChromeClass(clang::SourceLocation record_location,
+  virtual void CheckChromeClass(LocationType location_type,
+                                clang::SourceLocation record_location,
                                 clang::CXXRecordDecl* record) = 0;
 
   // Filtered versions of enum type that are only called with things defined
   // in chrome header files.
-  virtual void CheckChromeEnum(clang::SourceLocation enum_location,
-                               clang::EnumDecl* enum_decl) {
-  }
+  virtual void CheckChromeEnum(LocationType location_type,
+                               clang::SourceLocation enum_location,
+                               clang::EnumDecl* enum_decl) = 0;
 
   // Utility methods used for filtering out non-chrome classes (and ones we
   // deliberately ignore) in HandleTagDeclDefinition().
@@ -88,9 +101,8 @@
   // List of banned namespaces.
   std::set<std::string> banned_namespaces_;
 
-  // List of directories allowed even though their parent directories are in
-  // |banned_directories_|, below.
-  std::set<std::string> allowed_directories_;
+  // List of Blink directories.
+  std::set<std::string> blink_directories_;
 
   // List of banned directories.
   std::set<std::string> banned_directories_;
diff --git a/tools/clang/plugins/FindBadConstructsConsumer.cpp b/tools/clang/plugins/FindBadConstructsConsumer.cpp
index 4c93af4d..629090c 100644
--- a/tools/clang/plugins/FindBadConstructsConsumer.cpp
+++ b/tools/clang/plugins/FindBadConstructsConsumer.cpp
@@ -216,8 +216,14 @@
   return true;
 }
 
-void FindBadConstructsConsumer::CheckChromeClass(SourceLocation record_location,
+void FindBadConstructsConsumer::CheckChromeClass(LocationType location_type,
+                                                 SourceLocation record_location,
                                                  CXXRecordDecl* record) {
+  // TODO(dcheng): After emitWarning() is removed, move warning filtering into
+  // ReportIfSpellingLocNotIgnored.
+  if (location_type == LocationType::kBlink)
+    return;
+
   bool implementation_file = InImplementationFile(record_location);
 
   if (!implementation_file) {
@@ -245,11 +251,15 @@
   CheckWeakPtrFactoryMembers(record_location, record);
 }
 
-void FindBadConstructsConsumer::CheckChromeEnum(SourceLocation enum_location,
+void FindBadConstructsConsumer::CheckChromeEnum(LocationType location_type,
+                                                SourceLocation enum_location,
                                                 EnumDecl* enum_decl) {
   if (!options_.check_enum_last_value)
     return;
 
+  if (location_type == LocationType::kBlink)
+    return;
+
   bool got_one = false;
   bool is_signed = false;
   llvm::APSInt max_so_far;
@@ -451,9 +461,12 @@
 FindBadConstructsConsumer::ReportIfSpellingLocNotIgnored(
     SourceLocation loc,
     unsigned diagnostic_id) {
-  return SuppressibleDiagnosticBuilder(
-      &diagnostic(), loc, diagnostic_id,
-      InBannedDirectory(instance().getSourceManager().getSpellingLoc(loc)));
+  LocationType type =
+      ClassifyLocation(instance().getSourceManager().getSpellingLoc(loc));
+  bool ignored =
+      type == LocationType::kThirdParty || type == LocationType::kBlink;
+  return SuppressibleDiagnosticBuilder(&diagnostic(), loc, diagnostic_id,
+                                       ignored);
 }
 
 // Checks that virtual methods are correctly annotated, and have no body in a
@@ -609,7 +622,8 @@
         bool emit = true;
         if (loc.isMacroID()) {
           SourceManager& manager = instance().getSourceManager();
-          if (InBannedDirectory(manager.getSpellingLoc(loc)))
+          LocationType type = ClassifyLocation(manager.getSpellingLoc(loc));
+          if (type == LocationType::kThirdParty || type == LocationType::kBlink)
             emit = false;
           else {
             StringRef name = Lexer::getImmediateMacroName(
@@ -998,8 +1012,10 @@
           // Check if we should even be considering this type (note that there
           // should be fewer auto types than banned namespace/directory types,
           // so check this last.
+          LocationType location_type =
+              ClassifyLocation(var_decl->getLocStart());
           if (!InBannedNamespace(var_decl) &&
-              !InBannedDirectory(var_decl->getLocStart())) {
+              location_type != LocationType::kThirdParty) {
             // The range starts from |var_decl|'s loc start, which is the
             // beginning of the full expression defining this |var_decl|. It
             // ends, however, where this |var_decl|'s type loc ends, since
diff --git a/tools/clang/plugins/FindBadConstructsConsumer.h b/tools/clang/plugins/FindBadConstructsConsumer.h
index 64032d8..a1acf4a1 100644
--- a/tools/clang/plugins/FindBadConstructsConsumer.h
+++ b/tools/clang/plugins/FindBadConstructsConsumer.h
@@ -56,9 +56,11 @@
   bool VisitCallExpr(clang::CallExpr* call_expr);
 
   // ChromeClassTester overrides:
-  void CheckChromeClass(clang::SourceLocation record_location,
+  void CheckChromeClass(LocationType location_type,
+                        clang::SourceLocation record_location,
                         clang::CXXRecordDecl* record) override;
-  void CheckChromeEnum(clang::SourceLocation enum_location,
+  void CheckChromeEnum(LocationType location_type,
+                       clang::SourceLocation enum_location,
                        clang::EnumDecl* enum_decl) override;
 
  private:
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 9cd079e..29585d80 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -433,6 +433,7 @@
     'official.desktop': {
       'linux64': 'official',
       'mac64': 'official',
+      'mac64-recipes': 'official',
       'precise64': 'official_six_concurrent_links',
 
       # Currently the official bots set mini_installer_official_deps=1
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index cbc2af4..80c319a 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -20466,6 +20466,14 @@
   </summary>
 </histogram>
 
+<histogram name="Favicons.LargeIconService.DownloadedSize" units="pixels">
+  <owner>jkrcal@chromium.org</owner>
+  <summary>
+    Records the size (concretely the width) in pixel of the favicon downloaded
+    from Google favicon server (size 0 denotes that download has failed).
+  </summary>
+</histogram>
+
 <histogram name="FileBrowser.CloudImport.UserAction"
     enum="CloudImportUserAction">
   <owner>smckay@google.com</owner>
@@ -101102,6 +101110,7 @@
   <int value="-1772172557" label="enable-osk-overscroll"/>
   <int value="-1767470652" label="out-of-process-pdf"/>
   <int value="-1751928267" label="disable-icon-ntp"/>
+  <int value="-1749176684" label="PauseBackgroundTabs:disabled"/>
   <int value="-1746767834" label="ssl-interstitial-v2-gray"/>
   <int value="-1740519217" label="disable-software-rasterizer"/>
   <int value="-1735643253" label="enable-display-list-2d-canvas"/>
@@ -101129,6 +101138,7 @@
   <int value="-1660972490" label="gpu-rasterization-msaa-sample-count"/>
   <int value="-1655535052" label="enable-pointer-events"/>
   <int value="-1654344175" label="disable-extension-info-dialog"/>
+  <int value="-1653838003" label="PauseBackgroundTabs:enabled"/>
   <int value="-1649778035" label="disable-clear-browsing-data-counters"/>
   <int value="-1648216169" label="NewOmniboxAnswerTypes:disabled"/>
   <int value="-1634154256" label="ZeroSuggestRedirectToChrome:enabled"/>
@@ -101179,6 +101189,7 @@
   <int value="-1473136627" label="enable-web-payments"/>
   <int value="-1467332609" label="tab-management-experiment-type-anise"/>
   <int value="-1466990325" label="CrosCompUpdates:enabled"/>
+  <int value="-1463410070" label="IPH_DemoMode:enabled"/>
   <int value="-1460462432" label="disable-media-source"/>
   <int value="-1456004000" label="VrShell:disabled"/>
   <int value="-1443796945" label="OfflinePagesSharing:disabled"/>
@@ -101310,6 +101321,7 @@
   <int value="-945806012" label="DownloadsUi:enabled"/>
   <int value="-938178614" label="enable-suggestions-with-substring-match"/>
   <int value="-933316841" label="enable-permissions-blacklist"/>
+  <int value="-928138978" label="IPH_DemoMode:disabled"/>
   <int value="-926422468" label="disable-embedded-shared-worker"/>
   <int value="-918900957" label="AutofillCreditCardAssist:disabled"/>
   <int value="-918618075" label="enable-service-worker"/>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 75d8a1a2..d85419ea 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -41,4 +41,21 @@
   <metric name="UploadDecision"/>
 </event>
 
+<event name="Translate">
+  <owner>hamelphi@chromium.org</owner>
+  <summary>
+    Metrics related to a Translate event. These metrics are described in
+    TranslateEventProto.
+  </summary>
+  <metric name="AcceptCount"/>
+  <metric name="Country"/>
+  <metric name="DeclineCount"/>
+  <metric name="EventType"/>
+  <metric name="IgnoreCount"/>
+  <metric name="RankerResponse"/>
+  <metric name="RankerVersion"/>
+  <metric name="SourceLanguage"/>
+  <metric name="TargetLanguage"/>
+</event>
+
 </ukm-configuration>
diff --git a/ui/aura/gestures/gesture_recognizer_unittest.cc b/ui/aura/gestures/gesture_recognizer_unittest.cc
index 0513496..0aff210 100644
--- a/ui/aura/gestures/gesture_recognizer_unittest.cc
+++ b/ui/aura/gestures/gesture_recognizer_unittest.cc
@@ -4750,5 +4750,30 @@
   EXPECT_TRUE(delegate_2->tap());
 }
 
+TEST_F(GestureRecognizerTest, GestureConsumerCleanupBeforeTouchAck) {
+  std::unique_ptr<QueueTouchEventDelegate> delegate(
+      new QueueTouchEventDelegate(host()->dispatcher()));
+  TimedEvents tes;
+  const int kTouchId = 7;
+  gfx::Rect bounds(0, 0, 1000, 1000);
+
+  std::unique_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+      delegate.get(), -1234, bounds, root_window()));
+
+  delegate->set_window(window.get());
+
+  delegate->Reset();
+  ui::TouchEvent press(
+      ui::ET_TOUCH_PRESSED, gfx::Point(512, 512), tes.Now(),
+      ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, kTouchId));
+  DispatchEventUsingWindowDispatcher(&press);
+
+  window->Hide();
+
+  delegate->Reset();
+  delegate->ReceivedAck();
+  EXPECT_0_EVENTS(delegate->events());
+}
+
 }  // namespace test
 }  // namespace aura
diff --git a/ui/events/gestures/gesture_recognizer_impl.cc b/ui/events/gestures/gesture_recognizer_impl.cc
index 5b2263f..5f7e70cd 100644
--- a/ui/events/gestures/gesture_recognizer_impl.cc
+++ b/ui/events/gestures/gesture_recognizer_impl.cc
@@ -36,18 +36,19 @@
   }
 }
 
-bool RemoveConsumerFromMap(GestureConsumer* consumer,
-                           GestureRecognizerImpl::TouchIdToConsumerMap* map) {
-  bool consumer_removed = false;
+// Generic function to remove every entry from a map having the given value.
+template <class Key, class T, class Value>
+bool RemoveValueFromMap(std::map<Key, T>* map, const Value& value) {
+  bool removed = false;
   for (auto i = map->begin(); i != map->end();) {
-    if (i->second == consumer) {
+    if (i->second == value) {
       map->erase(i++);
-      consumer_removed = true;
+      removed = true;
     } else {
       ++i;
     }
   }
-  return consumer_removed;
+  return removed;
 }
 
 }  // namespace
@@ -302,12 +303,17 @@
     GestureConsumer* consumer) {
   bool state_cleaned_up = false;
 
-  if (consumer_gesture_provider_.count(consumer)) {
+  auto consumer_gesture_provider_it = consumer_gesture_provider_.find(consumer);
+  if (consumer_gesture_provider_it != consumer_gesture_provider_.end()) {
+    // Remove gesture provider associated with the consumer from
+    // |event_to_gesture_provider_| map.
+    RemoveValueFromMap(&event_to_gesture_provider_,
+                       consumer_gesture_provider_it->second.get());
     state_cleaned_up = true;
-    consumer_gesture_provider_.erase(consumer);
+    consumer_gesture_provider_.erase(consumer_gesture_provider_it);
   }
 
-  state_cleaned_up |= RemoveConsumerFromMap(consumer, &touch_id_target_);
+  state_cleaned_up |= RemoveValueFromMap(&touch_id_target_, consumer);
   return state_cleaned_up;
 }
 
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc
index 6ca6f6b4..9ad137f4 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc
@@ -19,6 +19,38 @@
 const char* kSrcYPropName = "SRC_Y";
 const char* kSrcWPropName = "SRC_W";
 const char* kSrcHPropName = "SRC_H";
+const char* kRotationPropName = "rotation";
+
+// TODO(dcastagna): Remove the following defines once they're in libdrm headers.
+#if !defined(DRM_ROTATE_0)
+#define DRM_ROTATE_0 0
+#define DRM_ROTATE_90 1
+#define DRM_ROTATE_180 2
+#define DRM_ROTATE_270 3
+#define DRM_REFLECT_X 4
+#define DRM_REFLECT_Y 5
+#endif
+
+uint32_t OverlayTransformToDrmRotationPropertyValue(
+    gfx::OverlayTransform transform) {
+  switch (transform) {
+    case gfx::OVERLAY_TRANSFORM_NONE:
+      return 0;
+    case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL:
+      return 1 << DRM_REFLECT_X;
+    case gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL:
+      return 1 << DRM_REFLECT_Y;
+    case gfx::OVERLAY_TRANSFORM_ROTATE_90:
+      return 1 << DRM_ROTATE_90;
+    case gfx::OVERLAY_TRANSFORM_ROTATE_180:
+      return 1 << DRM_ROTATE_180;
+    case gfx::OVERLAY_TRANSFORM_ROTATE_270:
+      return 1 << DRM_ROTATE_270;
+    default:
+      NOTREACHED();
+  }
+  return 0;
+}
 
 }  // namespace
 
@@ -51,11 +83,13 @@
 HardwareDisplayPlaneAtomic::~HardwareDisplayPlaneAtomic() {
 }
 
-bool HardwareDisplayPlaneAtomic::SetPlaneData(drmModeAtomicReq* property_set,
-                                              uint32_t crtc_id,
-                                              uint32_t framebuffer,
-                                              const gfx::Rect& crtc_rect,
-                                              const gfx::Rect& src_rect) {
+bool HardwareDisplayPlaneAtomic::SetPlaneData(
+    drmModeAtomicReq* property_set,
+    uint32_t crtc_id,
+    uint32_t framebuffer,
+    const gfx::Rect& crtc_rect,
+    const gfx::Rect& src_rect,
+    const gfx::OverlayTransform transform) {
   int plane_set_succeeded =
       drmModeAtomicAddProperty(property_set, plane_id_, crtc_prop_.id,
                                crtc_id) &&
@@ -76,7 +110,10 @@
       drmModeAtomicAddProperty(property_set, plane_id_, src_w_prop_.id,
                                src_rect.width()) &&
       drmModeAtomicAddProperty(property_set, plane_id_, src_h_prop_.id,
-                               src_rect.height());
+                               src_rect.height()) &&
+      drmModeAtomicAddProperty(
+          property_set, plane_id_, rotation_prop_.id,
+          OverlayTransformToDrmRotationPropertyValue(transform));
   if (!plane_set_succeeded) {
     PLOG(ERROR) << "Failed to set plane data";
     return false;
@@ -87,16 +124,18 @@
 bool HardwareDisplayPlaneAtomic::InitializeProperties(
     DrmDevice* drm,
     const ScopedDrmObjectPropertyPtr& plane_props) {
-  bool props_init = crtc_prop_.Initialize(drm, kCrtcPropName, plane_props) &&
-                    fb_prop_.Initialize(drm, kFbPropName, plane_props) &&
-                    crtc_x_prop_.Initialize(drm, kCrtcXPropName, plane_props) &&
-                    crtc_y_prop_.Initialize(drm, kCrtcYPropName, plane_props) &&
-                    crtc_w_prop_.Initialize(drm, kCrtcWPropName, plane_props) &&
-                    crtc_h_prop_.Initialize(drm, kCrtcHPropName, plane_props) &&
-                    src_x_prop_.Initialize(drm, kSrcXPropName, plane_props) &&
-                    src_y_prop_.Initialize(drm, kSrcYPropName, plane_props) &&
-                    src_w_prop_.Initialize(drm, kSrcWPropName, plane_props) &&
-                    src_h_prop_.Initialize(drm, kSrcHPropName, plane_props);
+  bool props_init =
+      crtc_prop_.Initialize(drm, kCrtcPropName, plane_props) &&
+      fb_prop_.Initialize(drm, kFbPropName, plane_props) &&
+      crtc_x_prop_.Initialize(drm, kCrtcXPropName, plane_props) &&
+      crtc_y_prop_.Initialize(drm, kCrtcYPropName, plane_props) &&
+      crtc_w_prop_.Initialize(drm, kCrtcWPropName, plane_props) &&
+      crtc_h_prop_.Initialize(drm, kCrtcHPropName, plane_props) &&
+      src_x_prop_.Initialize(drm, kSrcXPropName, plane_props) &&
+      src_y_prop_.Initialize(drm, kSrcYPropName, plane_props) &&
+      src_w_prop_.Initialize(drm, kSrcWPropName, plane_props) &&
+      src_h_prop_.Initialize(drm, kSrcHPropName, plane_props) &&
+      rotation_prop_.Initialize(drm, kRotationPropName, plane_props);
 
   if (!props_init) {
     LOG(ERROR) << "Unable to get plane properties.";
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h b/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h
index b9c694df..5d58a46 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h
@@ -5,6 +5,7 @@
 #ifndef UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_PLANE_ATOMIC_H_
 #define UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_PLANE_ATOMIC_H_
 
+#include "ui/gfx/overlay_transform.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane.h"
 
 #include <stdint.h>
@@ -28,7 +29,8 @@
                     uint32_t crtc_id,
                     uint32_t framebuffer,
                     const gfx::Rect& crtc_rect,
-                    const gfx::Rect& src_rect);
+                    const gfx::Rect& src_rect,
+                    const gfx::OverlayTransform transform);
 
   void set_crtc(CrtcController* crtc) { crtc_ = crtc; }
   CrtcController* crtc() const { return crtc_; }
@@ -56,6 +58,7 @@
   Property src_y_prop_;
   Property src_w_prop_;
   Property src_h_prop_;
+  Property rotation_prop_;
   CrtcController* crtc_ = nullptr;
 };
 
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
index 17bdd2a..a71335e 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
@@ -46,7 +46,8 @@
       HardwareDisplayPlaneAtomic* atomic_plane =
           static_cast<HardwareDisplayPlaneAtomic*>(plane);
       atomic_plane->SetPlaneData(plane_list->atomic_property_set.get(), 0, 0,
-                                 gfx::Rect(), gfx::Rect());
+                                 gfx::Rect(), gfx::Rect(),
+                                 gfx::OVERLAY_TRANSFORM_NONE);
     }
   }
 
@@ -98,9 +99,9 @@
   uint32_t framebuffer_id = overlay.z_order
                                 ? overlay.buffer->GetFramebufferId()
                                 : overlay.buffer->GetOpaqueFramebufferId();
-  if (!atomic_plane->SetPlaneData(plane_list->atomic_property_set.get(),
-                                  crtc_id, framebuffer_id,
-                                  overlay.display_bounds, src_rect)) {
+  if (!atomic_plane->SetPlaneData(
+          plane_list->atomic_property_set.get(), crtc_id, framebuffer_id,
+          overlay.display_bounds, src_rect, overlay.plane_transform)) {
     LOG(ERROR) << "Failed to set plane properties";
     return false;
   }
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index e83f5b9..c5fdc56 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -876,6 +876,7 @@
     "controls/native/native_view_host_unittest.cc",
     "controls/prefix_selector_unittest.cc",
     "controls/progress_bar_unittest.cc",
+    "controls/resize_area_unittest.cc",
     "controls/scroll_view_unittest.cc",
     "controls/scrollbar/scrollbar_unittest.cc",
     "controls/slider_unittest.cc",
diff --git a/ui/views/controls/resize_area.cc b/ui/views/controls/resize_area.cc
index 3698078..06c0325 100644
--- a/ui/views/controls/resize_area.cc
+++ b/ui/views/controls/resize_area.cc
@@ -14,9 +14,6 @@
 
 const char ResizeArea::kViewClassName[] = "ResizeArea";
 
-////////////////////////////////////////////////////////////////////////////////
-// ResizeArea
-
 ResizeArea::ResizeArea(ResizeAreaDelegate* delegate)
     : delegate_(delegate),
       initial_position_(0) {
@@ -34,17 +31,25 @@
                    : gfx::kNullCursor;
 }
 
+void ResizeArea::OnGestureEvent(ui::GestureEvent* event) {
+  if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
+    SetInitialPosition(event->x());
+    event->SetHandled();
+  } else if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN ||
+             event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
+    ReportResizeAmount(event->x(), false);
+    event->SetHandled();
+  } else if (event->type() == ui::ET_GESTURE_END) {
+    ReportResizeAmount(event->x(), true);
+    event->SetHandled();
+  }
+}
+
 bool ResizeArea::OnMousePressed(const ui::MouseEvent& event) {
   if (!event.IsOnlyLeftMouseButton())
     return false;
 
-  // The resize area obviously will move once you start dragging so we need to
-  // convert coordinates to screen coordinates so that we don't lose our
-  // bearings.
-  gfx::Point point(event.x(), 0);
-  View::ConvertPointToScreen(this, &point);
-  initial_position_ = point.x();
-
+  SetInitialPosition(event.x());
   return true;
 }
 
@@ -76,4 +81,10 @@
                       last_update);
 }
 
+void ResizeArea::SetInitialPosition(int event_x) {
+  gfx::Point point(event_x, 0);
+  View::ConvertPointToScreen(this, &point);
+  initial_position_ = point.x();
+}
+
 }  // namespace views
diff --git a/ui/views/controls/resize_area.h b/ui/views/controls/resize_area.h
index 171c80565..31dc6e3 100644
--- a/ui/views/controls/resize_area.h
+++ b/ui/views/controls/resize_area.h
@@ -14,11 +14,7 @@
 
 class ResizeAreaDelegate;
 
-////////////////////////////////////////////////////////////////////////////////
-//
 // An invisible area that acts like a horizontal resizer.
-//
-////////////////////////////////////////////////////////////////////////////////
 class VIEWS_EXPORT ResizeArea : public View {
  public:
   static const char kViewClassName[];
@@ -26,9 +22,10 @@
   explicit ResizeArea(ResizeAreaDelegate* delegate);
   ~ResizeArea() override;
 
-  // Overridden from views::View:
+  // views::View:
   const char* GetClassName() const override;
   gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override;
+  void OnGestureEvent(ui::GestureEvent* event) override;
   bool OnMousePressed(const ui::MouseEvent& event) override;
   bool OnMouseDragged(const ui::MouseEvent& event) override;
   void OnMouseReleased(const ui::MouseEvent& event) override;
@@ -40,10 +37,16 @@
   // directionality.
   void ReportResizeAmount(int resize_amount, bool last_update);
 
+  // Converts |event_x| to screen coordinates and sets |initial_position_| to
+  // this value.
+  void SetInitialPosition(int event_x);
+
   // The delegate to notify when we have updates.
   ResizeAreaDelegate* delegate_;
 
-  // The mouse position at start (in screen coordinates).
+  // The event's x-position at the start of the resize operation. The resize
+  // area will move while being dragged, so |initial_position_| is represented
+  // in screen coordinates so that we don't lose our bearings.
   int initial_position_;
 
   DISALLOW_COPY_AND_ASSIGN(ResizeArea);
diff --git a/ui/views/controls/resize_area_delegate.h b/ui/views/controls/resize_area_delegate.h
index cc5e133d..ee5e57b 100644
--- a/ui/views/controls/resize_area_delegate.h
+++ b/ui/views/controls/resize_area_delegate.h
@@ -16,7 +16,7 @@
   // positive (depending on direction of dragging and flips according to
   // locale directionality: dragging to the left in LTR locales gives negative
   // |resize_amount| but positive amount for RTL). |done_resizing| is true if
-  // the user has released the mouse.
+  // the user has released the pointer (mouse, stylus, touch, etc.).
   virtual void OnResize(int resize_amount, bool done_resizing) = 0;
 
  protected:
diff --git a/ui/views/controls/resize_area_unittest.cc b/ui/views/controls/resize_area_unittest.cc
new file mode 100644
index 0000000..0dd9107
--- /dev/null
+++ b/ui/views/controls/resize_area_unittest.cc
@@ -0,0 +1,200 @@
+// 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 "ui/views/controls/resize_area.h"
+
+#include <memory>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/aura/window.h"
+#include "ui/events/test/event_generator.h"
+#include "ui/views/controls/resize_area_delegate.h"
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
+
+namespace {
+// Constants used by the ResizeAreaTest.SuccessfulGestureDrag test to simulate
+// a gesture drag by |kGestureScrollDistance| resulting from
+// |kGestureScrollSteps| ui::ET_GESTURE_SCROLL_UPDATE events being delivered.
+const int kGestureScrollDistance = 100;
+const int kGestureScrollSteps = 4;
+const int kDistancePerGestureScrollUpdate =
+    kGestureScrollDistance / kGestureScrollSteps;
+}
+
+namespace views {
+
+// Testing delegate used by ResizeAreaTest.
+class TestResizeAreaDelegate : public ResizeAreaDelegate {
+ public:
+  TestResizeAreaDelegate();
+  ~TestResizeAreaDelegate() override;
+
+  // ResizeAreaDelegate:
+  void OnResize(int resize_amount, bool done_resizing) override;
+
+  int resize_amount() { return resize_amount_; }
+  bool done_resizing() { return done_resizing_; }
+  bool on_resize_called() { return on_resize_called_; }
+
+ private:
+  int resize_amount_;
+  bool done_resizing_;
+  bool on_resize_called_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestResizeAreaDelegate);
+};
+
+TestResizeAreaDelegate::TestResizeAreaDelegate()
+    : resize_amount_(0), done_resizing_(false), on_resize_called_(false) {}
+
+TestResizeAreaDelegate::~TestResizeAreaDelegate() {}
+
+void TestResizeAreaDelegate::OnResize(int resize_amount, bool done_resizing) {
+  resize_amount_ = resize_amount;
+  done_resizing_ = done_resizing;
+  on_resize_called_ = true;
+}
+
+// Test fixture for testing the ResizeArea class.
+class ResizeAreaTest : public ViewsTestBase {
+ public:
+  ResizeAreaTest();
+  ~ResizeAreaTest() override;
+
+  // Callback used by the SuccessfulGestureDrag test.
+  void ProcessGesture(ui::EventType type, const gfx::Vector2dF& delta);
+
+ protected:
+  // testing::Test:
+  void SetUp() override;
+  void TearDown() override;
+
+  ui::test::EventGenerator* event_generator() { return event_generator_.get(); }
+
+  int resize_amount() { return delegate_->resize_amount(); }
+  bool done_resizing() { return delegate_->done_resizing(); }
+  bool on_resize_called() { return delegate_->on_resize_called(); }
+  views::Widget* widget() { return widget_; }
+
+ private:
+  TestResizeAreaDelegate* delegate_ = nullptr;
+  ResizeArea* resize_area_ = nullptr;
+  views::Widget* widget_ = nullptr;
+  std::unique_ptr<ui::test::EventGenerator> event_generator_;
+
+  // The number of ui::ET_GESTURE_SCROLL_UPDATE events seen by
+  // ProcessGesture().
+  int gesture_scroll_updates_seen_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(ResizeAreaTest);
+};
+
+ResizeAreaTest::ResizeAreaTest() {}
+
+ResizeAreaTest::~ResizeAreaTest() {}
+
+void ResizeAreaTest::ProcessGesture(ui::EventType type,
+                                    const gfx::Vector2dF& delta) {
+  if (type == ui::ET_GESTURE_SCROLL_BEGIN) {
+    EXPECT_FALSE(done_resizing());
+    EXPECT_FALSE(on_resize_called());
+  } else if (type == ui::ET_GESTURE_SCROLL_UPDATE) {
+    gesture_scroll_updates_seen_++;
+    EXPECT_EQ(kDistancePerGestureScrollUpdate * gesture_scroll_updates_seen_,
+              resize_amount());
+    EXPECT_FALSE(done_resizing());
+    EXPECT_TRUE(on_resize_called());
+  } else if (type == ui::ET_GESTURE_SCROLL_END) {
+    EXPECT_TRUE(done_resizing());
+  }
+}
+
+void ResizeAreaTest::SetUp() {
+  views::ViewsTestBase::SetUp();
+
+  delegate_ = new TestResizeAreaDelegate;
+  resize_area_ = new ResizeArea(delegate_);
+
+  gfx::Size size(10, 10);
+  resize_area_->SetBounds(0, 0, size.width(), size.height());
+
+  views::Widget::InitParams init_params(
+      CreateParams(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS));
+  init_params.bounds = gfx::Rect(size);
+
+  widget_ = new views::Widget();
+  widget_->Init(init_params);
+  widget_->SetContentsView(resize_area_);
+  widget_->Show();
+
+  event_generator_.reset(
+      new ui::test::EventGenerator(widget_->GetNativeWindow()));
+}
+
+void ResizeAreaTest::TearDown() {
+  if (widget_ && !widget_->IsClosed())
+    widget_->Close();
+
+  views::ViewsTestBase::TearDown();
+}
+
+// TODO(tdanderson): Enable these tests on OSX. See crbug.com/710475.
+#if !defined(OS_MACOSX)
+// Verifies the correct calls have been made to
+// TestResizeAreaDelegate::OnResize() for a sequence of mouse events
+// corresponding to a successful resize operation.
+TEST_F(ResizeAreaTest, SuccessfulMouseDrag) {
+  event_generator()->MoveMouseToCenterOf(widget()->GetNativeView());
+  event_generator()->PressLeftButton();
+
+  const int kFirstDragAmount = -5;
+  event_generator()->MoveMouseBy(kFirstDragAmount, 0);
+  EXPECT_EQ(kFirstDragAmount, resize_amount());
+  EXPECT_FALSE(done_resizing());
+  EXPECT_TRUE(on_resize_called());
+
+  const int kSecondDragAmount = 17;
+  event_generator()->MoveMouseBy(kSecondDragAmount, 0);
+  EXPECT_EQ(kFirstDragAmount + kSecondDragAmount, resize_amount());
+  EXPECT_FALSE(done_resizing());
+
+  event_generator()->ReleaseLeftButton();
+  EXPECT_EQ(kFirstDragAmount + kSecondDragAmount, resize_amount());
+  EXPECT_TRUE(done_resizing());
+}
+
+// Verifies that no resize is performed when attempting to resize using the
+// right mouse button.
+TEST_F(ResizeAreaTest, FailedMouseDrag) {
+  event_generator()->MoveMouseToCenterOf(widget()->GetNativeView());
+  event_generator()->PressRightButton();
+
+  const int kDragAmount = 18;
+  event_generator()->MoveMouseBy(kDragAmount, 0);
+  EXPECT_EQ(0, resize_amount());
+}
+
+// Verifies the correct calls have been made to
+// TestResizeAreaDelegate::OnResize() for a sequence of gesture events
+// corresponding to a successful resize operation.
+TEST_F(ResizeAreaTest, SuccessfulGestureDrag) {
+  gfx::Point start = widget()->GetNativeView()->bounds().CenterPoint();
+  event_generator()->GestureScrollSequenceWithCallback(
+      start, gfx::Point(start.x() + kGestureScrollDistance, start.y()),
+      base::TimeDelta::FromMilliseconds(200), kGestureScrollSteps,
+      base::Bind(&ResizeAreaTest::ProcessGesture, base::Unretained(this)));
+}
+
+// Verifies that no resize is performed on a gesture tap.
+TEST_F(ResizeAreaTest, NoDragOnGestureTap) {
+  event_generator()->GestureTapAt(
+      widget()->GetNativeView()->bounds().CenterPoint());
+
+  EXPECT_EQ(0, resize_amount());
+}
+#endif  // !defined(OS_MACOSX)
+
+}  // namespace views