diff --git a/AUTHORS b/AUTHORS
index 9278d9d..412f738 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -221,7 +221,6 @@
 Franklin Ta <fta2012@gmail.com>
 Frédéric Jacob <frederic.jacob.78@gmail.com>
 Frédéric Wang <fred.wang@free.fr>
-Fu Junwei <junwei.fu@intel.com>
 Gaetano Mendola <mendola@gmail.com>
 Gajendra N <gajendra.n@samsung.com>
 Gajendra Singh <wxjg68@motorola.com>
diff --git a/BUILD.gn b/BUILD.gn
index 613a340..9af0273 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -365,12 +365,6 @@
     ]
   }
 
-  if (is_linux && !is_chromeos && !is_chromecast && !use_ozone) {
-    # TODO(thomasanderson): Remove this once we build using
-    # GTK3 by default. (crbug.com/132847, crbug.com/79722)
-    deps += [ "//chrome/browser/ui/libgtkui:libgtk3ui" ]
-  }
-
   if (use_ozone) {
     deps += [
       "//ui/ozone",
diff --git a/DEPS b/DEPS
index 4c5562a..f5580a8 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': '4b66512a6384d32c1a89396bd4a229c03cce67a0',
+  'skia_revision': 'c43559e6e6bd403c5f3abc46a33e996498eef63f',
   # 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': '73183b7b5de332bdff347b1c9403ae5bb1fb36a7',
+  'v8_revision': 'abb293d2c26943d89c113d3f23baa88ff9830e3f',
   # 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.
@@ -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': 'd31d896231ad698f50692b012810969c4d03f0a9',
+  'catapult_revision': 'fadc168f513f08690b97aea9fa132464bf1648f7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -232,7 +232,7 @@
     Var('chromium_git') + '/native_client/src/third_party/scons-2.0.1.git' + '@' + '1c1550e17fc26355d08627fbdec13d8291227067',
 
   'src/third_party/webrtc':
-    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '149997cfe303770079d5b4887e937f80a2b5c46b', # commit position 16571
+    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '7eb7de80bd811a2c7c9e9da245862298529487ec', # commit position 16606
 
   'src/third_party/openmax_dl':
     Var('chromium_git') + '/external/webrtc/deps/third_party/openmax.git' + '@' +  Var('openmax_dl_revision'),
diff --git a/android_webview/apk/java/proguard.flags b/android_webview/apk/java/proguard.flags
index dbbc1bb..495d4861 100644
--- a/android_webview/apk/java/proguard.flags
+++ b/android_webview/apk/java/proguard.flags
@@ -6,6 +6,10 @@
 # build/android/stacktrace/java_deobfuscate.py to fix the stacktrace up for us.
 -keepnames,allowoptimization class *** { *; }
 
+-keepclassmembers class org.chromium.android_webview.AwPdfExporter {
+    android.view.ViewGroup mContainerView;
+}
+
 # Keep the factory and its public members; it's the main entry point used by the
 # framework.
 -keep class com.android.webview.chromium.WebViewChromiumFactoryProvider {
@@ -16,7 +20,6 @@
     public *;
 }
 
-
 -keep class com.android.webview.chromium.WebViewDatabaseAdapter {
   public *;
 }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentViewClient.java b/android_webview/java/src/org/chromium/android_webview/AwContentViewClient.java
index c258f92..ff33c778 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContentViewClient.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContentViewClient.java
@@ -4,10 +4,15 @@
 
 package org.chromium.android_webview;
 
+import android.content.Context;
+import android.content.Intent;
 import android.view.KeyEvent;
 
+import org.chromium.base.Log;
 import org.chromium.content.browser.ContentViewClient;
 
+import java.net.URISyntaxException;
+
 /**
  * ContentViewClient implementation for WebView
  */
@@ -17,12 +22,40 @@
     private final AwContentsClient mAwContentsClient;
     private final AwSettings mAwSettings;
     private final AwContents mAwContents;
+    private final Context mContext;
 
     public AwContentViewClient(AwContentsClient awContentsClient, AwSettings awSettings,
-            AwContents awContents) {
+            AwContents awContents, Context context) {
         mAwContentsClient = awContentsClient;
         mAwSettings = awSettings;
         mAwContents = awContents;
+        mContext = context;
+    }
+
+    @Override
+    public void onBackgroundColorChanged(int color) {
+        mAwContentsClient.onBackgroundColorChanged(color);
+    }
+
+    @Override
+    public void onStartContentIntent(Context context, String contentUrl, boolean isMainFrame) {
+        // Make sure that this URL is a valid scheme for this callback if interpreted as an intent,
+        // even though we don't dispatch it as an intent here, because many WebView apps will once
+        // it reaches them.
+        String scheme = null;
+        try {
+            Intent intent = Intent.parseUri(contentUrl, Intent.URI_INTENT_SCHEME);
+            scheme = intent.getScheme();
+        } catch (URISyntaxException e) {
+            // Just don't set the scheme, it will be rejected.
+        }
+        if (!isAcceptableContentIntentScheme(scheme)) {
+            Log.w(TAG, "Invalid scheme for URI %s", contentUrl);
+            return;
+        }
+        // Comes from WebViewImpl::detectContentOnTouch in Blink, so must be user-initiated, and
+        // isn't a redirect.
+        mAwContentsClient.shouldIgnoreNavigation(context, contentUrl, isMainFrame, true, false);
     }
 
     @Override
@@ -30,7 +63,7 @@
         if (mAwContentsClient.hasWebViewClient()) {
             // The check below is reflecting Chrome's behavior and is a workaround for
             // http://b/7697782.
-            if (!shouldPropagateKey(event.getKeyCode())) return true;
+            if (!ContentViewClient.shouldPropagateKey(event.getKeyCode())) return true;
             return mAwContentsClient.shouldOverrideKeyEvent(event);
         }
 
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 7c8df9f..83b9150 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -762,7 +762,7 @@
         mAwViewMethods = new AwViewMethodsImpl();
         mFullScreenTransitionsState = new FullScreenTransitionsState(
                 mContainerView, mInternalAccessAdapter, mAwViewMethods);
-        mContentViewClient = new AwContentViewClient(contentsClient, settings, this);
+        mContentViewClient = new AwContentViewClient(contentsClient, settings, this, mContext);
         mLayoutSizer = dependencyFactory.createLayoutSizer();
         mSettings = settings;
         mLayoutSizer.setDelegate(new AwLayoutSizerDelegate());
@@ -1056,7 +1056,7 @@
         mWindowAndroid = getWindowAndroid(mContext);
         mContentViewCore = new ContentViewCore(mContext, PRODUCT_VERSION);
         mViewAndroidDelegate = new AwViewAndroidDelegate(mContainerView,
-                mContentsClient, mContentViewCore.getRenderCoordinates());
+                mContentViewCore.getRenderCoordinates());
         initializeContentViewCore(mContentViewCore, mContext, mViewAndroidDelegate,
                 mInternalAccessAdapter, webContents, new AwGestureStateListener(),
                 mContentViewClient, mWindowAndroid.getWindowAndroid());
diff --git a/android_webview/java/src/org/chromium/android_webview/AwPdfExporter.java b/android_webview/java/src/org/chromium/android_webview/AwPdfExporter.java
index 97f84fb..b22350e9 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwPdfExporter.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwPdfExporter.java
@@ -33,7 +33,9 @@
     // Maintain a reference to the top level object (i.e. WebView) since in a common
     // use case (offscreen webview) application may expect the framework's print manager
     // to own the Webview (via PrintDocumentAdapter).
-    // NOTE: it looks unused, but please do not remove this reference.
+    // NOTE: it looks unused, but please do not remove this reference. There is also a proguard
+    // configuration to prevent this variable to be optimized away. Any name changes should
+    // be reflected there.
     private ViewGroup mContainerView;
 
     AwPdfExporter(ViewGroup containerView) {
diff --git a/android_webview/java/src/org/chromium/android_webview/AwViewAndroidDelegate.java b/android_webview/java/src/org/chromium/android_webview/AwViewAndroidDelegate.java
index d2f26ef6..80753a42 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwViewAndroidDelegate.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwViewAndroidDelegate.java
@@ -4,7 +4,6 @@
 
 package org.chromium.android_webview;
 
-import android.content.Intent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -22,9 +21,6 @@
  * Implementation of the abstract class {@link ViewAndroidDelegate} for WebView.
  */
 public class AwViewAndroidDelegate extends ViewAndroidDelegate {
-    /** Used for logging. */
-    private static final String TAG = "AwVAD";
-
     /**
      * The current container view. This view can be updated with
      * {@link #updateCurrentContainerView()}.
@@ -37,7 +33,6 @@
      */
     private final Map<View, Position> mAnchorViews = new LinkedHashMap<>();
 
-    private final AwContentsClient mContentsClient;
     private final RenderCoordinates mRenderCoordinates;
 
     /**
@@ -64,10 +59,8 @@
     }
 
     @VisibleForTesting
-    public AwViewAndroidDelegate(ViewGroup containerView, AwContentsClient contentsClient,
-            RenderCoordinates renderCoordinates) {
+    public AwViewAndroidDelegate(ViewGroup containerView, RenderCoordinates renderCoordinates) {
         mContainerView = containerView;
-        mContentsClient = contentsClient;
         mRenderCoordinates = renderCoordinates;
     }
 
@@ -144,24 +137,6 @@
     }
 
     @Override
-    public void onBackgroundColorChanged(int color) {
-        mContentsClient.onBackgroundColorChanged(color);
-    }
-
-    @Override
-    public void startContentIntent(Intent intent, String contentUrl, boolean isMainFrame) {
-        // Make sure that this URL is a valid scheme for this callback if interpreted as an intent,
-        // even though we don't dispatch it as an intent here, because many WebView apps will once
-        // it reaches them.
-        assert intent != null;
-
-        // Comes from WebViewImpl::detectContentOnTouch in Blink, so must be user-initiated, and
-        // isn't a redirect.
-        mContentsClient.shouldIgnoreNavigation(mContainerView.getContext(), contentUrl,
-                isMainFrame, true, false);
-    }
-
-    @Override
     public ViewGroup getContainerView() {
         return mContainerView;
     }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsAnchorViewTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsAnchorViewTest.java
index 237eaa565..ea1578e 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsAnchorViewTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsAnchorViewTest.java
@@ -27,7 +27,7 @@
     public void setUp() throws Exception {
         super.setUp();
         mContainerView = new FrameLayout(getActivity());
-        mViewDelegate = new AwViewAndroidDelegate(mContainerView, null, new RenderCoordinates());
+        mViewDelegate = new AwViewAndroidDelegate(mContainerView, new RenderCoordinates());
     }
 
     @Feature({"AndroidWebView"})
diff --git a/android_webview/native/aw_metrics_service_client_impl.cc b/android_webview/native/aw_metrics_service_client_impl.cc
index 3668f26..7566b51 100644
--- a/android_webview/native/aw_metrics_service_client_impl.cc
+++ b/android_webview/native/aw_metrics_service_client_impl.cc
@@ -10,6 +10,7 @@
 #include "base/files/file_util.h"
 #include "base/guid.h"
 #include "base/i18n/rtl.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "components/metrics/call_stack_profile_metrics_provider.h"
 #include "components/metrics/enabled_state_provider.h"
 #include "components/metrics/gpu/gpu_metrics_provider.h"
diff --git a/ash/common/system/tray/system_tray_notifier.h b/ash/common/system/tray/system_tray_notifier.h
index bd65d94..fe2b28c2b4 100644
--- a/ash/common/system/tray/system_tray_notifier.h
+++ b/ash/common/system/tray/system_tray_notifier.h
@@ -9,6 +9,7 @@
 
 #include "ash/ash_export.h"
 #include "ash/common/accessibility_types.h"
+#include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "base/strings/string16.h"
diff --git a/ash/common/wm_shell.cc b/ash/common/wm_shell.cc
index 2053c17..4aa1183 100644
--- a/ash/common/wm_shell.cc
+++ b/ash/common/wm_shell.cc
@@ -54,6 +54,7 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "services/preferences/public/cpp/pref_client_store.h"
 #include "services/preferences/public/interfaces/preferences.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
diff --git a/ash/mus/window_manager.cc b/ash/mus/window_manager.cc
index 03a99b4..dc70051 100644
--- a/ash/mus/window_manager.cc
+++ b/ash/mus/window_manager.cc
@@ -31,6 +31,7 @@
 #include "ash/shell_init_params.h"
 #include "ash/wm/ash_focus_rules.h"
 #include "base/memory/ptr_util.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/ui/common/accelerator_util.h"
 #include "services/ui/common/types.h"
diff --git a/ash/mus/window_manager_application.cc b/ash/mus/window_manager_application.cc
index 0a8abdb..c3dbcf5 100644
--- a/ash/mus/window_manager_application.cc
+++ b/ash/mus/window_manager_application.cc
@@ -13,6 +13,7 @@
 #include "ash/mus/window_manager.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chromeos/audio/cras_audio_handler.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
diff --git a/ash/shell.cc b/ash/shell.cc
index e765b34..f1ecea4f 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -95,6 +95,7 @@
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "base/sys_info.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "base/trace_event/trace_event.h"
 #include "chromeos/audio/audio_a11y_controller.h"
 #include "chromeos/chromeos_switches.h"
diff --git a/base/values.cc b/base/values.cc
index 96e3aa5..e835e008 100644
--- a/base/values.cc
+++ b/base/values.cc
@@ -78,7 +78,7 @@
   auto IsImplemented = [](Value::Type type) {
     return type == Value::Type::NONE || type == Value::Type::BOOLEAN ||
            type == Value::Type::INTEGER || type == Value::Type::DOUBLE ||
-           type == Value::Type::STRING;
+           type == Value::Type::STRING || type == Value::Type::BINARY;
   };
 
   return lhs == rhs || (IsImplemented(lhs) && IsImplemented(rhs));
@@ -91,6 +91,13 @@
   return WrapUnique(new Value(Type::NONE));
 }
 
+// static
+std::unique_ptr<BinaryValue> BinaryValue::CreateWithCopiedBuffer(
+    const char* buffer,
+    size_t size) {
+  return MakeUnique<BinaryValue>(std::vector<char>(buffer, buffer + size));
+}
+
 Value::Value(const Value& that) {
   InternalCopyConstructFrom(that);
 }
@@ -119,10 +126,12 @@
     case Type::STRING:
       string_value_.Init();
       return;
+    case Type::BINARY:
+      binary_value_.Init();
+      return;
 
     // TODO(crbug.com/646113): Implement these once the corresponding derived
     // classes are removed.
-    case Type::BINARY:
     case Type::LIST:
     case Type::DICTIONARY:
       return;
@@ -166,6 +175,14 @@
 
 Value::Value(StringPiece in_string) : Value(in_string.as_string()) {}
 
+Value::Value(const std::vector<char>& in_blob) : type_(Type::BINARY) {
+  binary_value_.Init(in_blob);
+}
+
+Value::Value(std::vector<char>&& in_blob) : type_(Type::BINARY) {
+  binary_value_.Init(std::move(in_blob));
+}
+
 Value& Value::operator=(const Value& that) {
   if (this != &that) {
     DCHECK(IsAssignmentSafe(type_, that.type_));
@@ -229,6 +246,19 @@
   return *string_value_;
 }
 
+const std::vector<char>& Value::GetBlob() const {
+  CHECK(is_blob());
+  return *binary_value_;
+}
+
+size_t Value::GetSize() const {
+  return GetBlob().size();
+}
+
+const char* Value::GetBuffer() const {
+  return GetBlob().data();
+}
+
 bool Value::GetAsBoolean(bool* out_value) const {
   if (out_value && is_bool()) {
     *out_value = bool_value_;
@@ -290,7 +320,11 @@
 }
 
 bool Value::GetAsBinary(const BinaryValue** out_value) const {
-  return false;
+  if (out_value && is_blob()) {
+    *out_value = this;
+    return true;
+  }
+  return is_blob();
 }
 
 bool Value::GetAsList(ListValue** out_value) {
@@ -328,6 +362,10 @@
     // Value when that code is deleted.
     case Type::STRING:
       return new StringValue(*string_value_);
+    // For now, make BinaryValues for backward-compatibility. Convert to
+    // Value when that code is deleted.
+    case Type::BINARY:
+      return new BinaryValue(*binary_value_);
 
     default:
       // All other types should be handled by subclasses.
@@ -355,6 +393,8 @@
       return double_value_ == other->double_value_;
     case Type::STRING:
       return *string_value_ == *(other->string_value_);
+    case Type::BINARY:
+      return *binary_value_ == *(other->binary_value_);
     default:
       // This method should only be getting called for the above types -- all
       // subclasses need to provide their own implementation;.
@@ -405,10 +445,12 @@
     case Type::STRING:
       string_value_.Init(*that.string_value_);
       return;
+    case Type::BINARY:
+      binary_value_.Init(*that.binary_value_);
+      return;
 
     // TODO(crbug.com/646113): Implement these once the corresponding derived
     // classes are removed.
-    case Type::BINARY:
     case Type::LIST:
     case Type::DICTIONARY:
       return;
@@ -429,10 +471,12 @@
     case Type::STRING:
       string_value_.InitFromMove(std::move(that.string_value_));
       return;
+    case Type::BINARY:
+      binary_value_.InitFromMove(std::move(that.binary_value_));
+      return;
 
     // TODO(crbug.com/646113): Implement these once the corresponding derived
     // classes are removed.
-    case Type::BINARY:
     case Type::LIST:
     case Type::DICTIONARY:
       return;
@@ -453,10 +497,12 @@
     case Type::STRING:
       *string_value_ = *that.string_value_;
       return;
+    case Type::BINARY:
+      *binary_value_ = *that.binary_value_;
+      return;
 
     // TODO(crbug.com/646113): Implement these once the corresponding derived
     // classes are removed.
-    case Type::BINARY:
     case Type::LIST:
     case Type::DICTIONARY:
       return;
@@ -477,10 +523,12 @@
     case Type::STRING:
       *string_value_ = std::move(*that.string_value_);
       return;
+    case Type::BINARY:
+      *binary_value_ = std::move(*that.binary_value_);
+      return;
 
     // TODO(crbug.com/646113): Implement these once the corresponding derived
     // classes are removed.
-    case Type::BINARY:
     case Type::LIST:
     case Type::DICTIONARY:
       return;
@@ -499,54 +547,18 @@
     case Type::STRING:
       string_value_.Destroy();
       return;
+    case Type::BINARY:
+      binary_value_.Destroy();
+      return;
 
     // TODO(crbug.com/646113): Implement these once the corresponding derived
     // classes are removed.
-    case Type::BINARY:
     case Type::LIST:
     case Type::DICTIONARY:
       return;
   }
 }
 
-///////////////////// BinaryValue ////////////////////
-
-BinaryValue::BinaryValue() : Value(Type::BINARY), size_(0) {}
-
-BinaryValue::BinaryValue(std::unique_ptr<char[]> buffer, size_t size)
-    : Value(Type::BINARY), buffer_(std::move(buffer)), size_(size) {}
-
-BinaryValue::~BinaryValue() {
-}
-
-// static
-std::unique_ptr<BinaryValue> BinaryValue::CreateWithCopiedBuffer(
-    const char* buffer,
-    size_t size) {
-  std::unique_ptr<char[]> buffer_copy(new char[size]);
-  memcpy(buffer_copy.get(), buffer, size);
-  return MakeUnique<BinaryValue>(std::move(buffer_copy), size);
-}
-
-bool BinaryValue::GetAsBinary(const BinaryValue** out_value) const {
-  if (out_value)
-    *out_value = this;
-  return true;
-}
-
-BinaryValue* BinaryValue::DeepCopy() const {
-  return CreateWithCopiedBuffer(buffer_.get(), size_).release();
-}
-
-bool BinaryValue::Equals(const Value* other) const {
-  if (other->GetType() != GetType())
-    return false;
-  const BinaryValue* other_binary = static_cast<const BinaryValue*>(other);
-  if (other_binary->size_ != size_)
-    return false;
-  return !memcmp(GetBuffer(), other_binary->GetBuffer(), size_);
-}
-
 ///////////////////// DictionaryValue ////////////////////
 
 // static
@@ -768,7 +780,7 @@
     return false;
 
   if (out_value)
-    *out_value = static_cast<const BinaryValue*>(value);
+    *out_value = value;
 
   return true;
 }
@@ -1162,7 +1174,7 @@
     return false;
 
   if (out_value)
-    *out_value = static_cast<const BinaryValue*>(value);
+    *out_value = value;
 
   return true;
 }
diff --git a/base/values.h b/base/values.h
index 5c74eb8..9b4ebef 100644
--- a/base/values.h
+++ b/base/values.h
@@ -36,12 +36,12 @@
 
 namespace base {
 
-class BinaryValue;
 class DictionaryValue;
 class ListValue;
 class Value;
 using FundamentalValue = Value;
 using StringValue = Value;
+using BinaryValue = Value;
 
 // The Value class is the base class for Values. A Value can be instantiated
 // via the Create*Value() factory methods, or by directly creating instances of
@@ -64,6 +64,14 @@
 
   static std::unique_ptr<Value> CreateNullValue();
 
+  // For situations where you want to keep ownership of your buffer, this
+  // factory method creates a new BinaryValue by copying the contents of the
+  // buffer that's passed in.
+  // DEPRECATED, use MakeUnique<Value>(const std::vector<char>&) instead.
+  // TODO(crbug.com/646113): Delete this and migrate callsites.
+  static std::unique_ptr<BinaryValue> CreateWithCopiedBuffer(const char* buffer,
+                                                             size_t size);
+
   Value(const Value& that);
   Value(Value&& that);
   Value();  // A null value.
@@ -85,6 +93,9 @@
   explicit Value(const string16& in_string);
   explicit Value(StringPiece in_string);
 
+  explicit Value(const std::vector<char>& in_blob);
+  explicit Value(std::vector<char>&& in_blob);
+
   Value& operator=(const Value& that);
   Value& operator=(Value&& that);
 
@@ -116,6 +127,10 @@
   int GetInt() const;
   double GetDouble() const;  // Implicitly converts from int if necessary.
   const std::string& GetString() const;
+  const std::vector<char>& GetBlob() const;
+
+  size_t GetSize() const;         // DEPRECATED, use GetBlob().size() instead.
+  const char* GetBuffer() const;  // DEPRECATED, use GetBlob().data() instead.
 
   // These methods allow the convenient retrieval of the contents of the Value.
   // If the current object can be converted into the given type, the value is
@@ -167,44 +182,10 @@
     int int_value_;
     double double_value_;
     ManualConstructor<std::string> string_value_;
+    ManualConstructor<std::vector<char>> binary_value_;
   };
 };
 
-class BASE_EXPORT BinaryValue: public Value {
- public:
-  // Creates a BinaryValue with a null buffer and size of 0.
-  BinaryValue();
-
-  // Creates a BinaryValue, taking ownership of the bytes pointed to by
-  // |buffer|.
-  BinaryValue(std::unique_ptr<char[]> buffer, size_t size);
-
-  ~BinaryValue() override;
-
-  // For situations where you want to keep ownership of your buffer, this
-  // factory method creates a new BinaryValue by copying the contents of the
-  // buffer that's passed in.
-  static std::unique_ptr<BinaryValue> CreateWithCopiedBuffer(const char* buffer,
-                                                             size_t size);
-
-  size_t GetSize() const { return size_; }
-
-  // May return NULL.
-  char* GetBuffer() { return buffer_.get(); }
-  const char* GetBuffer() const { return buffer_.get(); }
-
-  // Overridden from Value:
-  bool GetAsBinary(const BinaryValue** out_value) const override;
-  BinaryValue* DeepCopy() const override;
-  bool Equals(const Value* other) const override;
-
- private:
-  std::unique_ptr<char[]> buffer_;
-  size_t size_;
-
-  DISALLOW_COPY_AND_ASSIGN(BinaryValue);
-};
-
 // DictionaryValue provides a key-value dictionary with (optional) "path"
 // parsing for recursive access; see the comment at the top of the file. Keys
 // are |std::string|s and should be UTF-8 encoded.
diff --git a/base/values_unittest.cc b/base/values_unittest.cc
index 9f2d9d62..e62b6e57 100644
--- a/base/values_unittest.cc
+++ b/base/values_unittest.cc
@@ -9,6 +9,7 @@
 #include <limits>
 #include <memory>
 #include <utility>
+#include <vector>
 
 #include "base/memory/ptr_util.h"
 #include "base/strings/string16.h"
@@ -82,6 +83,12 @@
   EXPECT_EQ("foobar", value.GetString());
 }
 
+TEST(ValuesTest, ConstructBinary) {
+  BinaryValue value(std::vector<char>({0xF, 0x0, 0x0, 0xB, 0xA, 0x2}));
+  EXPECT_EQ(Value::Type::BINARY, value.type());
+  EXPECT_EQ(std::vector<char>({0xF, 0x0, 0x0, 0xB, 0xA, 0x2}), value.GetBlob());
+}
+
 // Group of tests for the copy constructors and copy-assigmnent. For equality
 // checks comparisons of the interesting fields are done instead of relying on
 // Equals being correct.
@@ -146,6 +153,19 @@
   EXPECT_EQ(value.GetString(), blank.GetString());
 }
 
+TEST(ValuesTest, CopyBinary) {
+  BinaryValue value(std::vector<char>({0xF, 0x0, 0x0, 0xB, 0xA, 0x2}));
+  BinaryValue copied_value(value);
+  EXPECT_EQ(value.type(), copied_value.type());
+  EXPECT_EQ(value.GetBlob(), copied_value.GetBlob());
+
+  Value blank;
+
+  blank = value;
+  EXPECT_EQ(value.type(), blank.type());
+  EXPECT_EQ(value.GetBlob(), blank.GetBlob());
+}
+
 // Group of tests for the move constructors and move-assigmnent.
 TEST(ValuesTest, MoveBool) {
   FundamentalValue true_value(true);
@@ -208,6 +228,20 @@
   EXPECT_EQ("foobar", blank.GetString());
 }
 
+TEST(ValuesTest, MoveBinary) {
+  const std::vector<char> buffer = {0xF, 0x0, 0x0, 0xB, 0xA, 0x2};
+  BinaryValue value(buffer);
+  BinaryValue moved_value(std::move(value));
+  EXPECT_EQ(Value::Type::BINARY, moved_value.type());
+  EXPECT_EQ(buffer, moved_value.GetBlob());
+
+  Value blank;
+
+  blank = BinaryValue(buffer);
+  EXPECT_EQ(Value::Type::BINARY, blank.type());
+  EXPECT_EQ(buffer, blank.GetBlob());
+}
+
 TEST(ValuesTest, Basic) {
   // Test basic dictionary getting/setting
   DictionaryValue settings;
@@ -301,16 +335,15 @@
 }
 
 TEST(ValuesTest, BinaryValue) {
-  // Default constructor creates a BinaryValue with a null buffer and size 0.
-  std::unique_ptr<BinaryValue> binary(new BinaryValue());
+  // Default constructor creates a BinaryValue with a buffer of size 0.
+  auto binary = MakeUnique<Value>(Value::Type::BINARY);
   ASSERT_TRUE(binary.get());
-  ASSERT_EQ(NULL, binary->GetBuffer());
   ASSERT_EQ(0U, binary->GetSize());
 
   // Test the common case of a non-empty buffer
-  std::unique_ptr<char[]> buffer(new char[15]);
-  char* original_buffer = buffer.get();
-  binary.reset(new BinaryValue(std::move(buffer), 15));
+  std::vector<char> buffer(15);
+  char* original_buffer = buffer.data();
+  binary.reset(new BinaryValue(std::move(buffer)));
   ASSERT_TRUE(binary.get());
   ASSERT_TRUE(binary->GetBuffer());
   ASSERT_EQ(original_buffer, binary->GetBuffer());
@@ -608,10 +641,9 @@
   StringValue* original_string16 = scoped_string16.get();
   original_dict.Set("string16", std::move(scoped_string16));
 
-  std::unique_ptr<char[]> original_buffer(new char[42]);
-  memset(original_buffer.get(), '!', 42);
+  std::vector<char> original_buffer(42, '!');
   std::unique_ptr<BinaryValue> scoped_binary(
-      new BinaryValue(std::move(original_buffer), 42));
+      new BinaryValue(std::move(original_buffer)));
   BinaryValue* original_binary = scoped_binary.get();
   original_dict.Set("binary", std::move(scoped_binary));
 
@@ -697,13 +729,10 @@
   ASSERT_TRUE(copy_binary);
   ASSERT_NE(copy_binary, original_binary);
   ASSERT_TRUE(copy_binary->IsType(Value::Type::BINARY));
-  ASSERT_NE(original_binary->GetBuffer(),
-    static_cast<BinaryValue*>(copy_binary)->GetBuffer());
-  ASSERT_EQ(original_binary->GetSize(),
-    static_cast<BinaryValue*>(copy_binary)->GetSize());
-  ASSERT_EQ(0, memcmp(original_binary->GetBuffer(),
-               static_cast<BinaryValue*>(copy_binary)->GetBuffer(),
-               original_binary->GetSize()));
+  ASSERT_NE(original_binary->GetBuffer(), copy_binary->GetBuffer());
+  ASSERT_EQ(original_binary->GetSize(), copy_binary->GetSize());
+  ASSERT_EQ(0, memcmp(original_binary->GetBuffer(), copy_binary->GetBuffer(),
+                      original_binary->GetSize()));
 
   Value* copy_value = NULL;
   ASSERT_TRUE(copy_dict->Get("list", &copy_value));
@@ -829,10 +858,9 @@
   Value* original_string16 = scoped_string16.get();
   original_dict.Set("string16", std::move(scoped_string16));
 
-  std::unique_ptr<char[]> original_buffer(new char[42]);
-  memset(original_buffer.get(), '!', 42);
+  std::vector<char> original_buffer(42, '!');
   std::unique_ptr<BinaryValue> scoped_binary(
-      new BinaryValue(std::move(original_buffer), 42));
+      new BinaryValue(std::move(original_buffer)));
   Value* original_binary = scoped_binary.get();
   original_dict.Set("binary", std::move(scoped_binary));
 
@@ -1069,7 +1097,7 @@
   FundamentalValue int_value(1234);
   FundamentalValue double_value(12.34567);
   StringValue string_value("foo");
-  BinaryValue binary_value;
+  BinaryValue binary_value(Value::Type::BINARY);
   DictionaryValue dict_value;
   ListValue list_value;
 
diff --git a/cc/output/color_lut_cache.cc b/cc/output/color_lut_cache.cc
index 612573d..aa22d50 100644
--- a/cc/output/color_lut_cache.cc
+++ b/cc/output/color_lut_cache.cc
@@ -76,7 +76,7 @@
         samples[y].set_y(u * inverse);
         samples[y].set_z(v * inverse);
       }
-      transform->transform(samples.data(), samples.size());
+      transform->Transform(samples.data(), samples.size());
       T* lutp2 = lutp + lut_samples;
       FloatToLUT(reinterpret_cast<float*>(samples.data()), lutp2,
                  lut_samples * 3);
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 2d1e3e8..da28944f 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -198,7 +198,6 @@
     "//services/service_manager/public/interfaces:interfaces_java",
     "//services/service_manager/public/java:service_manager_java",
     "//services/shape_detection/public/interfaces:interfaces_java",
-    "//skia/public/interfaces:interfaces_java",
     "//third_party/WebKit/public:android_mojo_bindings_java",
     "//third_party/WebKit/public:blink_headers_java",
     "//third_party/WebKit/public:mojo_bindings_java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/shapedetection/BarcodeDetectionImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/shapedetection/BarcodeDetectionImpl.java
index f7d8f73..6d46e5f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/shapedetection/BarcodeDetectionImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/shapedetection/BarcodeDetectionImpl.java
@@ -20,10 +20,11 @@
 import org.chromium.gfx.mojom.PointF;
 import org.chromium.gfx.mojom.RectF;
 import org.chromium.mojo.system.MojoException;
+import org.chromium.mojo.system.SharedBufferHandle;
+import org.chromium.mojo.system.SharedBufferHandle.MapFlags;
 import org.chromium.services.service_manager.InterfaceFactory;
 import org.chromium.shape_detection.mojom.BarcodeDetection;
 import org.chromium.shape_detection.mojom.BarcodeDetectionResult;
-import org.chromium.skia.mojom.ColorType;
 
 import java.nio.ByteBuffer;
 
@@ -42,7 +43,8 @@
     }
 
     @Override
-    public void detect(org.chromium.skia.mojom.Bitmap bitmapData, DetectResponse callback) {
+    public void detect(
+            SharedBufferHandle frameData, int width, int height, DetectResponse callback) {
         if (!ExternalAuthUtils.getInstance().canUseGooglePlayServices(
                     mContext, new UserRecoverableErrorHandler.Silent())) {
             Log.e(TAG, "Google Play Services not available");
@@ -59,21 +61,9 @@
             return;
         }
 
-        // TODO(junwei.fu): Consider supporting other bitmap pixel formats,
-        // https://crbug.com/684921.
-        if (bitmapData.colorType != ColorType.RGBA_8888
-                && bitmapData.colorType != ColorType.BGRA_8888) {
-            Log.e(TAG, "Unsupported bitmap pixel format");
-            callback.call(new BarcodeDetectionResult[0]);
-            return;
-        }
-
-        int width = bitmapData.width;
-        int height = bitmapData.height;
         final long numPixels = (long) width * height;
         // TODO(mcasas): https://crbug.com/670028 homogeneize overflow checking.
-        if (bitmapData.pixelData == null || width <= 0 || height <= 0
-                || numPixels > (Long.MAX_VALUE / 4)) {
+        if (!frameData.isValid() || width <= 0 || height <= 0 || numPixels > (Long.MAX_VALUE / 4)) {
             callback.call(new BarcodeDetectionResult[0]);
             return;
         }
@@ -81,7 +71,7 @@
         // Mapping |frameData| will fail if the intended mapped size is larger
         // than its actual capacity, which is limited by the appropriate
         // mojo::edk::Configuration entry.
-        ByteBuffer imageBuffer = ByteBuffer.wrap(bitmapData.pixelData);
+        ByteBuffer imageBuffer = frameData.map(0, numPixels * 4, MapFlags.none());
         if (imageBuffer.capacity() <= 0) {
             callback.call(new BarcodeDetectionResult[0]);
             return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/shapedetection/TextDetectionImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/shapedetection/TextDetectionImpl.java
index 6af95153..2f4fab0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/shapedetection/TextDetectionImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/shapedetection/TextDetectionImpl.java
@@ -18,10 +18,11 @@
 import org.chromium.chrome.browser.externalauth.UserRecoverableErrorHandler;
 import org.chromium.gfx.mojom.RectF;
 import org.chromium.mojo.system.MojoException;
+import org.chromium.mojo.system.SharedBufferHandle;
+import org.chromium.mojo.system.SharedBufferHandle.MapFlags;
 import org.chromium.services.service_manager.InterfaceFactory;
 import org.chromium.shape_detection.mojom.TextDetection;
 import org.chromium.shape_detection.mojom.TextDetectionResult;
-import org.chromium.skia.mojom.ColorType;
 
 import java.nio.ByteBuffer;
 
@@ -40,7 +41,8 @@
     }
 
     @Override
-    public void detect(org.chromium.skia.mojom.Bitmap bitmapData, DetectResponse callback) {
+    public void detect(
+            SharedBufferHandle frameData, int width, int height, DetectResponse callback) {
         if (!ExternalAuthUtils.getInstance().canUseGooglePlayServices(
                     mContext, new UserRecoverableErrorHandler.Silent())) {
             Log.e(TAG, "Google Play Services not available");
@@ -57,21 +59,9 @@
             return;
         }
 
-        // TODO(junwei.fu): Consider supporting other bitmap pixel formats,
-        // https://crbug.com/684921.
-        if (bitmapData.colorType != ColorType.RGBA_8888
-                && bitmapData.colorType != ColorType.BGRA_8888) {
-            Log.e(TAG, "Unsupported bitmap pixel format");
-            callback.call(new TextDetectionResult[0]);
-            return;
-        }
-
-        int width = bitmapData.width;
-        int height = bitmapData.height;
         final long numPixels = (long) width * height;
         // TODO(xianglu): https://crbug.com/670028 homogeneize overflow checking.
-        if (bitmapData.pixelData == null || width <= 0 || height <= 0
-                || numPixels > (Long.MAX_VALUE / 4)) {
+        if (!frameData.isValid() || width <= 0 || height <= 0 || numPixels > (Long.MAX_VALUE / 4)) {
             callback.call(new TextDetectionResult[0]);
             return;
         }
@@ -79,7 +69,7 @@
         // Mapping |frameData| will fail if the intended mapped size is larger
         // than its actual capacity, which is limited by the appropriate
         // mojo::edk::Configuration entry.
-        ByteBuffer imageBuffer = ByteBuffer.wrap(bitmapData.pixelData);
+        ByteBuffer imageBuffer = frameData.map(0, numPixels * 4, MapFlags.none());
         if (imageBuffer.capacity() <= 0) {
             callback.call(new TextDetectionResult[0]);
             return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
index 2f573f618..a786a45 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -106,6 +106,7 @@
 import org.chromium.printing.PrintingControllerImpl;
 import org.chromium.ui.base.LocalizationUtils;
 import org.chromium.ui.base.PageTransition;
+import org.chromium.ui.base.ViewAndroidDelegate;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.mojom.WindowOpenDisposition;
 
@@ -375,6 +376,26 @@
 
     private class TabContentViewClient extends ContentViewClient {
         @Override
+        public void onBackgroundColorChanged(int color) {
+            Tab.this.onBackgroundColorChanged(color);
+        }
+
+        @Override
+        public void onTopControlsChanged(float topControlsOffsetY, float topContentOffsetY) {
+            super.onTopControlsChanged(topControlsOffsetY, topContentOffsetY);
+            onOffsetsChanged(topControlsOffsetY, mPreviousBottomControlsOffsetY,
+                    topContentOffsetY, isShowingSadTab());
+        }
+
+        @Override
+        public void onBottomControlsChanged(float bottomControlsOffsetY,
+                float bottomContentOffsetY) {
+            super.onBottomControlsChanged(bottomControlsOffsetY, bottomContentOffsetY);
+            onOffsetsChanged(mPreviousTopControlsOffsetY, bottomControlsOffsetY,
+                    mPreviousContentOffsetY, isShowingSadTab());
+        }
+
+        @Override
         public void onImeEvent() {
             // Some text was set in the page. Don't reuse it if a tab is
             // open from the same external application, we might lose some
@@ -389,6 +410,33 @@
         }
 
         @Override
+        public int getSystemWindowInsetLeft() {
+            ChromeActivity activity = getActivity();
+            if (activity != null && activity.getInsetObserverView() != null) {
+                return activity.getInsetObserverView().getSystemWindowInsetsLeft();
+            }
+            return 0;
+        }
+
+        @Override
+        public int getSystemWindowInsetTop() {
+            ChromeActivity activity = getActivity();
+            if (activity != null && activity.getInsetObserverView() != null) {
+                return activity.getInsetObserverView().getSystemWindowInsetsTop();
+            }
+            return 0;
+        }
+
+        @Override
+        public int getSystemWindowInsetRight() {
+            ChromeActivity activity = getActivity();
+            if (activity != null && activity.getInsetObserverView() != null) {
+                return activity.getInsetObserverView().getSystemWindowInsetsRight();
+            }
+            return 0;
+        }
+
+        @Override
         public int getSystemWindowInsetBottom() {
             ChromeActivity activity = getActivity();
             if (activity != null && activity.getInsetObserverView() != null) {
@@ -1594,7 +1642,7 @@
         ContentView cv = ContentView.createContentView(mThemedApplicationContext, cvc);
         cv.setContentDescription(mThemedApplicationContext.getResources().getString(
                 R.string.accessibility_content_view));
-        cvc.initialize(new TabViewAndroidDelegate(this, cv), cv, webContents,
+        cvc.initialize(ViewAndroidDelegate.createBasicDelegate(cv), cv, webContents,
                 getWindowAndroid());
         ChromeActionModeCallback actionModeCallback = new ChromeActionModeCallback(
                 mThemedApplicationContext, this, cvc.getActionModeCallbackHelper());
@@ -2173,7 +2221,7 @@
      * Called when the background color for the content changes.
      * @param color The current for the background.
      */
-    void onBackgroundColorChanged(int color) {
+    protected void onBackgroundColorChanged(int color) {
         for (TabObserver observer : mObservers) observer.onBackgroundColorChanged(this, color);
     }
 
@@ -2473,21 +2521,19 @@
      * Called when offset values related with fullscreen functionality has been changed by the
      * compositor.
      * @param topControlsOffsetY The Y offset of the top controls in physical pixels.
-     *    {@code Float.NaN} if the value is invalid and the cached value should be used.
      * @param bottomControlsOffsetY The Y offset of the bottom controls in physical pixels.
-     *    {@code Float.NaN} if the value is invalid and the cached value should be used.
      * @param contentOffsetY The Y offset of the content in physical pixels.
+     * @param isNonFullscreenPage Whether a current page is non-fullscreen page or not.
      */
-    void onOffsetsChanged(
-            float topControlsOffsetY, float bottomControlsOffsetY, float contentOffsetY) {
-        if (!Float.isNaN(topControlsOffsetY)) mPreviousTopControlsOffsetY = topControlsOffsetY;
-        if (!Float.isNaN(bottomControlsOffsetY)) {
-            mPreviousBottomControlsOffsetY = bottomControlsOffsetY;
-        }
+    private void onOffsetsChanged(
+            float topControlsOffsetY, float bottomControlsOffsetY, float contentOffsetY,
+            boolean isNonFullscreenPage) {
+        mPreviousTopControlsOffsetY = topControlsOffsetY;
+        mPreviousBottomControlsOffsetY = bottomControlsOffsetY;
         mPreviousContentOffsetY = contentOffsetY;
 
         if (mFullscreenManager == null) return;
-        if (isShowingSadTab() || isNativePage()) {
+        if (isNonFullscreenPage || isNativePage()) {
             mFullscreenManager.setPositionsForTabToNonFullscreen();
         } else {
             mFullscreenManager.setPositionsForTab(topControlsOffsetY, bottomControlsOffsetY,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegate.java
deleted file mode 100644
index e4689e0e..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegate.java
+++ /dev/null
@@ -1,59 +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.
-
-package org.chromium.chrome.browser.tab;
-
-import android.content.ActivityNotFoundException;
-import android.content.Intent;
-import android.view.ViewGroup;
-
-import org.chromium.base.Log;
-import org.chromium.base.metrics.RecordUserAction;
-import org.chromium.ui.base.ViewAndroidDelegate;
-
-/**
- * Implementation of the abstract class {@link ViewAndroidDelegate} for Chrome.
- */
-class TabViewAndroidDelegate extends ViewAndroidDelegate {
-    /** Used for logging. */
-    private static final String TAG = "TabVAD";
-
-    private final Tab mTab;
-    private final ViewGroup mContainerView;
-
-    TabViewAndroidDelegate(Tab tab, ViewGroup containerView) {
-        mTab = tab;
-        mContainerView = containerView;
-    }
-
-    @Override
-    public void onBackgroundColorChanged(int color) {
-        mTab.onBackgroundColorChanged(color);
-    }
-
-    @Override
-    public void onTopControlsChanged(float topControlsOffsetY, float topContentOffsetY) {
-        mTab.onOffsetsChanged(topControlsOffsetY, Float.NaN, topContentOffsetY);
-    }
-
-    @Override
-    public void onBottomControlsChanged(float bottomControlsOffsetY, float bottomContentOffsetY) {
-        mTab.onOffsetsChanged(Float.NaN, bottomControlsOffsetY, Float.NaN);
-    }
-
-    @Override
-    public void startContentIntent(Intent intent, String intentUrl, boolean isMainFrame) {
-        try {
-            RecordUserAction.record("Android.ContentDetectorActivated");
-            mContainerView.getContext().startActivity(intent);
-        } catch (ActivityNotFoundException ex) {
-            Log.w(TAG, "No application can handle %s", intentUrl);
-        }
-    }
-
-    @Override
-    public ViewGroup getContainerView() {
-        return mContainerView;
-    }
-}
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index a7ee4e5b..87fb0bc 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -960,7 +960,6 @@
   "java/src/org/chromium/chrome/browser/tab/TabRedirectHandler.java",
   "java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java",
   "java/src/org/chromium/chrome/browser/tab/TabUma.java",
-  "java/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegate.java",
   "java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java",
   "java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java",
   "java/src/org/chromium/chrome/browser/tabmodel/AsyncTabParams.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/DocumentModeAssassinTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/DocumentModeAssassinTest.java
index ead2316a..a5a3b43 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/DocumentModeAssassinTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/DocumentModeAssassinTest.java
@@ -16,6 +16,7 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.AdvancedMockContext;
 import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.chrome.browser.TabState;
 import org.chromium.chrome.browser.tab.Tab;
@@ -179,7 +180,9 @@
     }
 
     /** Tests the full pathway. */
-    @MediumTest
+    // Flaky, see http://crbug/666815.
+    // @MediumTest
+    @DisabledTest
     public void testFullMigration() throws Exception {
         final CallbackHelper copyStartedCallback = new CallbackHelper();
         final CallbackHelper copyCallback = new CallbackHelper();
diff --git a/chrome/browser/android/logo_service.cc b/chrome/browser/android/logo_service.cc
index 54b8b78..ae0796a 100644
--- a/chrome/browser/android/logo_service.cc
+++ b/chrome/browser/android/logo_service.cc
@@ -6,6 +6,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/image_decoder.h"
 #include "chrome/browser/profiles/profile.h"
diff --git a/chrome/browser/android/metrics/uma_session_stats.cc b/chrome/browser/android/metrics/uma_session_stats.cc
index c318ddb..854f9fc 100644
--- a/chrome/browser/android/metrics/uma_session_stats.cc
+++ b/chrome/browser/android/metrics/uma_session_stats.cc
@@ -9,6 +9,7 @@
 #include "base/command_line.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "base/time/time.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
diff --git a/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.cc b/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.cc
index 992702b6..81af5aed 100644
--- a/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.cc
+++ b/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.cc
@@ -11,6 +11,7 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/sequenced_task_runner.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "chrome/browser/android/offline_pages/background_loader_offliner.h"
 #include "chrome/browser/android/offline_pages/background_scheduler_bridge.h"
 #include "chrome/browser/android/offline_pages/downloads/offline_page_notification_bridge.h"
diff --git a/chrome/browser/android/offline_pages/offline_page_model_factory.cc b/chrome/browser/android/offline_pages/offline_page_model_factory.cc
index 2201011..089b558 100644
--- a/chrome/browser/android/offline_pages/offline_page_model_factory.cc
+++ b/chrome/browser/android/offline_pages/offline_page_model_factory.cc
@@ -9,6 +9,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/singleton.h"
 #include "base/sequenced_task_runner.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_constants.h"
diff --git a/chrome/browser/android/offline_pages/offline_page_request_job.cc b/chrome/browser/android/offline_pages/offline_page_request_job.cc
index d327a5ae4..f7a9535b 100644
--- a/chrome/browser/android/offline_pages/offline_page_request_job.cc
+++ b/chrome/browser/android/offline_pages/offline_page_request_job.cc
@@ -11,6 +11,7 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/android/offline_pages/offline_page_model_factory.h"
 #include "chrome/browser/android/offline_pages/offline_page_tab_helper.h"
diff --git a/chrome/browser/android/offline_pages/request_coordinator_factory.cc b/chrome/browser/android/offline_pages/request_coordinator_factory.cc
index d1d1141..2648078e 100644
--- a/chrome/browser/android/offline_pages/request_coordinator_factory.cc
+++ b/chrome/browser/android/offline_pages/request_coordinator_factory.cc
@@ -9,6 +9,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/singleton.h"
 #include "base/sequenced_task_runner.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "chrome/browser/android/offline_pages/background_loader_offliner.h"
 #include "chrome/browser/android/offline_pages/background_scheduler_bridge.h"
 #include "chrome/browser/android/offline_pages/downloads/offline_page_notification_bridge.h"
diff --git a/chrome/browser/android/shortcut_helper.cc b/chrome/browser/android/shortcut_helper.cc
index dfa0aefc..aa309f6 100644
--- a/chrome/browser/android/shortcut_helper.cc
+++ b/chrome/browser/android/shortcut_helper.cc
@@ -15,6 +15,7 @@
 #include "base/command_line.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "chrome/browser/android/webapk/chrome_webapk_host.h"
 #include "chrome/browser/android/webapk/webapk_install_service.h"
 #include "chrome/browser/manifest/manifest_icon_downloader.h"
diff --git a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc
index 9f7d15a..1b2078a 100644
--- a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc
+++ b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc
@@ -10,6 +10,7 @@
 #include "base/callback.h"
 #include "base/location.h"
 #include "base/strings/string16.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "chrome/browser/android/offline_pages/offline_page_utils.h"
 #include "chrome/browser/android/shortcut_helper.h"
 #include "chrome/browser/android/webapk/webapk_web_manifest_checker.h"
diff --git a/chrome/browser/android/webapps/add_to_homescreen_manager.cc b/chrome/browser/android/webapps/add_to_homescreen_manager.cc
index 9d4dc4f..45b0459e 100644
--- a/chrome/browser/android/webapps/add_to_homescreen_manager.cc
+++ b/chrome/browser/android/webapps/add_to_homescreen_manager.cc
@@ -11,6 +11,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "chrome/browser/android/banners/app_banner_infobar_delegate_android.h"
 #include "chrome/browser/android/shortcut_helper.h"
 #include "chrome/browser/android/webapk/chrome_webapk_host.h"
diff --git a/chrome/browser/chromeos/accessibility/accessibility_highlight_manager_interactive_uitest.cc b/chrome/browser/chromeos/accessibility/accessibility_highlight_manager_interactive_uitest.cc
index 806d059..5511e7ea 100644
--- a/chrome/browser/chromeos/accessibility/accessibility_highlight_manager_interactive_uitest.cc
+++ b/chrome/browser/chromeos/accessibility/accessibility_highlight_manager_interactive_uitest.cc
@@ -8,6 +8,7 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/run_loop.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "chrome/browser/chromeos/ui/accessibility_focus_ring_controller.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chromeos/chromeos_switches.h"
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc b/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc
index ec64b6c8..fde4eed4 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc
@@ -19,6 +19,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/sys_info.h"
 #include "base/task_scheduler/post_task.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "base/version.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/app_mode/app_session.h"
diff --git a/chrome/browser/chromeos/arc/arc_service_launcher.cc b/chrome/browser/chromeos/arc/arc_service_launcher.cc
index 782625e..52541dbe 100644
--- a/chrome/browser/chromeos/arc/arc_service_launcher.cc
+++ b/chrome/browser/chromeos/arc/arc_service_launcher.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.h"
 #include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.h"
 #include "chrome/browser/chromeos/arc/arc_auth_service.h"
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_file_stream_reader.cc b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_file_stream_reader.cc
index 426b51307..401d439 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_file_stream_reader.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_file_stream_reader.cc
@@ -8,6 +8,7 @@
 #include <unistd.h>
 
 #include "base/files/file.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "base/threading/thread_restrictions.h"
 #include "chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_util.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/chromeos/arc/process/arc_process_service.cc b/chrome/browser/chromeos/arc/process/arc_process_service.cc
index 9de7760..ec6e417 100644
--- a/chrome/browser/chromeos/arc/process/arc_process_service.cc
+++ b/chrome/browser/chromeos/arc/process/arc_process_service.cc
@@ -21,6 +21,7 @@
 #include "base/process/process.h"
 #include "base/process/process_iterator.h"
 #include "base/task_runner_util.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "base/trace_event/trace_event.h"
 #include "components/arc/arc_bridge_service.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/chromeos/base/file_flusher_unittest.cc b/chrome/browser/chromeos/base/file_flusher_unittest.cc
index a038cea..361d489 100644
--- a/chrome/browser/chromeos/base/file_flusher_unittest.cc
+++ b/chrome/browser/chromeos/base/file_flusher_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/chromeos/customization/customization_wallpaper_downloader.cc b/chrome/browser/chromeos/customization/customization_wallpaper_downloader.cc
index 180f529..7fcbf30 100644
--- a/chrome/browser/chromeos/customization/customization_wallpaper_downloader.cc
+++ b/chrome/browser/chromeos/customization/customization_wallpaper_downloader.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/files/file_util.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_status_code.h"
diff --git a/chrome/browser/chromeos/external_metrics.cc b/chrome/browser/chromeos/external_metrics.cc
index 78f071e1..5ca83bc 100644
--- a/chrome/browser/chromeos/external_metrics.cc
+++ b/chrome/browser/chromeos/external_metrics.cc
@@ -13,6 +13,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/sparse_histogram.h"
 #include "base/metrics/statistics_recorder.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/metrics/chromeos_metrics_provider.h"
 #include "components/metrics/metrics_service.h"
diff --git a/chrome/browser/chromeos/file_manager/snapshot_manager.cc b/chrome/browser/chromeos/file_manager/snapshot_manager.cc
index f33fdfdf9..03c11a75 100644
--- a/chrome/browser/chromeos/file_manager/snapshot_manager.cc
+++ b/chrome/browser/chromeos/file_manager/snapshot_manager.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/sys_info.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "chrome/browser/chromeos/file_manager/app_id.h"
 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
 #include "chrome/browser/profiles/profile.h"
diff --git a/chrome/browser/chromeos/input_method/input_method_syncer.cc b/chrome/browser/chromeos/input_method/input_method_syncer.cc
index 0140796a..d27002a 100644
--- a/chrome/browser/chromeos/input_method/input_method_syncer.cc
+++ b/chrome/browser/chromeos/input_method/input_method_syncer.cc
@@ -13,6 +13,7 @@
 #include "base/strings/string_util.h"
 #include "base/task_runner.h"
 #include "base/task_runner_util.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/common/pref_names.h"
 #include "components/pref_registry/pref_registry_syncable.h"
diff --git a/chrome/browser/chromeos/login/login_browsertest.cc b/chrome/browser/chromeos/login/login_browsertest.cc
index f7d9206a..b1dd99b5 100644
--- a/chrome/browser/chromeos/login/login_browsertest.cc
+++ b/chrome/browser/chromeos/login/login_browsertest.cc
@@ -254,7 +254,8 @@
   CrosSettings::Get()->SetBoolean(kAccountsPrefShowUserNamesOnSignIn, false);
 }
 
-IN_PROC_BROWSER_TEST_F(LoginTest, GaiaAuthOffline) {
+// Flaky, see http://crbug/692364.
+IN_PROC_BROWSER_TEST_F(LoginTest, DISABLED_GaiaAuthOffline) {
   PrepareOfflineLogin();
   content::WindowedNotificationObserver session_start_waiter(
       chrome::NOTIFICATION_SESSION_STARTED,
diff --git a/chrome/browser/chromeos/login/screenshot_testing/screenshot_tester.cc b/chrome/browser/chromeos/login/screenshot_testing/screenshot_tester.cc
index 92dc8336..448d48a3 100644
--- a/chrome/browser/chromeos/login/screenshot_testing/screenshot_tester.cc
+++ b/chrome/browser/chromeos/login/screenshot_testing/screenshot_tester.cc
@@ -11,6 +11,7 @@
 #include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/run_loop.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "chrome/browser/chromeos/login/screenshot_testing/SkDiffPixelsMetric.h"
 #include "chrome/browser/chromeos/login/screenshot_testing/SkImageDiffer.h"
 #include "chrome/browser/chromeos/login/screenshot_testing/SkPMetric.h"
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index 8d7eee48..8e5aca8 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -21,6 +21,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
diff --git a/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc b/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc
index fcf6a66..3d9977d 100644
--- a/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc
+++ b/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc
@@ -16,6 +16,7 @@
 #include "base/callback.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "base/threading/thread_checker.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h"
diff --git a/chrome/browser/chromeos/policy/device_status_collector.cc b/chrome/browser/chromeos/policy/device_status_collector.cc
index c0b5512..77638a0 100644
--- a/chrome/browser/chromeos/policy/device_status_collector.cc
+++ b/chrome/browser/chromeos/policy/device_status_collector.cc
@@ -27,6 +27,7 @@
 #include "base/sys_info.h"
 #include "base/task_runner_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "base/values.h"
 #include "base/version.h"
 #include "chrome/browser/browser_process.h"
diff --git a/chrome/browser/chromeos/policy/dm_token_storage.cc b/chrome/browser/chromeos/policy/dm_token_storage.cc
index b3baeeb..99a4acb 100644
--- a/chrome/browser/chromeos/policy/dm_token_storage.cc
+++ b/chrome/browser/chromeos/policy/dm_token_storage.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/chromeos/policy/dm_token_storage.h"
 
 #include "base/bind.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "chrome/browser/chromeos/settings/token_encryptor.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/cryptohome/system_salt_getter.h"
diff --git a/chrome/browser/chromeos/policy/system_log_uploader.cc b/chrome/browser/chromeos/policy/system_log_uploader.cc
index 8f53545..f472a2e4 100644
--- a/chrome/browser/chromeos/policy/system_log_uploader.cc
+++ b/chrome/browser/chromeos/policy/system_log_uploader.cc
@@ -13,6 +13,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/syslog_logging.h"
 #include "base/task_runner_util.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/policy/upload_job_impl.h"
 #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
diff --git a/chrome/browser/chromeos/system_logs/debug_log_writer.cc b/chrome/browser/chromeos/system_logs/debug_log_writer.cc
index 4ac2f6c2..9c8a2271 100644
--- a/chrome/browser/chromeos/system_logs/debug_log_writer.cc
+++ b/chrome/browser/chromeos/system_logs/debug_log_writer.cc
@@ -16,6 +16,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/process/kill.h"
 #include "base/process/launch.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "chrome/common/logging_chrome.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/debug_daemon_client.h"
diff --git a/chrome/browser/chromeos/system_logs/touch_log_source_ozone.cc b/chrome/browser/chromeos/system_logs/touch_log_source_ozone.cc
index b2e04b2..6a76479 100644
--- a/chrome/browser/chromeos/system_logs/touch_log_source_ozone.cc
+++ b/chrome/browser/chromeos/system_logs/touch_log_source_ozone.cc
@@ -17,6 +17,7 @@
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
 #include "base/process/launch.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "components/feedback/feedback_util.h"
 #include "content/public/browser/browser_thread.h"
 #include "ui/ozone/public/input_controller.h"
diff --git a/chrome/browser/extensions/api/messaging/native_message_process_host.cc b/chrome/browser/extensions/api/messaging/native_message_process_host.cc
index 7d4304e..bf2c37c 100644
--- a/chrome/browser/extensions/api/messaging/native_message_process_host.cc
+++ b/chrome/browser/extensions/api/messaging/native_message_process_host.cc
@@ -12,6 +12,7 @@
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/process/kill.h"
+#include "base/task_scheduler/post_task.h"
 #include "base/threading/sequenced_worker_pool.h"
 #include "build/build_config.h"
 #include "chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h"
@@ -70,8 +71,9 @@
     // On OSX base::EnsureProcessTerminated() may block, so we have to post a
     // task on the blocking pool.
 #if defined(OS_MACOSX)
-    content::BrowserThread::PostBlockingPoolTask(
-        FROM_HERE,
+    base::PostTaskWithTraits(
+        FROM_HERE, base::TaskTraits().MayBlock().WithPriority(
+                       base::TaskPriority::BACKGROUND),
         base::Bind(&base::EnsureProcessTerminated, Passed(&process_)));
 #else
     base::EnsureProcessTerminated(std::move(process_));
diff --git a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
index 05dc3ab..21d28b5 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/task_runner_util.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "base/values.h"
 #include "chrome/browser/chromeos/arc/arc_support_host.h"
 #include "chrome/browser/extensions/extension_service.h"
diff --git a/chrome/browser/ui/ash/ash_init.cc b/chrome/browser/ui/ash/ash_init.cc
index be79194e..b29a791 100644
--- a/chrome/browser/ui/ash/ash_init.cc
+++ b/chrome/browser/ui/ash/ash_init.cc
@@ -15,6 +15,7 @@
 #include "ash/shell_init_params.h"
 #include "base/command_line.h"
 #include "base/sys_info.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
diff --git a/chrome/browser/ui/ash/chrome_screenshot_grabber.cc b/chrome/browser/ui/ash/chrome_screenshot_grabber.cc
index 9ba9b8a..cc3cbb0 100644
--- a/chrome/browser/ui/ash/chrome_screenshot_grabber.cc
+++ b/chrome/browser/ui/ash/chrome_screenshot_grabber.cc
@@ -17,6 +17,7 @@
 #include "base/macros.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/drive/file_system_util.h"
diff --git a/chrome/browser/ui/libgtkui/BUILD.gn b/chrome/browser/ui/libgtkui/BUILD.gn
index 849f289..9b50cbc 100644
--- a/chrome/browser/ui/libgtkui/BUILD.gn
+++ b/chrome/browser/ui/libgtkui/BUILD.gn
@@ -140,26 +140,26 @@
   }
 }
 
-libgtkui("libgtk2ui") {
-  sources = [
-    "native_theme_gtk2.cc",
-    "native_theme_gtk2.h",
-  ]
-  deps = [
-    "//build/config/linux/gtk2",
-    "//build/config/linux/gtk2:gtkprint2",
-  ]
-}
-
-# This is compiled with "all" even when it's not referenced to ensure that
-# GTK3 continues to build. GTK3 is explicitly specified by some distros.
-libgtkui("libgtk3ui") {
-  sources = [
-    "native_theme_gtk3.cc",
-    "native_theme_gtk3.h",
-  ]
-  deps = [
-    "//build/config/linux/gtk3",
-    "//build/config/linux/gtk3:gtkprint3",
-  ]
+if (use_gtk3) {
+  libgtkui("libgtk3ui") {
+    sources = [
+      "native_theme_gtk3.cc",
+      "native_theme_gtk3.h",
+    ]
+    deps = [
+      "//build/config/linux/gtk3",
+      "//build/config/linux/gtk3:gtkprint3",
+    ]
+  }
+} else {
+  libgtkui("libgtk2ui") {
+    sources = [
+      "native_theme_gtk2.cc",
+      "native_theme_gtk2.h",
+    ]
+    deps = [
+      "//build/config/linux/gtk2",
+      "//build/config/linux/gtk2:gtkprint2",
+    ]
+  }
 }
diff --git a/chrome/renderer/extensions/cast_streaming_native_handler.h b/chrome/renderer/extensions/cast_streaming_native_handler.h
index 25cc556..946099fd 100644
--- a/chrome/renderer/extensions/cast_streaming_native_handler.h
+++ b/chrome/renderer/extensions/cast_streaming_native_handler.h
@@ -17,8 +17,9 @@
 class CastUdpTransport;
 
 namespace base {
-class BinaryValue;
 class DictionaryValue;
+class Value;
+using BinaryValue = Value;
 }
 
 namespace net {
diff --git a/chrome/renderer/media/cast_rtp_stream.h b/chrome/renderer/media/cast_rtp_stream.h
index d03d313..d867c73 100644
--- a/chrome/renderer/media/cast_rtp_stream.h
+++ b/chrome/renderer/media/cast_rtp_stream.h
@@ -18,8 +18,9 @@
 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
 
 namespace base {
-class BinaryValue;
 class DictionaryValue;
+class Value;
+using BinaryValue = Value;
 }
 
 class CastAudioSink;
diff --git a/chrome/renderer/media/cast_session.h b/chrome/renderer/media/cast_session.h
index d1bfdec..132af169 100644
--- a/chrome/renderer/media/cast_session.h
+++ b/chrome/renderer/media/cast_session.h
@@ -15,9 +15,10 @@
 #include "net/base/ip_endpoint.h"
 
 namespace base {
-class BinaryValue;
 class DictionaryValue;
 class SingleThreadTaskRunner;
+class Value;
+using BinaryValue = Value;
 }  // namespace base
 
 namespace media {
diff --git a/chrome/renderer/media/cast_session_delegate.cc b/chrome/renderer/media/cast_session_delegate.cc
index b76f34c..1216f2a 100644
--- a/chrome/renderer/media/cast_session_delegate.cc
+++ b/chrome/renderer/media/cast_session_delegate.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <utility>
+#include <vector>
 
 #include "base/callback_helpers.h"
 #include "base/lazy_instance.h"
@@ -207,14 +208,14 @@
   DCHECK(io_task_runner_->BelongsToCurrentThread());
 
   if (!event_subscribers_.get()) {
-    callback.Run(base::MakeUnique<base::BinaryValue>());
+    callback.Run(base::MakeUnique<base::Value>(base::Value::Type::BINARY));
     return;
   }
 
   media::cast::EncodingEventSubscriber* subscriber =
       event_subscribers_->GetEncodingEventSubscriber(is_audio);
   if (!subscriber) {
-    callback.Run(base::MakeUnique<base::BinaryValue>());
+    callback.Run(base::MakeUnique<base::Value>(base::Value::Type::BINARY));
     return;
   }
 
@@ -245,14 +246,14 @@
 
   if (!success) {
     DVLOG(2) << "Failed to serialize event log.";
-    callback.Run(base::MakeUnique<base::BinaryValue>());
+    callback.Run(base::MakeUnique<base::Value>(base::Value::Type::BINARY));
     return;
   }
 
   DVLOG(2) << "Serialized log length: " << output_bytes;
 
-  std::unique_ptr<base::BinaryValue> blob(
-      new base::BinaryValue(std::move(serialized_log), output_bytes));
+  auto blob = base::MakeUnique<base::BinaryValue>(std::vector<char>(
+      serialized_log.get(), serialized_log.get() + output_bytes));
   callback.Run(std::move(blob));
 }
 
diff --git a/chrome/renderer/media/cast_session_delegate.h b/chrome/renderer/media/cast_session_delegate.h
index 9d2434f..56c4b6e 100644
--- a/chrome/renderer/media/cast_session_delegate.h
+++ b/chrome/renderer/media/cast_session_delegate.h
@@ -20,9 +20,10 @@
 #include "media/cast/logging/logging_defines.h"
 
 namespace base {
-class BinaryValue;
 class DictionaryValue;
 class SingleThreadTaskRunner;
+class Value;
+using BinaryValue = Value;
 }  // namespace base
 
 namespace media {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 604ce853..1d4129d 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1037,35 +1037,41 @@
       "data/webui/print_preview.js",
       "data/webui/print_preview_destination_search_test.js",
       "data/webui/sandboxstatus_browsertest.js",
-      "data/webui/settings/advanced_page_browsertest.js",
-      "data/webui/settings/animation_browsertest.js",
-      "data/webui/settings/basic_page_browsertest.js",
-      "data/webui/settings/cr_settings_browsertest.js",
-      "data/webui/settings/easy_unlock_browsertest_chromeos.js",
-      "data/webui/settings/help_page_browsertest.js",
-      "data/webui/settings/languages_page_browsertest.js",
-      "data/webui/settings/on_startup_browsertest.js",
-      "data/webui/settings/passwords_and_autofill_fake_data.js",
-      "data/webui/settings/passwords_and_forms_browsertest.js",
-      "data/webui/settings/settings_autofill_section_browsertest.js",
-      "data/webui/settings/settings_idle_render_browsertest.js",
-      "data/webui/settings/settings_page_browsertest.js",
-      "data/webui/settings/settings_passwords_section_browsertest.js",
-      "data/webui/settings/settings_subpage_browsertest.js",
-      "data/webui/settings/settings_ui_browsertest.js",
-      "data/webui/settings/site_settings_page_browsertest.js",
       "data/webui/text_defaults_browsertest.js",
       "data/webui/webui_resource_async_browsertest.js",
     ]
+
+    if (!is_asan) {
+      sources += [
+        "data/webui/settings/advanced_page_browsertest.js",
+        "data/webui/settings/animation_browsertest.js",
+        "data/webui/settings/basic_page_browsertest.js",
+        "data/webui/settings/cr_settings_browsertest.js",
+        "data/webui/settings/help_page_browsertest.js",
+        "data/webui/settings/languages_page_browsertest.js",
+        "data/webui/settings/on_startup_browsertest.js",
+        "data/webui/settings/passwords_and_autofill_fake_data.js",
+        "data/webui/settings/passwords_and_forms_browsertest.js",
+        "data/webui/settings/settings_autofill_section_browsertest.js",
+        "data/webui/settings/settings_idle_render_browsertest.js",
+        "data/webui/settings/settings_page_browsertest.js",
+        "data/webui/settings/settings_passwords_section_browsertest.js",
+        "data/webui/settings/settings_subpage_browsertest.js",
+        "data/webui/settings/settings_ui_browsertest.js",
+        "data/webui/settings/site_settings_page_browsertest.js",
+      ]
+
+      if (is_chromeos) {
+        sources += [ "data/webui/settings/easy_unlock_browsertest_chromeos.js" ]
+      }
+    }
+
     if (is_chrome_branded) {
       # crbug.com/230471
       sources -= [ "data/webui/accessibility_audit_browsertest.js" ]
     }
     if (!is_chromeos) {
-      sources -= [
-        "data/webui/certificate_viewer_dialog_test.js",
-        "data/webui/settings/easy_unlock_browsertest_chromeos.js",
-      ]
+      sources -= [ "data/webui/certificate_viewer_dialog_test.js" ]
     } else {
       sources -= [ "data/webui/md_user_manager/user_manager_browsertest.js" ]
     }
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index 1453a78c3..d8880c32 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -1003,13 +1003,6 @@
   mocha.run();
 });
 
-// Hangs on ASAN builder for unknown reasons. TODO(michaelpg): Find reason.
-GEN('#if defined(ADDRESS_SANITIZER)');
-GEN('#define MAYBE_All DISABLED_All');
-GEN('#else');
-GEN('#define MAYBE_All All');
-GEN('#endif');
-
 /**
  * @constructor
  * @extends {SettingsPageBrowserTest}
@@ -1026,7 +1019,7 @@
   runAccessibilityChecks: false,
 };
 
-TEST_F('CrSettingsRouteDynamicParametersTest', 'MAYBE_All', function() {
+TEST_F('CrSettingsRouteDynamicParametersTest', 'All', function() {
   suite('DynamicParameters', function() {
     test('get parameters from URL and navigation', function(done) {
       assertEquals(settings.Route.PEOPLE, settings.getCurrentRoute());
diff --git a/chromecast/base/serializers_unittest.cc b/chromecast/base/serializers_unittest.cc
index d5c4189a..8dc9b07 100644
--- a/chromecast/base/serializers_unittest.cc
+++ b/chromecast/base/serializers_unittest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <vector>
+
 #include "base/files/file_util.h"
 #include "base/values.h"
 #include "chromecast/base/scoped_temp_file.h"
@@ -53,7 +55,7 @@
 }
 
 TEST(SerializeToJson, BadValue) {
-  base::BinaryValue value(std::unique_ptr<char[]>(new char[12]), 12);
+  base::BinaryValue value(std::vector<char>(12));
   std::unique_ptr<std::string> str = SerializeToJson(value);
   EXPECT_EQ(nullptr, str.get());
 }
@@ -116,7 +118,7 @@
 TEST(SerializeJsonToFile, BadValue) {
   ScopedTempFile temp;
 
-  base::BinaryValue value(std::unique_ptr<char[]>(new char[12]), 12);
+  base::BinaryValue value(std::vector<char>(12));
   ASSERT_FALSE(SerializeJsonToFile(temp.path(), value));
   std::string str(temp.Read());
   EXPECT_TRUE(str.empty());
diff --git a/chromeos/login/auth/login_performer.h b/chromeos/login/auth/login_performer.h
index 633f540..21c46ca 100644
--- a/chromeos/login/auth/login_performer.h
+++ b/chromeos/login/auth/login_performer.h
@@ -20,6 +20,10 @@
 
 class AccountId;
 
+namespace base {
+class TaskRunner;
+}
+
 namespace net {
 class URLRequestContextGetter;
 }
diff --git a/chromeos/tpm/tpm_token_info_getter.cc b/chromeos/tpm/tpm_token_info_getter.cc
index dc772487..1e7918d 100644
--- a/chromeos/tpm/tpm_token_info_getter.cc
+++ b/chromeos/tpm/tpm_token_info_getter.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/location.h"
+#include "base/task_runner.h"
 #include "chromeos/cryptohome/cryptohome_parameters.h"
 #include "chromeos/dbus/cryptohome_client.h"
 
diff --git a/chromeos/tpm/tpm_token_loader.h b/chromeos/tpm/tpm_token_loader.h
index be32d38c..bf2f8e7d 100644
--- a/chromeos/tpm/tpm_token_loader.h
+++ b/chromeos/tpm/tpm_token_loader.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
diff --git a/components/ntp_tiles/BUILD.gn b/components/ntp_tiles/BUILD.gn
index 1e1a20b..050f8a5d 100644
--- a/components/ntp_tiles/BUILD.gn
+++ b/components/ntp_tiles/BUILD.gn
@@ -53,10 +53,12 @@
     "//components/pref_registry",
     "//components/prefs",
     "//components/rappor/public",
+    "//components/resources",
     "//components/search_engines",
     "//components/url_formatter",
     "//components/variations",
     "//components/variations/service",
+    "//ui/base",
   ]
 
   if (is_android) {
diff --git a/components/ntp_tiles/DEPS b/components/ntp_tiles/DEPS
index c5cd18d..2296e56f 100644
--- a/components/ntp_tiles/DEPS
+++ b/components/ntp_tiles/DEPS
@@ -3,6 +3,7 @@
   "+components/favicon",
   "+components/favicon_base",
   "+components/google/core/browser",
+  "+components/grit",
   "+components/image_fetcher",
   "+components/history/core/browser",
   "+components/pref_registry",
@@ -16,4 +17,5 @@
   "+jni",
   "+net",
   "+ui/gfx",
+  "+ui/base/resource",
 ]
diff --git a/components/ntp_tiles/most_visited_sites.cc b/components/ntp_tiles/most_visited_sites.cc
index 6df20e3..f3695c9 100644
--- a/components/ntp_tiles/most_visited_sites.cc
+++ b/components/ntp_tiles/most_visited_sites.cc
@@ -337,7 +337,8 @@
   size_t num_popular_sites_tiles = num_sites_ - num_tiles;
   NTPTilesVector popular_sites_tiles;
 
-  if (num_popular_sites_tiles > 0 && popular_sites_) {
+  if (num_popular_sites_tiles > 0 && popular_sites_ &&
+      ShouldShowPopularSites()) {
     std::set<std::string> hosts;
     for (const auto& tile : personal_tiles)
       hosts.insert(tile.url.host());
@@ -417,9 +418,6 @@
     LOG(WARNING) << "Download of popular sites failed";
     return;
   }
-
-  // Re-build the tile list. Once done, this will notify the observer.
-  BuildCurrentTiles();
 }
 
 void MostVisitedSites::OnIconMadeAvailable(const GURL& site_url,
diff --git a/components/ntp_tiles/most_visited_sites_unittest.cc b/components/ntp_tiles/most_visited_sites_unittest.cc
index 9c16609..a4c0406 100644
--- a/components/ntp_tiles/most_visited_sites_unittest.cc
+++ b/components/ntp_tiles/most_visited_sites_unittest.cc
@@ -207,9 +207,8 @@
         url_request_context_(new net::TestURLRequestContextGetter(
             base::ThreadTaskRunnerHandle::Get())),
         worker_pool_owner_(/*max_threads=*/2, "PopularSitesFactoryForTest.") {
+    PopularSitesImpl::RegisterProfilePrefs(pref_service->registry());
     if (enabled) {
-      PopularSitesImpl::RegisterProfilePrefs(pref_service->registry());
-
       prefs_->SetString(prefs::kPopularSitesOverrideCountry, "IN");
       prefs_->SetString(prefs::kPopularSitesOverrideVersion, "7");
 
diff --git a/components/ntp_tiles/popular_sites_impl.cc b/components/ntp_tiles/popular_sites_impl.cc
index ce69227f..44c2982 100644
--- a/components/ntp_tiles/popular_sites_impl.cc
+++ b/components/ntp_tiles/popular_sites_impl.cc
@@ -19,6 +19,7 @@
 #include "components/data_use_measurement/core/data_use_user_data.h"
 #include "components/google/core/browser/google_util.h"
 #include "components/ntp_tiles/constants.h"
+#include "components/ntp_tiles/field_trial.h"
 #include "components/ntp_tiles/pref_names.h"
 #include "components/ntp_tiles/switches.h"
 #include "components/pref_registry/pref_registry_syncable.h"
@@ -30,6 +31,12 @@
 #include "net/base/load_flags.h"
 #include "net/http/http_status_code.h"
 
+#if defined(OS_ANDROID) || defined(OS_IOS)
+#include "base/json/json_reader.h"
+#include "components/grit/components_resources.h"
+#include "ui/base/resource/resource_bundle.h"
+#endif
+
 #if defined(OS_IOS)
 #include "components/ntp_tiles/country_code_ios.h"
 #endif
@@ -125,6 +132,19 @@
   return sites;
 }
 
+// Creates the list of popular sites based on a snapshot available for mobile.
+std::unique_ptr<base::ListValue> DefaultPopularSites() {
+#if defined(OS_ANDROID) || defined(OS_IOS)
+  std::unique_ptr<base::ListValue> sites =
+      base::ListValue::From(base::JSONReader().ReadToValue(
+          ResourceBundle::GetSharedInstance().GetRawDataResource(
+              IDR_DEFAULT_POPULAR_SITES_JSON)));
+  DCHECK(sites);
+  return sites;
+#endif
+  return base::MakeUnique<base::ListValue>();
+}
+
 }  // namespace
 
 PopularSites::Site::Site(const base::string16& title,
@@ -158,6 +178,7 @@
       download_context_(download_context),
       parse_json_(std::move(parse_json)),
       is_fallback_(false),
+      sites_(ParseSiteList(*prefs->GetList(kPopularSitesJsonPref))),
       weak_ptr_factory_(this) {
   // If valid path provided, remove local files created by older versions.
   if (!directory.empty() && blocking_runner_) {
@@ -194,17 +215,7 @@
     FetchPopularSites();
     return true;
   }
-
-  const base::ListValue* json = prefs_->GetList(kPopularSitesJsonPref);
-  if (!json) {
-    // Cache didn't exist.
-    FetchPopularSites();
-    return true;
-  } else {
-    // Note that we don't run the callback.
-    sites_ = ParseSiteList(*json);
-    return false;
-  }
+  return false;
 }
 
 const PopularSites::SitesVector& PopularSitesImpl::sites() const {
@@ -289,7 +300,8 @@
 
   user_prefs->RegisterInt64Pref(kPopularSitesLastDownloadPref, 0);
   user_prefs->RegisterStringPref(kPopularSitesURLPref, std::string());
-  user_prefs->RegisterListPref(kPopularSitesJsonPref);
+  user_prefs->RegisterListPref(kPopularSitesJsonPref,
+                               DefaultPopularSites().release());
 }
 
 void PopularSitesImpl::FetchPopularSites() {
diff --git a/components/ntp_tiles/popular_sites_impl_unittest.cc b/components/ntp_tiles/popular_sites_impl_unittest.cc
index 86fbfde2..6732e27 100644
--- a/components/ntp_tiles/popular_sites_impl_unittest.cc
+++ b/components/ntp_tiles/popular_sites_impl_unittest.cc
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/files/important_file_writer.h"
 #include "base/files/scoped_temp_dir.h"
@@ -22,6 +23,7 @@
 #include "base/values.h"
 #include "components/ntp_tiles/json_unsafe_parser.h"
 #include "components/ntp_tiles/pref_names.h"
+#include "components/ntp_tiles/switches.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "net/http/http_status_code.h"
@@ -53,6 +55,14 @@
   return ::testing::Eq(GURL(s));
 }
 
+size_t GetNumberOfDefaultPopularSitesForPlatform() {
+#if defined(OS_ANDROID) || defined(OS_IOS)
+  return 8ul;
+#else
+  return 0;
+#endif
+}
+
 class PopularSitesTest : public ::testing::Test {
  protected:
   PopularSitesTest()
@@ -73,6 +83,8 @@
         },
         worker_pool_owner_(2, "PopularSitesTest."),
         url_fetcher_factory_(nullptr) {
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        switches::kEnableNTPPopularSites);
     PopularSitesImpl::RegisterProfilePrefs(prefs_.registry());
     CHECK(scoped_cache_dir_.CreateUniqueTempDir());
     cache_dir_ = scoped_cache_dir_.GetPath();
@@ -117,15 +129,12 @@
     scoped_refptr<net::TestURLRequestContextGetter> url_request_context(
         new net::TestURLRequestContextGetter(
             base::ThreadTaskRunnerHandle::Get()));
-    PopularSitesImpl popular_sites(worker_pool_owner_.pool().get(), &prefs_,
-                                   /*template_url_service=*/nullptr,
-                                   /*variations_service=*/nullptr,
-                                   url_request_context.get(), cache_dir_,
-                                   base::Bind(JsonUnsafeParser::Parse));
+    std::unique_ptr<PopularSites> popular_sites =
+        CreatePopularSites(url_request_context.get());
 
     base::RunLoop loop;
     base::Optional<bool> save_success;
-    if (popular_sites.MaybeStartFetch(
+    if (popular_sites->MaybeStartFetch(
             force_download, base::Bind(
                                 [](base::Optional<bool>* save_success,
                                    base::RunLoop* loop, bool success) {
@@ -135,10 +144,19 @@
                                 &save_success, &loop))) {
       loop.Run();
     }
-    *sites = popular_sites.sites();
+    *sites = popular_sites->sites();
     return save_success;
   }
 
+  std::unique_ptr<PopularSites> CreatePopularSites(
+      net::URLRequestContextGetter* context) {
+    return base::MakeUnique<PopularSitesImpl>(
+        worker_pool_owner_.pool().get(), &prefs_,
+        /*template_url_service=*/nullptr,
+        /*variations_service=*/nullptr, context, cache_dir_,
+        base::Bind(JsonUnsafeParser::Parse));
+  }
+
   const TestPopularSite kWikipedia;
   const TestPopularSite kYouTube;
   const TestPopularSite kChromium;
@@ -169,6 +187,15 @@
   EXPECT_THAT(sites[0].favicon_url, URLEq(""));
 }
 
+TEST_F(PopularSitesTest, ContainsDefaultTilesRightAfterConstruction) {
+  scoped_refptr<net::TestURLRequestContextGetter> url_request_context(
+      new net::TestURLRequestContextGetter(
+          base::ThreadTaskRunnerHandle::Get()));
+
+  EXPECT_THAT(CreatePopularSites(url_request_context.get())->sites().size(),
+              Eq(GetNumberOfDefaultPopularSitesForPlatform()));
+}
+
 TEST_F(PopularSitesTest, Fallback) {
   SetCountryAndVersion("ZZ", "9");
   RespondWith404(
@@ -194,7 +221,7 @@
               URLEq("https://www.chromium.org/favicon.ico"));
 }
 
-TEST_F(PopularSitesTest, Failure) {
+TEST_F(PopularSitesTest, PopulatesWithDefaultResoucesOnFailure) {
   SetCountryAndVersion("ZZ", "9");
   RespondWith404(
       "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_9.json");
@@ -204,7 +231,43 @@
   PopularSites::SitesVector sites;
   EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
               Eq(base::Optional<bool>(false)));
-  ASSERT_THAT(sites, IsEmpty());
+  EXPECT_THAT(sites.size(), Eq(GetNumberOfDefaultPopularSitesForPlatform()));
+}
+
+TEST_F(PopularSitesTest, ProvidesDefaultSitesUntilCallbackReturns) {
+  SetCountryAndVersion("ZZ", "9");
+  RespondWithJSON(
+      "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_9.json",
+      {kWikipedia});
+  scoped_refptr<net::TestURLRequestContextGetter> url_request_context(
+      new net::TestURLRequestContextGetter(
+          base::ThreadTaskRunnerHandle::Get()));
+  std::unique_ptr<PopularSites> popular_sites =
+      CreatePopularSites(url_request_context.get());
+
+  base::RunLoop loop;
+  base::Optional<bool> save_success = false;
+
+  bool callback_was_scheduled = popular_sites->MaybeStartFetch(
+      /*force_download=*/true, base::Bind(
+                                   [](base::Optional<bool>* save_success,
+                                      base::RunLoop* loop, bool success) {
+                                     save_success->emplace(success);
+                                     loop->Quit();
+                                   },
+                                   &save_success, &loop));
+
+  // Assert that callback was scheduled so we can wait for its completion.
+  ASSERT_TRUE(callback_was_scheduled);
+  // There should be 8 default sites as nothing was fetched yet.
+  EXPECT_THAT(popular_sites->sites().size(),
+              Eq(GetNumberOfDefaultPopularSitesForPlatform()));
+
+  loop.Run();  // Wait for the fetch to finish and the callback to return.
+
+  EXPECT_TRUE(save_success.value());
+  // The 1 fetched site should replace the default sites.
+  EXPECT_THAT(popular_sites->sites().size(), Eq(1ul));
 }
 
 TEST_F(PopularSitesTest, ClearsCacheFileFromOldVersions) {
diff --git a/components/ntp_tiles/resources/default_popular_sites.json b/components/ntp_tiles/resources/default_popular_sites.json
new file mode 100644
index 0000000..9e14a78
--- /dev/null
+++ b/components/ntp_tiles/resources/default_popular_sites.json
@@ -0,0 +1,42 @@
+[
+  {
+    "title": "Facebook - Log In or Sign Up",
+    "url": "https://m.facebook.com/",
+    "large_icon_url": "https://static.xx.fbcdn.net/rsrc.php/v3/ya/r/O2aKM2iSbOw.png"
+  },
+  {
+    "title": "YouTube",
+    "url": "https://m.youtube.com/",
+    "large_icon_url": "https://s.ytimg.com/yts/mobile/img/apple-touch-icon-144x144-precomposed-vflwq-hLZ.png"
+  },
+  {
+    "title": "Amazon.com: Online Shopping for Electronics, Apparel, Computers, Books, DVDs \u0026 more",
+    "url": "https://www.amazon.com/",
+    "large_icon_url": "https://images-na.ssl-images-amazon.com/images/G/01/anywhere/a_smile_196x196._CB368246573_.png"
+  },
+  {
+    "title": "Wikipedia, the free encyclopedia",
+    "url": "https://en.m.wikipedia.org/",
+    "large_icon_url": "https://en.m.wikipedia.org/static/apple-touch/wikipedia.png"
+  },
+  {
+    "title": "ESPN: The Worldwide Leader in Sports",
+    "url": "http://www.espn.com/",
+    "large_icon_url": "http://a.espncdn.com/wireless/mw5/r1/images/bookmark-icons/espn_icon-152x152.min.png"
+  },
+  {
+    "title": "Yahoo",
+    "url": "https://www.yahoo.com/",
+    "large_icon_url": "https://s.yimg.com/dh/ap/default/130909/y_200_a.png"
+  },
+  {
+    "title": "Instagram",
+    "url": "https://www.instagram.com/",
+    "large_icon_url": "https://instagramstatic-a.akamaihd.net/h1/images/ico/favicon-192.png/b407fa101800.png"
+  },
+  {
+    "title": "Electronics, Cars, Fashion, Collectibles, Coupons and More | eBay",
+    "url": "http://m.ebay.com/",
+    "large_icon_url": "http://ir.ebaystatic.com/pictures/aw/pics/mobile/images/apple-touch-icon.png"
+  }
+]
diff --git a/components/proximity_auth/webui/proximity_auth_ui.cc b/components/proximity_auth/webui/proximity_auth_ui.cc
index 89b2d2f..ce56260d 100644
--- a/components/proximity_auth/webui/proximity_auth_ui.cc
+++ b/components/proximity_auth/webui/proximity_auth_ui.cc
@@ -21,33 +21,14 @@
     : content::WebUIController(web_ui) {
   content::WebUIDataSource* source =
       content::WebUIDataSource::Create(kChromeUIProximityAuthHost);
-  source->SetDefaultResource(IDR_PROXIMITY_AUTH_UI_HTML);
-  source->AddResourcePath("proximity_auth.css", IDR_PROXIMITY_AUTH_UI_CSS);
-  source->AddResourcePath("content-panel.html",
-                          IDR_PROXIMITY_AUTH_CONTENT_PANEL_HTML);
-  source->AddResourcePath("content-panel.js",
-                          IDR_PROXIMITY_AUTH_CONTENT_PANEL_JS);
-  source->AddResourcePath("log-panel.html", IDR_PROXIMITY_AUTH_LOG_PANEL_HTML);
-  source->AddResourcePath("log-panel.js", IDR_PROXIMITY_AUTH_LOG_PANEL_JS);
-  source->AddResourcePath("local-state.html",
-                          IDR_PROXIMITY_AUTH_LOCAL_STATE_HTML);
-  source->AddResourcePath("local-state.js", IDR_PROXIMITY_AUTH_LOCAL_STATE_JS);
-  source->AddResourcePath("device-list.html",
-                          IDR_PROXIMITY_AUTH_DEVICE_LIST_HTML);
-  source->AddResourcePath("device-list.js", IDR_PROXIMITY_AUTH_DEVICE_LIST_JS);
-  source->AddResourcePath("log-buffer.html",
-                          IDR_PROXIMITY_AUTH_LOG_BUFFER_HTML);
-  source->AddResourcePath("log-buffer.js", IDR_PROXIMITY_AUTH_LOG_BUFFER_JS);
-  source->AddResourcePath("eligible-devices.html",
-                          IDR_PROXIMITY_AUTH_ELIGIBLE_DEVICES_HTML);
-  source->AddResourcePath("eligible-devices.js",
-                          IDR_PROXIMITY_AUTH_ELIGIBLE_DEVICES_JS);
-  source->AddResourcePath("reachable-devices.html",
-                          IDR_PROXIMITY_AUTH_REACHABLE_DEVICES_HTML);
-  source->AddResourcePath("reachable-devices.js",
-                          IDR_PROXIMITY_AUTH_REACHABLE_DEVICES_JS);
-  source->AddResourcePath("cryptauth_interface.js",
-                          IDR_PROXIMITY_AUTH_CRYPTAUTH_INTERFACE_JS);
+  source->SetDefaultResource(IDR_PROXIMITY_AUTH_POLLUX_DEBUG_HTML);
+  source->AddResourcePath("pollux_debug.css",
+                          IDR_PROXIMITY_AUTH_POLLUX_DEBUG_CSS);
+  source->AddResourcePath("pollux_debug.js",
+                          IDR_PROXIMITY_AUTH_POLLUX_DEBUG_JS);
+  source->AddResourcePath("webui.js", IDR_PROXIMITY_AUTH_POLLUX_WEBUI_JS);
+  source->AddResourcePath("logs_controller.js",
+                          IDR_PROXIMITY_AUTH_POLLUX_LOGS_CONTROLLER_JS);
 
   content::BrowserContext* browser_context =
       web_ui->GetWebContents()->GetBrowserContext();
diff --git a/components/proximity_auth/webui/resources/pollux/logs_controller.js b/components/proximity_auth/webui/resources/pollux/logs_controller.js
new file mode 100644
index 0000000..edbd5b7
--- /dev/null
+++ b/components/proximity_auth/webui/resources/pollux/logs_controller.js
@@ -0,0 +1,124 @@
+/* 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.
+ */
+
+Logs = {
+  controller_: null,
+
+  /**
+   * Initializes the logs UI.
+   */
+  init: function() {
+    Logs.controller_ = new LogsListController();
+
+    var clearLogsButton = document.getElementById('clear-logs-button');
+    clearLogsButton.onclick = function() {
+      WebUI.clearLogs();
+    };
+
+    WebUI.getLogMessages();
+  },
+};
+
+/**
+ * Interface with the native WebUI component for LogBuffer events. The functions
+ * contained in this object will be invoked by the browser for each operation
+ * performed on the native LogBuffer.
+ */
+LogBufferInterface = {
+  /**
+   * Called when a new log message is added.
+   */
+  onLogMessageAdded: function(log) {
+    if (Logs.controller_) {
+      Logs.controller_.add(log);
+    }
+  },
+
+  /**
+   * Called when the log buffer is cleared.
+   */
+  onLogBufferCleared: function() {
+    if (Logs.controller_) {
+      Logs.controller_.clear();
+    }
+  },
+
+  /**
+   * Called in response to chrome.send('getLogMessages') with the log messages
+   * currently in the buffer.
+   */
+  onGotLogMessages: function(messages) {
+    if (Logs.controller_) {
+      Logs.controller_.set(messages);
+    }
+  },
+};
+
+/**
+ * Controller for the logs list UI, updating it based on user input and logs
+ * received from native code.
+ *
+ */
+class LogsListController {
+  constructor() {
+    this.logsList_ = document.getElementById('logs-list');
+    this.itemTemplate_ = document.getElementById('item-template');
+    this.shouldSnapToBottom_ = true;
+
+    this.logsList_.onscroll = this.onScroll_.bind(this);
+  }
+
+  /**
+   * Listener for scroll event of the logs list element, used for snap to bottom
+   * logic.
+   */
+  onScroll_() {
+    const list = this.logsList_;
+    this.shouldSnapToBottom_ =
+        list.scrollTop + list.offsetHeight == list.scrollHeight;
+  }
+
+  /**
+   * Clears all log items from the logs list.
+   */
+  clear() {
+    var items = this.logsList_.querySelectorAll('.log-item');
+    for (var i = 0; i < items.length; ++i) {
+      items[i].remove();
+    }
+    this.shouldSnapToBottom_ = true;
+  }
+
+  /**
+   * Adds a log to the logs list.
+   */
+  add(log) {
+    var directories = log.file.split('/');
+    var source = directories[directories.length - 1] + ':' + log.line;
+
+    var t = this.itemTemplate_.content;
+    console.log(t.querySelector('.log-item').attributes);
+    t.querySelector('.log-item').attributes.severity.value = log.severity;
+    t.querySelector('.item-time').textContent = log.time;
+    t.querySelector('.item-source').textContent = source;
+    t.querySelector('.item-text').textContent = log.text;
+
+    var newLogItem = document.importNode(this.itemTemplate_.content, true);
+    this.logsList_.appendChild(newLogItem);
+    if (this.shouldSnapToBottom_) {
+      this.logsList_.scrollTop = this.logsList_.scrollHeight;
+    }
+  }
+
+  /**
+   * Initializes the log list from an array of logs.
+   */
+  set(logs) {
+    this.clear();
+    for (var i = 0; i < logs.length; ++i) {
+      this.add(logs[i]);
+    }
+  }
+}
diff --git a/components/proximity_auth/webui/resources/pollux/pollux_debug.css b/components/proximity_auth/webui/resources/pollux/pollux_debug.css
new file mode 100644
index 0000000..c54ac13
--- /dev/null
+++ b/components/proximity_auth/webui/resources/pollux/pollux_debug.css
@@ -0,0 +1,161 @@
+/* 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.
+ */
+
+html, body {
+  width: 100%;
+  height: 100%;
+  margin: 0;
+  padding: 0;
+  overflow: hidden;
+  font-family: "Roboto", sans-serif;
+  display: flex;
+}
+
+header {
+  min-height: 50px;
+  font-size: 24px;
+  background-color: #069BDE;
+  display: flex;
+  padding: 0 20px;
+  color: #f4f4f4;
+  text-align: center;
+  align-items: center;
+  border-bottom: 1px solid rgba(0,0,0,0.12);
+}
+
+
+/** CSS for controls panel */
+#controls {
+  width: 60%;
+  display: flex;
+  flex-direction: column;
+  overflow-y: auto;
+}
+
+.logo {
+  font-size: 40px;
+  margin-right: 12px;
+}
+
+#controls-panel {
+  padding: 40px;
+}
+
+.control {
+  margin-bottom: 60px;
+}
+
+.control-title {
+  font-size: 16px;
+  font-weight: bold;
+  margin-bottom: 5px;
+}
+
+.input-row {
+  display: flex;
+  align-items: center;
+  margin-bottom: 5px;
+  padding-left: 12px;
+}
+
+.input-row button {
+  border: 1px solid rgba(0,0,0,0.12);
+  color: #fff;
+  background-color: #7496c8;
+  border: none;
+  cursor: pointer;
+  font-size: 14px;
+  height: 24px;
+  width: 80px;
+}
+
+.input-row button:hover {
+  background-color: #446392;
+}
+
+.input-row button:active {
+  background-color: #37496d;
+}
+
+.input-name {
+  font-size: 14px;
+  width: 100px;
+}
+
+.textinput {
+  min-width: 200px;
+  font-family: monospace;
+  margin: 0 5px;
+  font-size: 16px;
+}
+
+
+/** CSS for logs panel */
+#logs-panel {
+  width: 40%;
+  display: flex;
+  flex-direction: column;
+  border-left: 1px solid rgba(0,0,0,0.12);
+}
+
+#logs-panel > header {
+  flex-direction: row-reverse;
+}
+
+#clear-logs-button {
+  cursor: pointer;
+  background-color: rgba(0,0,0,0);
+  border: none;
+  font-size: 30px;
+  height: 100%;
+  color: #fff;
+}
+
+#clear-logs-button:hover {
+  cursor: pointer;
+  color: #535553;
+}
+
+#clear-logs-button:focus {
+  outline: 0;
+}
+
+#logs-list {
+  display: flex;
+  flex-direction: column;
+  overflow-y: auto;
+}
+
+.log-item {
+  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
+  font-family: monospace;
+  font-size: 12px;
+  padding: 15px 30px;
+}
+
+.log-item[severity="1"] {
+  background-color: #fffcef;
+  color: #312200;
+}
+
+.log-item[severity="2"] {
+  background-color: #fff1f1;
+  color: #ef0000;
+}
+
+.item-metadata {
+  color: #888888;
+  font-size: 10px;
+  display: flex;
+}
+
+.item-metadata > .flex {
+  flex: 1;
+}
+
+.item-text {
+  margin: 0;
+  display: inline-block;
+}
diff --git a/components/proximity_auth/webui/resources/pollux/pollux_debug.html b/components/proximity_auth/webui/resources/pollux/pollux_debug.html
new file mode 100644
index 0000000..55de707d
--- /dev/null
+++ b/components/proximity_auth/webui/resources/pollux/pollux_debug.html
@@ -0,0 +1,82 @@
+<!DOCTYPE HTML>
+<html>
+
+<head>
+  <meta charset="utf-8">
+
+  <link href="chrome://resources/css/roboto.css" rel="stylesheet">
+  <link href="pollux_debug.css" rel='stylesheet'>
+
+  <script src='webui.js' type='text/javascript'></script>
+  <script src='logs_controller.js' type='text/javascript'></script>
+  <script src='pollux_debug.js' type='text/javascript'></script>
+</head>
+
+<body>
+  <!-- Panel for debug controls. -->
+  <section id='controls'>
+    <header>
+      <div class='logo'>✧</div>
+      <div class='title'>POLLUX DEBUG</div>
+    </header>
+
+    <div id='controls-panel'>
+      <!-- Controls for server simulation -->
+      <div class='control'>
+        <div class='control-title'>SERVER</div>
+        <div class='input-row'>
+          <div class='input-name'>MASTER_KEY:</div>
+          <input class='textinput' type='text' value='F3DA234A14'></input>
+        </div>
+        <div class='input-row'><button>Challenge</button></div>
+      </div>
+
+      <!-- Controls for testing getAssertion() -->
+      <div class='control'>
+        <div class='control-title'>GET ASSERTION</div>
+        <div class='input-row'>
+          <div class='input-name'>CHALLENGE:</div>
+          <input class='textinput' type='text' value='A3DA234A14'></input>
+        </div>
+        <div class='input-row'>
+          <div class='input-name'>EID:</div>
+          <input class='textinput' type='text' value='A3DA234A14'></input>
+        </div>
+        <div class='input-row'>
+          <div class='input-name'>SESSION_KEY:</div>
+          <input class='textinput' type='text' value='A3DA234A14'></input>
+        </div>
+        <div class='input-row'><button>Start</button></div>
+      </div>
+
+      <!-- Controls for the state of the remote device (AKA Authenticator) -->
+      <div class='control'>
+        <div class='control-title'>AUTHENTICATOR STATE</div>
+        <div class='input-row'>
+          <div class='input-name'>NOT STARTED</div>
+        </div>
+      </div>
+    </div>
+  </section>
+
+  <!-- Panel for logs list. -->
+  <section id='logs-panel'>
+    <header>
+      <button id='clear-logs-button'>∅</button>
+    </header>
+    <div id='logs-list'>
+      <template id='item-template'>
+        <div class='log-item' severity='0'>
+          <div class="item-metadata">
+            <div class='item-time'></div>
+            <div class="flex"></div>
+            <div class='item-source'></div>
+          </div>
+          <pre class="item-text flex">This is an error.</pre>
+        </div>
+      </template>
+    </div>
+  </section>
+</body>
+
+</html>
diff --git a/components/proximity_auth/webui/resources/pollux/pollux_debug.js b/components/proximity_auth/webui/resources/pollux/pollux_debug.js
new file mode 100644
index 0000000..29af6bfe
--- /dev/null
+++ b/components/proximity_auth/webui/resources/pollux/pollux_debug.js
@@ -0,0 +1,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.
+ */
+
+document.addEventListener('DOMContentLoaded', function() {
+  Logs.init();
+});
diff --git a/components/proximity_auth/webui/resources/pollux/webui.js b/components/proximity_auth/webui/resources/pollux/webui.js
new file mode 100644
index 0000000..1525c66d
--- /dev/null
+++ b/components/proximity_auth/webui/resources/pollux/webui.js
@@ -0,0 +1,25 @@
+/* Copyright 2017 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * JavaScript hooks into the native WebUI handler.
+ */
+WebUI = {
+  getLogMessages: function() {
+    chrome.send('getLogMessages');
+  },
+
+  clearLogs: function() {
+    chrome.send('clearLogBuffer');
+  },
+
+  generateChallenge: function() {
+    chrome.send('generateChallenge');
+  },
+
+  getAssertion: function() {
+    chrome.send('getAssertion');
+  }
+};
diff --git a/components/resources/ntp_tiles_resources.grdp b/components/resources/ntp_tiles_resources.grdp
index 37999984..e42d4ea 100644
--- a/components/resources/ntp_tiles_resources.grdp
+++ b/components/resources/ntp_tiles_resources.grdp
@@ -4,6 +4,7 @@
     <include name="IDR_POPULAR_SITES_INTERNALS_HTML" file="../ntp_tiles/webui/resources/popular_sites_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
     <include name="IDR_POPULAR_SITES_INTERNALS_JS" file="../ntp_tiles/webui/resources/popular_sites_internals.js" type="BINDATA" />
     <include name="IDR_POPULAR_SITES_INTERNALS_CSS" file="../ntp_tiles/webui/resources/popular_sites_internals.css" type="BINDATA" />
+    <include name="IDR_DEFAULT_POPULAR_SITES_JSON" file="../ntp_tiles/resources/default_popular_sites.json" type="BINDATA" />
   </if>
   <include name="IDR_NTP_TILES_INTERNALS_HTML" file="../ntp_tiles/webui/resources/ntp_tiles_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
   <include name="IDR_NTP_TILES_INTERNALS_JS" file="../ntp_tiles/webui/resources/ntp_tiles_internals.js" type="BINDATA" />
diff --git a/components/resources/proximity_auth_resources.grdp b/components/resources/proximity_auth_resources.grdp
index 0c71d06..6798b6c 100644
--- a/components/resources/proximity_auth_resources.grdp
+++ b/components/resources/proximity_auth_resources.grdp
@@ -32,4 +32,16 @@
            file="../proximity_auth/webui/resources/reachable-devices.js" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
   <include name="IDR_PROXIMITY_AUTH_CRYPTAUTH_INTERFACE_JS"
            file="../proximity_auth/webui/resources/cryptauth_interface.js" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
+
+  <!-- Resources for Pollux debug page. -->
+  <include name="IDR_PROXIMITY_AUTH_POLLUX_DEBUG_HTML"
+          file="../proximity_auth/webui/resources/pollux/pollux_debug.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
+  <include name="IDR_PROXIMITY_AUTH_POLLUX_DEBUG_CSS"
+           file="../proximity_auth/webui/resources/pollux/pollux_debug.css" type="BINDATA" />
+  <include name="IDR_PROXIMITY_AUTH_POLLUX_DEBUG_JS"
+           file="../proximity_auth/webui/resources/pollux/pollux_debug.js" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
+  <include name="IDR_PROXIMITY_AUTH_POLLUX_LOGS_CONTROLLER_JS"
+           file="../proximity_auth/webui/resources/pollux/logs_controller.js" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
+  <include name="IDR_PROXIMITY_AUTH_POLLUX_WEBUI_JS"
+           file="../proximity_auth/webui/resources/pollux/webui.js" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
 </grit-part>
diff --git a/components/search_engines/default_search_policy_handler_unittest.cc b/components/search_engines/default_search_policy_handler_unittest.cc
index 93f7eaf..6db8390 100644
--- a/components/search_engines/default_search_policy_handler_unittest.cc
+++ b/components/search_engines/default_search_policy_handler_unittest.cc
@@ -179,7 +179,8 @@
     // Try changing policy param to BinaryValue and check that policy becomes
     // invalid.
     policy.Set(policy_name, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-               POLICY_SOURCE_CLOUD, base::WrapUnique(new base::BinaryValue()),
+               POLICY_SOURCE_CLOUD,
+               base::MakeUnique<base::Value>(base::Value::Type::BINARY),
                nullptr);
     UpdateProviderPolicy(policy);
 
diff --git a/content/app/BUILD.gn b/content/app/BUILD.gn
index 21d91f9..2480465 100644
--- a/content/app/BUILD.gn
+++ b/content/app/BUILD.gn
@@ -55,7 +55,6 @@
       "//device/geolocation",
       "//device/power_save_blocker",
       "//device/sensors",
-      "//device/time_zone_monitor",
       "//device/usb",
       "//device/vibration",
       "//gpu",
diff --git a/content/app/DEPS b/content/app/DEPS
index d001ffa1..76c9bee 100644
--- a/content/app/DEPS
+++ b/content/app/DEPS
@@ -8,7 +8,6 @@
   "+device/geolocation",
   "+device/power_save_blocker",
   "+device/sensors",
-  "+device/time_zone_monitor",
   "+device/usb",
   "+device/vibration",
   # For loading V8's initial snapshot from external files.
diff --git a/content/app/android/library_loader_hooks.cc b/content/app/android/library_loader_hooks.cc
index 77a7111b..8d16801 100644
--- a/content/app/android/library_loader_hooks.cc
+++ b/content/app/android/library_loader_hooks.cc
@@ -31,7 +31,6 @@
 #include "device/generic_sensor/android/sensors_jni_registrar.h"
 #include "device/geolocation/android/geolocation_jni_registrar.h"
 #include "device/sensors/android/device_sensor_jni_registrar.h"
-#include "device/time_zone_monitor/android/time_zone_monitor_jni_registrar.h"
 #include "device/usb/android/usb_jni_registrar.h"
 #include "media/base/android/media_jni_registrar.h"
 #include "media/capture/content/android/screen_capture_jni_registrar.h"
@@ -95,9 +94,6 @@
     if (!device::android::RegisterSensorsJni(env))
       return false;
 
-    if (!device::android::RegisterTimeZoneMonitorJni(env))
-      return false;
-
     if (!device::android::RegisterUsbJni(env))
       return false;
 
diff --git a/content/browser/DEPS b/content/browser/DEPS
index b3a770a..af18f2d 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -78,6 +78,7 @@
   "+third_party/WebKit/public/platform/WebReferrerPolicy.h",
   "+third_party/WebKit/public/platform/WebScreenInfo.h",
   "+third_party/WebKit/public/platform/WebSecurityStyle.h",
+  "+third_party/WebKit/public/platform/WebString.h",
   "+third_party/WebKit/public/platform/WebTouchEvent.h",
   "+third_party/WebKit/public/platform/WebTextInputType.h",
   "+third_party/WebKit/public/platform/mime_registry.mojom.h",
diff --git a/content/browser/android/content_view_core_impl.cc b/content/browser/android/content_view_core_impl.cc
index 4cfea45b..41f7930 100644
--- a/content/browser/android/content_view_core_impl.cc
+++ b/content/browser/android/content_view_core_impl.cc
@@ -418,6 +418,8 @@
     const gfx::SizeF& viewport_size,
     const float top_controls_height,
     const float top_controls_shown_ratio,
+    const float bottom_controls_height,
+    const float bottom_controls_shown_ratio,
     bool is_mobile_optimized_hint,
     const gfx::SelectionBound& selection_start) {
   JNIEnv* env = AttachCurrentThread();
@@ -447,11 +449,20 @@
       page_scale_factor_limits.x(), page_scale_factor_limits.y(),
       content_size.width(), content_size.height(), viewport_size.width(),
       viewport_size.height(), top_controls_height, top_controls_shown_ratio,
+      bottom_controls_height, bottom_controls_shown_ratio,
       is_mobile_optimized_hint, has_insertion_marker,
       is_insertion_marker_visible, insertion_marker_horizontal,
       insertion_marker_top, insertion_marker_bottom);
 }
 
+void ContentViewCoreImpl::OnBackgroundColorChanged(SkColor color) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+  if (obj.is_null())
+    return;
+  Java_ContentViewCore_onBackgroundColorChanged(env, obj, color);
+}
+
 void ContentViewCoreImpl::ShowSelectPopupMenu(
     RenderFrameHost* frame,
     const gfx::Rect& bounds,
@@ -641,6 +652,18 @@
                                       y_dip);
 }
 
+void ContentViewCoreImpl::StartContentIntent(const GURL& content_url,
+                                             bool is_main_frame) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
+  if (j_obj.is_null())
+    return;
+  ScopedJavaLocalRef<jstring> jcontent_url =
+      ConvertUTF8ToJavaString(env, content_url.spec());
+  Java_ContentViewCore_startContentIntent(env, j_obj, jcontent_url,
+                                          is_main_frame);
+}
+
 void ContentViewCoreImpl::ShowDisambiguationPopup(
     const gfx::Rect& rect_pixels,
     const SkBitmap& zoomed_bitmap) {
diff --git a/content/browser/android/content_view_core_impl.h b/content/browser/android/content_view_core_impl.h
index 7973a15..603fd441 100644
--- a/content/browser/android/content_view_core_impl.h
+++ b/content/browser/android/content_view_core_impl.h
@@ -307,6 +307,8 @@
                        const gfx::SizeF& viewport_size,
                        const float top_controls_height,
                        const float top_controls_shown_ratio,
+                       const float bottom_controls_height,
+                       const float bottom_controls_shown_ratio,
                        bool is_mobile_optimized_hint,
                        const gfx::SelectionBound& selection_start);
 
@@ -322,6 +324,7 @@
                         int composition_end,
                         bool show_ime_if_needed,
                         bool reply_to_request);
+  void OnBackgroundColorChanged(SkColor color);
 
   bool HasFocus();
   void RequestDisallowInterceptTouchEvent();
@@ -333,6 +336,8 @@
                         const gfx::PointF& selection_anchor,
                         const gfx::RectF& selection_rect);
 
+  void StartContentIntent(const GURL& content_url, bool is_main_frame);
+
   // Shows the disambiguation popup
   // |rect_pixels|   --> window coordinates which |zoomed_bitmap| represents
   // |zoomed_bitmap| --> magnified image of potential touch targets
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index 9f1db28..02d54e8 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -400,10 +400,6 @@
       display_compositor::GLHelper::SCALER_QUALITY_GOOD);
 }
 
-bool FloatEquals(float a, float b) {
-  return std::abs(a - b) < FLT_EPSILON;
-}
-
 }  // namespace
 
 void RenderWidgetHostViewAndroid::OnContextLost() {
@@ -439,8 +435,6 @@
       synchronous_compositor_client_(nullptr),
       frame_evictor_(new DelegatedFrameEvictor(this)),
       observing_root_window_(false),
-      prev_top_shown_pix_(0.f),
-      prev_bottom_shown_pix_(0.f),
       weak_ptr_factory_(this) {
   // Set the layer which will hold the content layer for this view. The content
   // layer is managed by the DelegatedFrameHost.
@@ -796,7 +790,8 @@
   if (delegated_frame_host_)
     delegated_frame_host_->UpdateBackgroundColor(color);
 
-  view_.OnBackgroundColorChanged(color);
+  if (content_view_core_)
+    content_view_core_->OnBackgroundColorChanged(color);
 }
 
 void RenderWidgetHostViewAndroid::SetNeedsBeginFrames(bool needs_begin_frames) {
@@ -810,7 +805,8 @@
 
 void RenderWidgetHostViewAndroid::OnStartContentIntent(
     const GURL& content_url, bool is_main_frame) {
-  view_.StartContentIntent(content_url, is_main_frame);
+  if (content_view_core_)
+    content_view_core_->StartContentIntent(content_url, is_main_frame);
 }
 
 bool RenderWidgetHostViewAndroid::OnTouchEvent(
@@ -1278,28 +1274,6 @@
       frame_metadata.top_controls_height *
           frame_metadata.top_controls_shown_ratio));
 
-  float dip_scale = ui::GetScaleFactorForNativeView(GetNativeView());
-  float top_controls_pix = frame_metadata.top_controls_height * dip_scale;
-  float top_shown_pix = top_controls_pix
-      * frame_metadata.top_controls_shown_ratio;
-  bool top_changed = !FloatEquals(top_shown_pix, prev_top_shown_pix_);
-
-  float bottom_controls_pix = frame_metadata.bottom_controls_height * dip_scale;
-  float bottom_shown_pix = bottom_controls_pix
-      * frame_metadata.bottom_controls_shown_ratio;
-  bool bottom_changed = !FloatEquals(bottom_shown_pix, prev_bottom_shown_pix_);
-
-  if (top_changed) {
-    float translate = top_shown_pix - top_controls_pix;
-    view_.OnTopControlsChanged(translate, top_shown_pix);
-    prev_top_shown_pix_ = top_shown_pix;
-  }
-  if (bottom_changed) {
-    float translate = bottom_controls_pix - bottom_shown_pix;
-    view_.OnBottomControlsChanged(translate, bottom_shown_pix);
-    prev_bottom_shown_pix_ = bottom_shown_pix;
-  }
-
   // All offsets and sizes are in CSS pixels.
   content_view_core_->UpdateFrameInfo(
       frame_metadata.root_scroll_offset,
@@ -1310,6 +1284,8 @@
       frame_metadata.scrollable_viewport_size,
       frame_metadata.top_controls_height,
       frame_metadata.top_controls_shown_ratio,
+      frame_metadata.bottom_controls_height,
+      frame_metadata.bottom_controls_shown_ratio,
       is_mobile_optimized,
       frame_metadata.selection.start);
 }
diff --git a/content/browser/renderer_host/render_widget_host_view_android.h b/content/browser/renderer_host/render_widget_host_view_android.h
index 05064919..a52a7a1 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.h
+++ b/content/browser/renderer_host/render_widget_host_view_android.h
@@ -376,9 +376,6 @@
   // The last scroll offset of the view.
   gfx::Vector2dF last_scroll_offset_;
 
-  float prev_top_shown_pix_;
-  float prev_bottom_shown_pix_;
-
   base::WeakPtrFactory<RenderWidgetHostViewAndroid> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAndroid);
diff --git a/content/browser/shared_worker/shared_worker_service_impl.cc b/content/browser/shared_worker/shared_worker_service_impl.cc
index 4524fd5..924582cd2 100644
--- a/content/browser/shared_worker/shared_worker_service_impl.cc
+++ b/content/browser/shared_worker/shared_worker_service_impl.cc
@@ -12,7 +12,6 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
-#include "base/memory/ref_counted.h"
 #include "base/stl_util.h"
 #include "content/browser/devtools/shared_worker_devtools_manager.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
@@ -38,6 +37,78 @@
 
 namespace {
 
+// A helper to request a renderer process allocation for a shared worker to the
+// UI thread from the IO thread.
+class SharedWorkerReserver {
+ public:
+  ~SharedWorkerReserver() = default;
+
+  using ResolverCallback = base::Callback<void(int worker_process_id,
+                                               int worker_route_id,
+                                               bool is_new_worker,
+                                               bool pause_on_start)>;
+
+  // Tries to reserve a renderer process for a shared worker. This should be
+  // called on the IO thread and given callbacks are also invoked on the IO
+  // thread on completion.
+  static void TryReserve(int worker_process_id,
+                         int worker_route_id,
+                         bool is_new_worker,
+                         const SharedWorkerInstance& instance,
+                         const ResolverCallback& success_cb,
+                         const ResolverCallback& failure_cb,
+                         bool (*try_increment_worker_ref_count)(int)) {
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
+    std::unique_ptr<SharedWorkerReserver> reserver(new SharedWorkerReserver(
+        worker_process_id, worker_route_id, is_new_worker, instance));
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        base::Bind(&SharedWorkerReserver::TryReserveOnUI, std::move(reserver),
+                   success_cb, failure_cb, try_increment_worker_ref_count));
+  }
+
+ private:
+  SharedWorkerReserver(int worker_process_id,
+                       int worker_route_id,
+                       bool is_new_worker,
+                       const SharedWorkerInstance& instance)
+      : worker_process_id_(worker_process_id),
+        worker_route_id_(worker_route_id),
+        is_new_worker_(is_new_worker),
+        instance_(instance) {}
+
+  void TryReserveOnUI(const ResolverCallback& success_cb,
+                      const ResolverCallback& failure_cb,
+                      bool (*try_increment_worker_ref_count)(int)) {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+    bool pause_on_start = false;
+    if (!try_increment_worker_ref_count(worker_process_id_)) {
+      DidTryReserveOnUI(failure_cb, pause_on_start);
+      return;
+    }
+    if (is_new_worker_) {
+      pause_on_start =
+          SharedWorkerDevToolsManager::GetInstance()->WorkerCreated(
+              worker_process_id_, worker_route_id_, instance_);
+    }
+    DidTryReserveOnUI(success_cb, pause_on_start);
+  }
+
+  void DidTryReserveOnUI(const ResolverCallback& callback,
+                         bool pause_on_start) {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+    BrowserThread::PostTask(
+        BrowserThread::IO, FROM_HERE,
+        base::Bind(callback, worker_process_id_, worker_route_id_,
+                   is_new_worker_, pause_on_start));
+  }
+
+  const int worker_process_id_;
+  const int worker_route_id_;
+  const bool is_new_worker_;
+  const SharedWorkerInstance instance_;
+};
+
 class ScopedWorkerDependencyChecker {
  public:
   explicit ScopedWorkerDependencyChecker(SharedWorkerServiceImpl* service)
@@ -194,47 +265,6 @@
   DISALLOW_COPY_AND_ASSIGN(SharedWorkerPendingInstance);
 };
 
-class SharedWorkerServiceImpl::SharedWorkerReserver
-    : public base::RefCountedThreadSafe<SharedWorkerReserver> {
- public:
-  SharedWorkerReserver(int pending_instance_id,
-                       int worker_process_id,
-                       int worker_route_id,
-                       bool is_new_worker,
-                       const SharedWorkerInstance& instance)
-      : worker_process_id_(worker_process_id),
-        worker_route_id_(worker_route_id),
-        is_new_worker_(is_new_worker),
-        instance_(instance) {}
-
-  void TryReserve(const base::Callback<void(bool)>& success_cb,
-                  const base::Closure& failure_cb,
-                  bool (*try_increment_worker_ref_count)(int)) {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    if (!try_increment_worker_ref_count(worker_process_id_)) {
-      BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, failure_cb);
-      return;
-    }
-    bool pause_on_start = false;
-    if (is_new_worker_) {
-      pause_on_start =
-          SharedWorkerDevToolsManager::GetInstance()->WorkerCreated(
-              worker_process_id_, worker_route_id_, instance_);
-    }
-    BrowserThread::PostTask(
-        BrowserThread::IO, FROM_HERE, base::Bind(success_cb, pause_on_start));
-  }
-
- private:
-  friend class base::RefCountedThreadSafe<SharedWorkerReserver>;
-  ~SharedWorkerReserver() {}
-
-  const int worker_process_id_;
-  const int worker_route_id_;
-  const bool is_new_worker_;
-  const SharedWorkerInstance instance_;
-};
-
 // static
 bool (*SharedWorkerServiceImpl::s_try_increment_worker_ref_count_)(int) =
     TryIncrementWorkerRefCount;
@@ -513,32 +543,14 @@
   }
 
   const int pending_instance_id = next_pending_instance_id_++;
-  scoped_refptr<SharedWorkerReserver> reserver(
-      new SharedWorkerReserver(pending_instance_id,
-                               worker_process_id,
-                               worker_route_id,
-                               is_new_worker,
-                               *pending_instance->instance()));
-  BrowserThread::PostTask(
-      BrowserThread::UI,
-      FROM_HERE,
-      base::Bind(
-          &SharedWorkerReserver::TryReserve,
-          reserver,
-          base::Bind(&SharedWorkerServiceImpl::RenderProcessReservedCallback,
-                     base::Unretained(this),
-                     pending_instance_id,
-                     worker_process_id,
-                     worker_route_id,
-                     is_new_worker),
-          base::Bind(
-              &SharedWorkerServiceImpl::RenderProcessReserveFailedCallback,
-              base::Unretained(this),
-              pending_instance_id,
-              worker_process_id,
-              worker_route_id,
-              is_new_worker),
-          s_try_increment_worker_ref_count_));
+  SharedWorkerReserver::TryReserve(
+      worker_process_id, worker_route_id, is_new_worker,
+      *pending_instance->instance(),
+      base::Bind(&SharedWorkerServiceImpl::RenderProcessReservedCallback,
+                 base::Unretained(this), pending_instance_id),
+      base::Bind(&SharedWorkerServiceImpl::RenderProcessReserveFailedCallback,
+                 base::Unretained(this), pending_instance_id),
+      s_try_increment_worker_ref_count_);
   pending_instances_[pending_instance_id] = std::move(pending_instance);
   return creation_error;
 }
@@ -603,7 +615,9 @@
     int pending_instance_id,
     int worker_process_id,
     int worker_route_id,
-    bool is_new_worker) {
+    bool is_new_worker,
+    bool pause_on_start) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   worker_hosts_.erase(std::make_pair(worker_process_id, worker_route_id));
   if (!base::ContainsKey(pending_instances_, pending_instance_id))
     return;
diff --git a/content/browser/shared_worker/shared_worker_service_impl.h b/content/browser/shared_worker/shared_worker_service_impl.h
index ca351d1..e6383c8 100644
--- a/content/browser/shared_worker/shared_worker_service_impl.h
+++ b/content/browser/shared_worker/shared_worker_service_impl.h
@@ -104,7 +104,6 @@
 
  private:
   class SharedWorkerPendingInstance;
-  class SharedWorkerReserver;
 
   friend struct base::DefaultSingletonTraits<SharedWorkerServiceImpl>;
   friend class SharedWorkerServiceImplTest;
@@ -146,7 +145,8 @@
   void RenderProcessReserveFailedCallback(int pending_instance_id,
                                           int worker_process_id,
                                           int worker_route_id,
-                                          bool is_new_worker);
+                                          bool is_new_worker,
+                                          bool pause_on_start);
 
   // Returns nullptr if there is no host for given ids.
   SharedWorkerHost* FindSharedWorkerHost(int render_process_id,
diff --git a/content/child/v8_value_converter_impl.cc b/content/child/v8_value_converter_impl.cc
index dd36788..86554c33 100644
--- a/content/child/v8_value_converter_impl.cc
+++ b/content/child/v8_value_converter_impl.cc
@@ -11,6 +11,7 @@
 #include <memory>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
@@ -268,9 +269,7 @@
                         static_cast<const base::DictionaryValue*>(value));
 
     case base::Value::Type::BINARY:
-      return ToArrayBuffer(isolate,
-                           creation_context,
-                           static_cast<const base::BinaryValue*>(value));
+      return ToArrayBuffer(isolate, creation_context, value);
 
     default:
       LOG(ERROR) << "Unexpected value type: " << value->GetType();
@@ -501,9 +500,9 @@
   } else if (val->IsArrayBufferView()) {
     v8::Local<v8::ArrayBufferView> view = val.As<v8::ArrayBufferView>();
     size_t byte_length = view->ByteLength();
-    auto buffer = base::MakeUnique<char[]>(byte_length);
-    view->CopyContents(buffer.get(), byte_length);
-    return base::MakeUnique<base::BinaryValue>(std::move(buffer), byte_length);
+    std::vector<char> buffer(byte_length);
+    view->CopyContents(buffer.data(), buffer.size());
+    return base::MakeUnique<base::BinaryValue>(std::move(buffer));
   } else {
     NOTREACHED() << "Only ArrayBuffer and ArrayBufferView should get here.";
     return nullptr;
diff --git a/content/child/v8_value_converter_impl.h b/content/child/v8_value_converter_impl.h
index 4b4c00a..0cc996e4 100644
--- a/content/child/v8_value_converter_impl.h
+++ b/content/child/v8_value_converter_impl.h
@@ -13,10 +13,10 @@
 #include "content/public/child/v8_value_converter.h"
 
 namespace base {
-class BinaryValue;
 class DictionaryValue;
 class ListValue;
 class Value;
+using BinaryValue = Value;
 }
 
 namespace content {
diff --git a/content/common/DEPS b/content/common/DEPS
index 1ef8e94..3c8ba65 100644
--- a/content/common/DEPS
+++ b/content/common/DEPS
@@ -11,6 +11,7 @@
   # header-only types, and some selected common code.
   "-third_party/WebKit",
   "+third_party/WebKit/public/platform/WebAddressSpace.h",
+  "+third_party/WebKit/public/platform/WebCString.h",
   "+third_party/WebKit/public/platform/WebDisplayMode.h",
   "+third_party/WebKit/public/platform/WebDragOperation.h",
   "+third_party/WebKit/public/platform/WebFeaturePolicy.h",
@@ -64,6 +65,7 @@
   "+third_party/WebKit/public/web/WebDeviceEmulationParams.h",
   "+third_party/WebKit/public/web/WebDragStatus.h",
   "+third_party/WebKit/public/web/WebFindOptions.h",
+  "+third_party/WebKit/public/web/WebFrameOwnerProperties.h",
   "+third_party/WebKit/public/web/WebFrameSerializerCacheControlPolicy.h",
   "+third_party/WebKit/public/web/WebMediaPlayerAction.h",
   "+third_party/WebKit/public/web/WebPluginAction.h",
@@ -73,6 +75,7 @@
   "+third_party/WebKit/public/web/WebSharedWorkerCreationErrors.h",
   "+third_party/WebKit/public/web/WebTextDirection.h",
   "+third_party/WebKit/public/web/WebTreeScopeType.h",
+  "+third_party/WebKit/public/web/WebWindowFeatures.h",
   "+third_party/WebKit/public/web/mac/WebScrollbarTheme.h",
   "+third_party/WebKit/public/web/win/WebFontRendering.h"
 ]
diff --git a/content/common/android/gin_java_bridge_value.cc b/content/common/android/gin_java_bridge_value.cc
index 5961461..ed3fd7e 100644
--- a/content/common/android/gin_java_bridge_value.cc
+++ b/content/common/android/gin_java_bridge_value.cc
@@ -57,11 +57,9 @@
 bool GinJavaBridgeValue::ContainsGinJavaBridgeValue(const base::Value* value) {
   if (!value->IsType(base::Value::Type::BINARY))
     return false;
-  const base::BinaryValue* binary_value =
-      reinterpret_cast<const base::BinaryValue*>(value);
-  if (binary_value->GetSize() < sizeof(Header))
+  if (value->GetSize() < sizeof(Header))
     return false;
-  base::Pickle pickle(binary_value->GetBuffer(), binary_value->GetSize());
+  base::Pickle pickle(value->GetBuffer(), value->GetSize());
   // Broken binary value: payload or header size is wrong
   if (!pickle.data() || pickle.size() - pickle.payload_size() != sizeof(Header))
     return false;
@@ -74,10 +72,8 @@
 std::unique_ptr<const GinJavaBridgeValue> GinJavaBridgeValue::FromValue(
     const base::Value* value) {
   return std::unique_ptr<const GinJavaBridgeValue>(
-      value->IsType(base::Value::Type::BINARY)
-          ? new GinJavaBridgeValue(
-                reinterpret_cast<const base::BinaryValue*>(value))
-          : NULL);
+      value->IsType(base::Value::Type::BINARY) ? new GinJavaBridgeValue(value)
+                                               : NULL);
 }
 
 GinJavaBridgeValue::Type GinJavaBridgeValue::GetType() const {
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index d8a1896..b0ed4d2 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -44,7 +44,6 @@
     "//device/nfc/android:java",
     "//device/power_save_blocker:java",
     "//device/sensors:java",
-    "//device/time_zone_monitor:java",
     "//device/usb:java",
     "//device/vibration:mojo_bindings_java",
     "//device/vibration/android:vibration_manager_java",
@@ -56,10 +55,10 @@
     "//mojo/public/java:bindings_java",
     "//mojo/public/java:system_java",
     "//net/android:net_java",
+    "//services/device:java",
     "//services/service_manager/public/interfaces:interfaces_java",
     "//services/service_manager/public/java:service_manager_java",
     "//services/shape_detection/public/interfaces:interfaces_java",
-    "//skia/public/interfaces:interfaces_java",
     "//third_party/WebKit/public:blink_headers_java",
     "//third_party/WebKit/public:mojo_bindings_java",
     "//third_party/android_tools:android_support_annotations_java",
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewClient.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewClient.java
index 6ef25210..be561324 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewClient.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewClient.java
@@ -4,8 +4,14 @@
 
 package org.chromium.content.browser;
 
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
 import android.view.KeyEvent;
 
+import org.chromium.base.Log;
+import org.chromium.base.metrics.RecordUserAction;
+
 /**
  *  Main callback class used by ContentView.
  *
@@ -14,21 +20,103 @@
  *  The memory and reference ownership of this class is unusual - see the .cc file and ContentView
  *  for more details.
  *
+ *  TODO(mkosiba): Rid this class of default implementations. This class is used by both WebView and
+ *  the browser and we don't want a the browser-specific default implementation to accidentally leak
+ *  over to WebView.
+ *
  *  WARNING: ConteViewClient is going away. Do not add new stuff in this class.
  */
 public class ContentViewClient {
+    // Tag used for logging.
+    private static final String TAG = "cr_ContentViewClient";
+
+    private static final String GEO_SCHEME = "geo";
+    private static final String TEL_SCHEME = "tel";
+    private static final String MAILTO_SCHEME = "mailto";
+
+    /**
+     * Called whenever the background color of the page changes as notified by WebKit.
+     * @param color The new ARGB color of the page background.
+     */
+    public void onBackgroundColorChanged(int color) {
+    }
+
+    /**
+     * Notifies the client of the position of the top controls.
+     * @param topControlsOffsetY The Y offset of the top controls in physical pixels.
+     * @param topContentOffsetY The Y offset of the content in physical pixels.
+     */
+    public void onTopControlsChanged(float browserControlsOffsetY, float topContentOffsetY) {}
+
+    /**
+     * Notifies the client of the position of the bottom controls.
+     * @param bottomControlsOffsetY The Y offset of the bottom controls in physical pixels.
+     * @param bottomContentOffsetY The Y offset of the content in physical pixels.
+     */
+    public void onBottomControlsChanged(float bottomControlsOffsetY, float bottomContentOffsetY) { }
+
+    public boolean shouldOverrideKeyEvent(KeyEvent event) {
+        int keyCode = event.getKeyCode();
+
+        if (!shouldPropagateKey(keyCode)) return true;
+
+        return false;
+    }
+
     /**
      * Called when an ImeEvent is sent to the page. Can be used to know when some text is entered
      * in a page.
      */
-    public void onImeEvent() {}
+    public void onImeEvent() {
+    }
 
     /**
      * Notified when the editability of the focused node changes.
      *
      * @param editable Whether the focused node is editable.
      */
-    public void onFocusedNodeEditabilityChanged(boolean editable) {}
+    public void onFocusedNodeEditabilityChanged(boolean editable) {
+    }
+
+    /**
+     * Check whether the given scheme is one of the acceptable schemes for onStartContentIntent.
+     *
+     * @param scheme The scheme to check.
+     * @return true if the scheme is okay, false if it should be blocked.
+     */
+    protected boolean isAcceptableContentIntentScheme(String scheme) {
+        return GEO_SCHEME.equals(scheme) || TEL_SCHEME.equals(scheme)
+                || MAILTO_SCHEME.equals(scheme);
+    }
+
+    /**
+     * Called when a new content intent is requested to be started.
+     */
+    public void onStartContentIntent(Context context, String intentUrl, boolean isMainFrame) {
+        Intent intent;
+        // Perform generic parsing of the URI to turn it into an Intent.
+        try {
+            intent = Intent.parseUri(intentUrl, Intent.URI_INTENT_SCHEME);
+
+            String scheme = intent.getScheme();
+            if (!isAcceptableContentIntentScheme(scheme)) {
+                Log.w(TAG, "Invalid scheme for URI %s", intentUrl);
+                return;
+            }
+
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        } catch (Exception ex) {
+            Log.w(TAG, "Bad URI %s", intentUrl, ex);
+            return;
+        }
+
+        try {
+            RecordUserAction.record("Android.ContentDetectorActivated");
+            context.startActivity(intent);
+        } catch (ActivityNotFoundException ex) {
+            Log.w(TAG, "No application can handle %s", intentUrl);
+        }
+    }
 
     /**
      * Check whether a key should be propagated to the embedder or not.
@@ -59,10 +147,33 @@
     }
 
     /**
-     * @see {@link #shouldPropagateKey(int)
+     * Returns the left system window inset in pixels. The system window inset represents the area
+     * of a full-screen window that is partially or fully obscured by the status bar, navigation
+     * bar, IME or other system windows.
+     * @return The left system window inset.
      */
-    public boolean shouldOverrideKeyEvent(KeyEvent event) {
-        return !shouldPropagateKey(event.getKeyCode());
+    public int getSystemWindowInsetLeft() {
+        return 0;
+    }
+
+    /**
+     * Returns the top system window inset in pixels. The system window inset represents the area of
+     * a full-screen window that is partially or fully obscured by the status bar, navigation bar,
+     * IME or other system windows.
+     * @return The top system window inset.
+     */
+    public int getSystemWindowInsetTop() {
+        return 0;
+    }
+
+    /**
+     * Returns the right system window inset in pixels. The system window inset represents the area
+     * of a full-screen window that is partially or fully obscured by the status bar, navigation
+     * bar, IME or other system windows.
+     * @return The right system window inset.
+     */
+    public int getSystemWindowInsetRight() {
+        return 0;
     }
 
     /**
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
index 16e8dfb..f8cbb86 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
@@ -819,6 +819,11 @@
         return mContentViewClient;
     }
 
+    @CalledByNative
+    private void onBackgroundColorChanged(int color) {
+        getContentViewClient().onBackgroundColorChanged(color);
+    }
+
     /**
      * @return Viewport width in physical pixels as set from onSizeChanged.
      */
@@ -1790,6 +1795,7 @@
             float minPageScaleFactor, float maxPageScaleFactor, float contentWidth,
             float contentHeight, float viewportWidth, float viewportHeight,
             float browserControlsHeightDp, float browserControlsShownRatio,
+            float bottomControlsHeightDp, float bottomControlsShownRatio,
             boolean isMobileOptimizedHint, boolean hasInsertionMarker,
             boolean isInsertionMarkerVisible, float insertionMarkerHorizontal,
             float insertionMarkerTop, float insertionMarkerBottom) {
@@ -1804,6 +1810,8 @@
                 mViewportHeightPix / (deviceScale * pageScaleFactor));
         final float topBarShownPix =
                 browserControlsHeightDp * deviceScale * browserControlsShownRatio;
+        final float bottomBarShownPix = bottomControlsHeightDp * deviceScale
+                * bottomControlsShownRatio;
 
         final boolean contentSizeChanged =
                 contentWidth != mRenderCoordinates.getContentWidthCss()
@@ -1819,6 +1827,8 @@
                 || scrollOffsetY != mRenderCoordinates.getScrollY();
         final boolean topBarChanged = Float.compare(topBarShownPix,
                 mRenderCoordinates.getContentOffsetYPix()) != 0;
+        final boolean bottomBarChanged = Float.compare(bottomBarShownPix, mRenderCoordinates
+                .getContentOffsetYPixBottom()) != 0;
 
         final boolean needHidePopupZoomer = contentSizeChanged || scrollChanged;
 
@@ -1837,7 +1847,7 @@
                 contentWidth, contentHeight,
                 viewportWidth, viewportHeight,
                 pageScaleFactor, minPageScaleFactor, maxPageScaleFactor,
-                topBarShownPix);
+                topBarShownPix, bottomBarShownPix);
 
         if (scrollChanged || topBarChanged) {
             for (mGestureStateListenersIterator.rewind();
@@ -1856,6 +1866,15 @@
             }
         }
 
+        if (topBarChanged) {
+            float topBarTranslate = topBarShownPix - browserControlsHeightDp * deviceScale;
+            getContentViewClient().onTopControlsChanged(topBarTranslate, topBarShownPix);
+        }
+        if (bottomBarChanged) {
+            float bottomBarTranslate = bottomControlsHeightDp * deviceScale - bottomBarShownPix;
+            getContentViewClient().onBottomControlsChanged(bottomBarTranslate, bottomBarShownPix);
+        }
+
         if (mBrowserAccessibilityManager != null) {
             mBrowserAccessibilityManager.notifyFrameInfoInitialized();
         }
@@ -2275,6 +2294,11 @@
         return mRenderCoordinates.getPageScaleFactor();
     }
 
+    @CalledByNative
+    private void startContentIntent(String contentUrl, boolean isMainFrame) {
+        getContentViewClient().onStartContentIntent(getContext(), contentUrl, isMainFrame);
+    }
+
     @Override
     public void onAccessibilityStateChanged(boolean enabled) {
         setAccessibilityState(enabled);
diff --git a/content/public/android/java/src/org/chromium/content/browser/RenderCoordinates.java b/content/public/android/java/src/org/chromium/content/browser/RenderCoordinates.java
index d4bd795..fcce242 100644
--- a/content/public/android/java/src/org/chromium/content/browser/RenderCoordinates.java
+++ b/content/public/android/java/src/org/chromium/content/browser/RenderCoordinates.java
@@ -46,6 +46,7 @@
     private float mWheelScrollFactor;
 
     private float mTopContentOffsetYPix;
+    private float mBottomContentOffsetYPix;
 
     private boolean mHasFrameInfo;
 
@@ -86,13 +87,14 @@
             float contentWidthCss, float contentHeightCss,
             float viewportWidthCss, float viewportHeightCss,
             float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor,
-            float contentOffsetYPix) {
+            float contentOffsetYPix, float contentOffsetYPixBottom) {
         mScrollXCss = scrollXCss;
         mScrollYCss = scrollYCss;
         mPageScaleFactor = pageScaleFactor;
         mMinPageScaleFactor = minPageScaleFactor;
         mMaxPageScaleFactor = maxPageScaleFactor;
         mTopContentOffsetYPix = contentOffsetYPix;
+        mBottomContentOffsetYPix = contentOffsetYPixBottom;
 
         updateContentSizeCss(contentWidthCss, contentHeightCss);
         mLastFrameViewportWidthCss = viewportWidthCss;
@@ -331,6 +333,13 @@
     }
 
     /**
+     * @return The Physical on-screen Y offset amount below the bottom controls.
+     */
+    public float getContentOffsetYPixBottom() {
+        return mBottomContentOffsetYPix;
+    }
+
+    /**
      * @return Current page scale factor (maps CSS pixels to DIP pixels).
      */
     public float getPageScaleFactor() {
diff --git a/content/public/android/java/src/org/chromium/content/browser/shapedetection/FaceDetectionImpl.java b/content/public/android/java/src/org/chromium/content/browser/shapedetection/FaceDetectionImpl.java
index 50bc619..17ff5506 100644
--- a/content/public/android/java/src/org/chromium/content/browser/shapedetection/FaceDetectionImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/shapedetection/FaceDetectionImpl.java
@@ -12,10 +12,11 @@
 import org.chromium.base.Log;
 import org.chromium.gfx.mojom.RectF;
 import org.chromium.mojo.system.MojoException;
+import org.chromium.mojo.system.SharedBufferHandle;
+import org.chromium.mojo.system.SharedBufferHandle.MapFlags;
 import org.chromium.shape_detection.mojom.FaceDetection;
 import org.chromium.shape_detection.mojom.FaceDetectionResult;
 import org.chromium.shape_detection.mojom.FaceDetectorOptions;
-import org.chromium.skia.mojom.ColorType;
 
 import java.nio.ByteBuffer;
 
@@ -35,42 +36,29 @@
     }
 
     @Override
-    public void detect(org.chromium.skia.mojom.Bitmap bitmapData, DetectResponse callback) {
-        int width = bitmapData.width;
-        int height = bitmapData.height;
+    public void detect(
+            SharedBufferHandle frameData, int width, int height, DetectResponse callback) {
         final long numPixels = (long) width * height;
         // TODO(xianglu): https://crbug.com/670028 homogeneize overflow checking.
-        if (bitmapData.pixelData == null || width <= 0 || height <= 0
-                || numPixels > (Long.MAX_VALUE / 4)) {
+        if (!frameData.isValid() || width <= 0 || height <= 0 || numPixels > (Long.MAX_VALUE / 4)) {
             Log.d(TAG, "Invalid argument(s).");
             callback.call(new FaceDetectionResult());
             return;
         }
 
-        // TODO(junwei.fu): Consider supporting other bitmap pixel formats,
-        // https://crbug.com/684921.
-        if (bitmapData.colorType != ColorType.RGBA_8888
-                && bitmapData.colorType != ColorType.BGRA_8888) {
-            Log.e(TAG, "Unsupported bitmap pixel format");
-            callback.call(new FaceDetectionResult());
-            return;
-        }
-
-        ByteBuffer imageBuffer = ByteBuffer.wrap(bitmapData.pixelData);
+        ByteBuffer imageBuffer = frameData.map(0, numPixels * 4, MapFlags.none());
         if (imageBuffer.capacity() <= 0) {
-            Log.d(TAG, "Failed to wrap from Bitmap.");
+            Log.d(TAG, "Failed to map from SharedBufferHandle.");
             callback.call(new FaceDetectionResult());
             return;
         }
 
-        // TODO(junwei.fu): Use |bitmapData| directly for |unPremultipliedBitmap| to spare a copy
-        // if the bitmap pixel format is RGB_565, the ARGB_8888 Bitmap doesn't need to be created
-        // in this case, https://crbug.com/684930.
         Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
 
         // An int array is needed to construct a Bitmap. However the Bytebuffer
-        // we get from |bitmapData| is directly allocated and does not have a supporting array.
-        // Therefore we need to copy from |imageBuffer| to create this intermediate Bitmap.
+        // we get from |sharedBufferHandle| is directly allocated and does not
+        // have a supporting array. Therefore we need to copy from |imageBuffer|
+        // to create this intermediate Bitmap.
         // TODO(xianglu): Consider worker pool as appropriate threads.
         // http://crbug.com/655814
         bitmap.copyPixelsFromBuffer(imageBuffer);
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ContentDetectionTestBase.java b/content/public/android/javatests/src/org/chromium/content/browser/ContentDetectionTestBase.java
index 9144b56..3ecf1a2 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/ContentDetectionTestBase.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/ContentDetectionTestBase.java
@@ -6,15 +6,13 @@
 
 import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
 
-import android.app.Activity;
 import android.net.Uri;
 
-import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.content.browser.test.util.DOMUtils;
 import org.chromium.content.browser.test.util.TestCallbackHelperContainer;
 import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnPageFinishedHelper;
-import org.chromium.content_shell.ShellViewAndroidDelegate.ContentIntentHandler;
+import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnStartContentIntentHelper;
 import org.chromium.content_shell_apk.ContentShellTestBase;
 
 import java.util.concurrent.TimeUnit;
@@ -27,41 +25,6 @@
     private static final long WAIT_TIMEOUT_SECONDS = scaleTimeout(10);
 
     private TestCallbackHelperContainer mCallbackHelper;
-    private TestContentIntentHandler mContentIntentHandler;
-
-    /**
-     * CallbackHelper for OnStartContentIntent.
-     */
-    private static class OnStartContentIntentHelper extends CallbackHelper {
-        private String mIntentUrl;
-        public void notifyCalled(String intentUrl) {
-            mIntentUrl = intentUrl;
-            notifyCalled();
-        }
-        public String getIntentUrl() {
-            assert getCallCount() > 0;
-            return mIntentUrl;
-        }
-    }
-
-    /**
-     * ContentIntentHandler impl to test content detection.
-     */
-    private static class TestContentIntentHandler implements ContentIntentHandler {
-        private OnStartContentIntentHelper mOnStartContentIntentHelper;
-
-        public OnStartContentIntentHelper getOnStartContentIntentHelper() {
-            if (mOnStartContentIntentHelper == null) {
-                mOnStartContentIntentHelper = new OnStartContentIntentHelper();
-            }
-            return mOnStartContentIntentHelper;
-        }
-
-        @Override
-        public void onIntentUrlReceived(String intentUrl) {
-            mOnStartContentIntentHelper.notifyCalled(intentUrl);
-        }
-    }
 
     /**
      * Returns the TestCallbackHelperContainer associated with this ContentView,
@@ -74,25 +37,12 @@
         return mCallbackHelper;
     }
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mContentIntentHandler = new TestContentIntentHandler();
-    }
-
-    @Override
-    protected void setActivity(Activity activity) {
-        super.setActivity(activity);
-        getActivity().getShellManager().getActiveShell().getViewAndroidDelegate()
-                .setContentIntentHandler(mContentIntentHandler);
-    }
-
     /**
      * Encodes the provided content string into an escaped url as intents do.
      * @param content Content to escape into a url.
      * @return Escaped url.
      */
-    protected static String urlForContent(String content) {
+    protected String urlForContent(String content) {
         return Uri.encode(content).replaceAll("%20", "+");
     }
 
@@ -112,8 +62,9 @@
      * @return The content url of the received intent or null if none.
      */
     protected String scrollAndTapExpectingIntent(String id) throws Throwable {
+        TestCallbackHelperContainer callbackHelperContainer = getTestCallbackHelperContainer();
         OnStartContentIntentHelper onStartContentIntentHelper =
-                mContentIntentHandler.getOnStartContentIntentHelper();
+                callbackHelperContainer.getOnStartContentIntentHelper();
         int currentCallCount = onStartContentIntentHelper.getCallCount();
 
         DOMUtils.clickNode(this, getContentViewCore(), id);
@@ -142,4 +93,4 @@
                 TimeUnit.SECONDS);
         getInstrumentation().waitForIdleSync();
     }
-}
\ No newline at end of file
+}
diff --git a/content/public/test/android/BUILD.gn b/content/public/test/android/BUILD.gn
index 2d1af8a..a2bd7c8 100644
--- a/content/public/test/android/BUILD.gn
+++ b/content/public/test/android/BUILD.gn
@@ -38,6 +38,7 @@
     "javatests/src/org/chromium/content/browser/test/util/KeyUtils.java",
     "javatests/src/org/chromium/content/browser/test/util/RenderProcessLimit.java",
     "javatests/src/org/chromium/content/browser/test/util/TestCallbackHelperContainer.java",
+    "javatests/src/org/chromium/content/browser/test/util/TestContentViewClient.java",
     "javatests/src/org/chromium/content/browser/test/util/TestInputMethodManagerWrapper.java",
     "javatests/src/org/chromium/content/browser/test/util/TestTouchUtils.java",
     "javatests/src/org/chromium/content/browser/test/util/TestWebContentsObserver.java",
diff --git a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/TestCallbackHelperContainer.java b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/TestCallbackHelperContainer.java
index 701106e..c06fa6c 100644
--- a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/TestCallbackHelperContainer.java
+++ b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/TestCallbackHelperContainer.java
@@ -17,9 +17,12 @@
  * This class is used to provide callback hooks for tests and related classes.
  */
 public class TestCallbackHelperContainer {
+    private final TestContentViewClient mTestContentViewClient;
     private TestWebContentsObserver mTestWebContentsObserver;
 
     public TestCallbackHelperContainer(final ContentViewCore contentViewCore) {
+        mTestContentViewClient = new TestContentViewClient();
+        contentViewCore.setContentViewClient(mTestContentViewClient);
         // TODO(yfriedman): Change callers to be executed on the UI thread. Unfortunately this is
         // super convenient as the caller is nearly always on the test thread which is fine to block
         // and it's cumbersome to keep bouncing to the UI thread.
@@ -32,6 +35,12 @@
         });
     }
 
+    protected TestCallbackHelperContainer(
+            TestContentViewClient viewClient, TestWebContentsObserver contentsObserver) {
+        mTestContentViewClient = viewClient;
+        mTestWebContentsObserver = contentsObserver;
+    }
+
     /**
      * CallbackHelper for OnPageCommitVisible.
      */
@@ -172,6 +181,21 @@
         }
     }
 
+    /**
+     * CallbackHelper for OnStartContentIntent.
+     */
+    public static class OnStartContentIntentHelper extends CallbackHelper {
+        private String mIntentUrl;
+        public void notifyCalled(String intentUrl) {
+            mIntentUrl = intentUrl;
+            notifyCalled();
+        }
+        public String getIntentUrl() {
+            assert getCallCount() > 0;
+            return mIntentUrl;
+        }
+    }
+
     public OnPageStartedHelper getOnPageStartedHelper() {
         return mTestWebContentsObserver.getOnPageStartedHelper();
     }
@@ -183,4 +207,8 @@
     public OnReceivedErrorHelper getOnReceivedErrorHelper() {
         return mTestWebContentsObserver.getOnReceivedErrorHelper();
     }
+
+    public OnStartContentIntentHelper getOnStartContentIntentHelper() {
+        return mTestContentViewClient.getOnStartContentIntentHelper();
+    }
 }
diff --git a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/TestContentViewClient.java b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/TestContentViewClient.java
new file mode 100644
index 0000000..7fda5207b
--- /dev/null
+++ b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/TestContentViewClient.java
@@ -0,0 +1,41 @@
+// 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.
+
+package org.chromium.content.browser.test.util;
+
+import android.content.Context;
+
+import org.chromium.content.browser.ContentViewClient;
+import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnStartContentIntentHelper;
+
+/**
+ * The default ContentViewClient used by ContentView tests.
+ * <p>
+ * Tests that need to supply their own ContentViewClient should do that
+ * by extending this one.
+ */
+public class TestContentViewClient extends ContentViewClient {
+
+    private final OnStartContentIntentHelper mOnStartContentIntentHelper;
+
+    public TestContentViewClient() {
+        mOnStartContentIntentHelper = new OnStartContentIntentHelper();
+    }
+
+    public OnStartContentIntentHelper getOnStartContentIntentHelper() {
+        return mOnStartContentIntentHelper;
+    }
+
+    /**
+     * ATTENTION!: When overriding the following method, be sure to call
+     * the corresponding method in the super class. Otherwise
+     * {@link CallbackHelper#waitForCallback()} methods will
+     * stop working!
+     */
+
+    @Override
+    public void onStartContentIntent(Context context, String contentUrl, boolean isMainFrame) {
+        mOnStartContentIntentHelper.notifyCalled(contentUrl);
+    }
+}
diff --git a/content/shell/android/BUILD.gn b/content/shell/android/BUILD.gn
index 278ab5f..3eed8d5 100644
--- a/content/shell/android/BUILD.gn
+++ b/content/shell/android/BUILD.gn
@@ -55,7 +55,6 @@
     "java/src/org/chromium/content_shell/Shell.java",
     "java/src/org/chromium/content_shell/ShellLayoutTestUtils.java",
     "java/src/org/chromium/content_shell/ShellManager.java",
-    "java/src/org/chromium/content_shell/ShellViewAndroidDelegate.java",
   ]
 }
 
diff --git a/content/shell/android/java/src/org/chromium/content_shell/Shell.java b/content/shell/android/java/src/org/chromium/content_shell/Shell.java
index 6215bf40..e2f82e34 100644
--- a/content/shell/android/java/src/org/chromium/content_shell/Shell.java
+++ b/content/shell/android/java/src/org/chromium/content_shell/Shell.java
@@ -36,6 +36,7 @@
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.WebContents;
+import org.chromium.ui.base.ViewAndroidDelegate;
 import org.chromium.ui.base.WindowAndroid;
 
 /**
@@ -67,7 +68,6 @@
     private long mNativeShell;
     private ContentViewRenderView mContentViewRenderView;
     private WindowAndroid mWindow;
-    private ShellViewAndroidDelegate mViewAndroidDelegate;
 
     private boolean mLoading;
     private boolean mIsFullscreen;
@@ -286,10 +286,6 @@
         }
     }
 
-    public ShellViewAndroidDelegate getViewAndroidDelegate() {
-        return mViewAndroidDelegate;
-    }
-
     /**
      * Initializes the ContentView based on the native tab contents pointer passed in.
      * @param webContents A {@link WebContents} object.
@@ -300,8 +296,8 @@
         Context context = getContext();
         mContentViewCore = new ContentViewCore(context, "");
         ContentView cv = ContentView.createContentView(context, mContentViewCore);
-        mViewAndroidDelegate = new ShellViewAndroidDelegate(cv);
-        mContentViewCore.initialize(mViewAndroidDelegate, cv, webContents, mWindow);
+        mContentViewCore.initialize(ViewAndroidDelegate.createBasicDelegate(cv), cv,
+                webContents, mWindow);
         mContentViewCore.setActionModeCallback(defaultActionCallback());
         mContentViewCore.setContentViewClient(mContentViewClient);
         mWebContents = mContentViewCore.getWebContents();
diff --git a/content/shell/android/java/src/org/chromium/content_shell/ShellViewAndroidDelegate.java b/content/shell/android/java/src/org/chromium/content_shell/ShellViewAndroidDelegate.java
deleted file mode 100644
index b1102b3..0000000
--- a/content/shell/android/java/src/org/chromium/content_shell/ShellViewAndroidDelegate.java
+++ /dev/null
@@ -1,52 +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.
-
-package org.chromium.content_shell;
-
-import android.content.Intent;
-import android.view.ViewGroup;
-
-import org.chromium.ui.base.ViewAndroidDelegate;
-
-/**
- * Implementation of the abstract class {@link ViewAndroidDelegate} for content shell.
- * Extended for testing.
- */
-public class ShellViewAndroidDelegate extends ViewAndroidDelegate {
-    private final ViewGroup mContainerView;
-    private ContentIntentHandler mContentIntentHandler;
-
-    /**
-     * Interface used to define/modify what {@link #startContentIntent} does.
-     */
-    public interface ContentIntentHandler {
-        /**
-         * Called when intent url from content is received.
-         * @param intentUrl intent url.
-         */
-        void onIntentUrlReceived(String intentUrl);
-    }
-
-    public ShellViewAndroidDelegate(ViewGroup containerView) {
-        mContainerView = containerView;
-    }
-
-    /**
-     * Set the {@link ContentIntentHandler} for {@link #starContentIntent}.
-     * @param handler Handler to inject to {@link #startContentIntent}.
-     */
-    public void setContentIntentHandler(ContentIntentHandler handler) {
-        mContentIntentHandler = handler;
-    }
-
-    @Override
-    public void startContentIntent(Intent intent, String intentUrl, boolean isMainFrame) {
-        if (mContentIntentHandler != null) mContentIntentHandler.onIntentUrlReceived(intentUrl);
-    }
-
-    @Override
-    public ViewGroup getContainerView() {
-        return mContainerView;
-    }
-}
diff --git a/content/test/gpu/generate_buildbot_json.py b/content/test/gpu/generate_buildbot_json.py
index 2bc0e42..640c34e 100755
--- a/content/test/gpu/generate_buildbot_json.py
+++ b/content/test/gpu/generate_buildbot_json.py
@@ -1258,6 +1258,8 @@
     ],
     'test': 'gles2_conform_test',
   },
+  # Face and barcode detection unit tests, which currently only run on
+  # Mac OS, and require physical hardware.
   'service_unittests': {
     'tester_configs': [
       {
@@ -1266,6 +1268,16 @@
         'os_types': ['mac'],
       },
     ],
+    'disabled_tester_configs': [
+      {
+        'swarming_dimension_sets': [
+          # These tests fail on the Mac Pros.
+          {
+            'gpu': '1002:679e',
+          },
+        ],
+      },
+    ],
     'args': [
       '--gtest_filter=*Detection*',
       '--use-gpu-in-tests'
diff --git a/device/time_zone_monitor/android/time_zone_monitor_jni_registrar.h b/device/time_zone_monitor/android/time_zone_monitor_jni_registrar.h
deleted file mode 100644
index 1581cd19..0000000
--- a/device/time_zone_monitor/android/time_zone_monitor_jni_registrar.h
+++ /dev/null
@@ -1,20 +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 DEVICE_TIME_ZONE_MONITOR_ANDROID_TIME_ZONE_MONITOR_JNI_REGISTRAR_H_
-#define DEVICE_TIME_ZONE_MONITOR_ANDROID_TIME_ZONE_MONITOR_JNI_REGISTRAR_H_
-
-#include <jni.h>
-
-#include "device/time_zone_monitor/time_zone_monitor_export.h"
-
-namespace device {
-namespace android {
-
-bool DEVICE_TIME_ZONE_MONITOR_EXPORT RegisterTimeZoneMonitorJni(JNIEnv* env);
-
-}  // namespace android
-}  // namespace device
-
-#endif  // DEVICE_TIME_ZONE_MONITOR_ANDROID_TIME_ZONE_MONITOR_JNI_REGISTRAR_H_
diff --git a/device/time_zone_monitor/public/interfaces/BUILD.gn b/device/time_zone_monitor/public/interfaces/BUILD.gn
deleted file mode 100644
index 52ca0ce..0000000
--- a/device/time_zone_monitor/public/interfaces/BUILD.gn
+++ /dev/null
@@ -1,11 +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.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-
-mojom("interfaces") {
-  sources = [
-    "time_zone_monitor.mojom",
-  ]
-}
diff --git a/device/time_zone_monitor/public/interfaces/OWNERS b/device/time_zone_monitor/public/interfaces/OWNERS
deleted file mode 100644
index 5e4aaef..0000000
--- a/device/time_zone_monitor/public/interfaces/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Changes to Mojo interfaces require a security review to avoid
-# introducing new sandbox escapes.
-per-file *.mojom=set noparent
-per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/device/time_zone_monitor/time_zone_monitor_export.h b/device/time_zone_monitor/time_zone_monitor_export.h
deleted file mode 100644
index 7889326..0000000
--- a/device/time_zone_monitor/time_zone_monitor_export.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 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_TIME_ZONE_MONITOR_DEVICE_TIME_ZONE_MONITOR_EXPORT_H_
-#define DEVICE_TIME_ZONE_MONITOR_DEVICE_TIME_ZONE_MONITOR_EXPORT_H_
-
-#if defined(COMPONENT_BUILD)
-#if defined(WIN32)
-
-#if defined(DEVICE_TIME_ZONE_MONITOR_IMPLEMENTATION)
-#define DEVICE_TIME_ZONE_MONITOR_EXPORT __declspec(dllexport)
-#else
-#define DEVICE_TIME_ZONE_MONITOR_EXPORT __declspec(dllimport)
-#endif  // defined(DEVICE_TIME_ZONE_MONITOR_IMPLEMENTATION)
-
-#else  // defined(WIN32)
-#if defined(DEVICE_TIME_ZONE_MONITOR_IMPLEMENTATION)
-#define DEVICE_TIME_ZONE_MONITOR_EXPORT __attribute__((visibility("default")))
-#else
-#define DEVICE_TIME_ZONE_MONITOR_EXPORT
-#endif
-#endif
-
-#else  // defined(COMPONENT_BUILD)
-#define DEVICE_TIME_ZONE_MONITOR_EXPORT
-#endif
-
-#endif  // DEVICE_TIME_ZONE_MONITOR_DEVICE_TIME_ZONE_MONITOR_EXPORT_H_
diff --git a/extensions/browser/api/cast_channel/cast_channel_api.cc b/extensions/browser/api/cast_channel/cast_channel_api.cc
index 29e111c..4355e0e 100644
--- a/extensions/browser/api/cast_channel/cast_channel_api.cc
+++ b/extensions/browser/api/cast_channel/cast_channel_api.cc
@@ -11,6 +11,7 @@
 #include <memory>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/json/json_writer.h"
 #include "base/lazy_instance.h"
diff --git a/extensions/browser/api/cast_channel/cast_message_util.cc b/extensions/browser/api/cast_channel/cast_message_util.cc
index 3726efd..7cdf1c5 100644
--- a/extensions/browser/api/cast_channel/cast_message_util.cc
+++ b/extensions/browser/api/cast_channel/cast_message_util.cc
@@ -37,7 +37,6 @@
   // Determine the type of the base::Value and set the message payload
   // appropriately.
   std::string data;
-  base::BinaryValue* real_value;
   switch (message.data->GetType()) {
     // JS string
     case base::Value::Type::STRING:
@@ -48,12 +47,9 @@
       break;
     // JS ArrayBuffer
     case base::Value::Type::BINARY:
-      real_value = static_cast<base::BinaryValue*>(message.data.get());
-      if (real_value->GetBuffer()) {
-        message_proto->set_payload_type(CastMessage_PayloadType_BINARY);
-        message_proto->set_payload_binary(real_value->GetBuffer(),
-                                          real_value->GetSize());
-      }
+      message_proto->set_payload_type(CastMessage_PayloadType_BINARY);
+      message_proto->set_payload_binary(message.data->GetBuffer(),
+                                        message.data->GetSize());
       break;
     default:
       // Unknown value type.  message_proto will remain uninitialized because
diff --git a/extensions/browser/api/socket/socket_api.cc b/extensions/browser/api/socket/socket_api.cc
index cbc8019..8a8f484 100644
--- a/extensions/browser/api/socket/socket_api.cc
+++ b/extensions/browser/api/socket/socket_api.cc
@@ -523,7 +523,7 @@
                 base::BinaryValue::CreateWithCopiedBuffer(io_buffer->data(),
                                                           bytes_read));
   } else {
-    result->Set(kDataKey, new base::BinaryValue());
+    result->Set(kDataKey, new base::Value(base::Value::Type::BINARY));
   }
   SetResult(std::move(result));
 
@@ -601,7 +601,7 @@
                 base::BinaryValue::CreateWithCopiedBuffer(io_buffer->data(),
                                                           bytes_read));
   } else {
-    result->Set(kDataKey, new base::BinaryValue());
+    result->Set(kDataKey, new base::Value(base::Value::Type::BINARY));
   }
   result->SetString(kAddressKey, address);
   result->SetInteger(kPortKey, port);
diff --git a/extensions/browser/api/usb/usb_api.cc b/extensions/browser/api/usb/usb_api.cc
index d0373f9..e8f0a8a 100644
--- a/extensions/browser/api/usb/usb_api.cc
+++ b/extensions/browser/api/usb/usb_api.cc
@@ -468,7 +468,7 @@
     transfer_info->Set(kDataKey, base::BinaryValue::CreateWithCopiedBuffer(
                                      data->data(), length));
   } else {
-    transfer_info->Set(kDataKey, new base::BinaryValue());
+    transfer_info->Set(kDataKey, new base::Value(base::Value::Type::BINARY));
   }
 
   if (status == device::USB_TRANSFER_COMPLETED) {
@@ -1241,8 +1241,8 @@
   std::unique_ptr<base::DictionaryValue> transfer_info(
       new base::DictionaryValue());
   transfer_info->SetInteger(kResultCodeKey, status);
-  transfer_info->Set(kDataKey,
-                     new base::BinaryValue(std::move(buffer), length));
+  transfer_info->Set(kDataKey, new base::BinaryValue(std::vector<char>(
+                                   buffer.get(), buffer.get() + length)));
   if (status == device::USB_TRANSFER_COMPLETED) {
     Respond(OneArgument(std::move(transfer_info)));
   } else {
diff --git a/extensions/renderer/argument_spec_unittest.cc b/extensions/renderer/argument_spec_unittest.cc
index 45de50c..4b94b84 100644
--- a/extensions/renderer/argument_spec_unittest.cc
+++ b/extensions/renderer/argument_spec_unittest.cc
@@ -262,7 +262,8 @@
     const char kBinarySpec[] = "{ 'type': 'binary' }";
     ArgumentSpec spec(*ValueFromString(kBinarySpec));
     // Simple case: empty ArrayBuffer -> empty BinaryValue.
-    ExpectSuccess(spec, "(new ArrayBuffer())", base::BinaryValue());
+    ExpectSuccess(spec, "(new ArrayBuffer())",
+                  base::Value(base::Value::Type::BINARY));
     {
       // A non-empty (but zero-filled) ArrayBufferView.
       const char kBuffer[] = {0, 0, 0, 0};
diff --git a/gpu/config/software_rendering_list_json.cc b/gpu/config/software_rendering_list_json.cc
index b9786b0..293566a 100644
--- a/gpu/config/software_rendering_list_json.cc
+++ b/gpu/config/software_rendering_list_json.cc
@@ -18,7 +18,7 @@
 {
   "name": "software rendering list",
   // Please update the version number whenever you change this file.
-  "version": "12.12",
+  "version": "12.13",
   "entries": [
     {
       "id": 1,
@@ -1070,6 +1070,7 @@
       },
       "vendor_id": "0x8086",
       "device_id": ["0x0116", "0x0126"],
+      "multi_gpu_category": "any",
       "features": [
         "all"
       ]
@@ -1441,6 +1442,7 @@
       },
       "vendor_id": "0x10de",
       "device_id": ["0x0407", "0x0647", "0x0863"],
+      "multi_gpu_category": "any",
       "features": [
         "all"
       ]
diff --git a/ios/chrome/browser/tabs/tab.mm b/ios/chrome/browser/tabs/tab.mm
index 8d01bd16..8e017f53 100644
--- a/ios/chrome/browser/tabs/tab.mm
+++ b/ios/chrome/browser/tabs/tab.mm
@@ -132,6 +132,7 @@
 #include "ios/web/public/interstitials/web_interstitial.h"
 #import "ios/web/public/navigation_manager.h"
 #include "ios/web/public/referrer.h"
+#import "ios/web/public/serializable_user_data_manager.h"
 #include "ios/web/public/ssl_status.h"
 #include "ios/web/public/url_scheme_util.h"
 #include "ios/web/public/url_util.h"
@@ -175,6 +176,10 @@
 class FaviconDriverObserverBridge;
 class TabInfoBarObserver;
 
+// The key under which the Tab ID is stored in the WebState's serializable user
+// data.
+NSString* const kTabIDKey = @"TabID";
+
 // Name of histogram for recording the state of the tab when the renderer is
 // terminated.
 const char kRendererTerminationStateHistogram[] =
@@ -780,8 +785,15 @@
 }
 
 - (NSString*)tabId {
-  DCHECK([self navigationManager]);
-  return [[self navigationManager]->GetSessionController() tabId];
+  DCHECK(self.webState);
+  web::SerializableUserDataManager* userDataManager =
+      web::SerializableUserDataManager::FromWebState(self.webState);
+  id<NSCoding> tabID = userDataManager->GetValueForSerializationKey(kTabIDKey);
+  if (!tabID) {
+    tabID = [[NSUUID UUID] UUIDString];
+    userDataManager->AddSerializableData(tabID, kTabIDKey);
+  }
+  return base::mac::ObjCCastStrict<NSString>(tabID);
 }
 
 - (web::WebState*)webState {
diff --git a/ios/web/navigation/crw_session_controller.h b/ios/web/navigation/crw_session_controller.h
index 19e8f8a9..d2ef342 100644
--- a/ios/web/navigation/crw_session_controller.h
+++ b/ios/web/navigation/crw_session_controller.h
@@ -30,7 +30,6 @@
 // TODO(crbug.com/454984): Remove this class.
 @interface CRWSessionController : NSObject<NSCopying>
 
-@property(nonatomic, readonly, copy) NSString* tabId;
 @property(nonatomic, readonly, assign) NSInteger currentNavigationIndex;
 @property(nonatomic, readonly, assign) NSInteger previousNavigationIndex;
 // The index of the pending item if it is in |items|, or -1 if |pendingItem|
diff --git a/ios/web/navigation/crw_session_controller.mm b/ios/web/navigation/crw_session_controller.mm
index aa98921e..2afdc55 100644
--- a/ios/web/navigation/crw_session_controller.mm
+++ b/ios/web/navigation/crw_session_controller.mm
@@ -37,7 +37,6 @@
   // the incremental merging of the two classes.
   web::NavigationManagerImpl* _navigationManager;
 
-  NSString* _tabId;  // Unique id of the tab.
   NSString* _openerId;  // Id of tab who opened this tab, empty/nil if none.
   // Navigation index of the tab which opened this tab. Do not rely on the
   // value of this member variable to indicate whether or not this tab has
@@ -92,7 +91,6 @@
 
 // TODO(rohitrao): These properties must be redefined readwrite to work around a
 // clang bug. crbug.com/228650
-@property(nonatomic, readwrite, copy) NSString* tabId;
 @property(nonatomic, readwrite, strong) NSArray* entries;
 @property(nonatomic, readwrite, strong)
     CRWSessionCertificatePolicyManager* sessionCertificatePolicyManager;
@@ -105,7 +103,6 @@
 @property(nonatomic, readwrite, assign) NSInteger openerNavigationIndex;
 @property(nonatomic, readwrite, assign) NSInteger previousNavigationIndex;
 
-- (NSString*)uniqueID;
 // Removes all entries after currentNavigationIndex_.
 - (void)clearForwardItems;
 // Discards the transient entry, if any.
@@ -125,7 +122,6 @@
 
 @implementation CRWSessionController
 
-@synthesize tabId = _tabId;
 @synthesize currentNavigationIndex = _currentNavigationIndex;
 @synthesize previousNavigationIndex = _previousNavigationIndex;
 @synthesize pendingItemIndex = _pendingItemIndex;
@@ -145,7 +141,6 @@
   self = [super init];
   if (self) {
     self.windowName = windowName;
-    _tabId = [[self uniqueID] copy];
     _openerId = [openerId copy];
     _openedByDOM = openedByDOM;
     _openerNavigationIndex = openerIndex;
@@ -167,7 +162,6 @@
                  browserState:(web::BrowserState*)browserState {
   self = [super init];
   if (self) {
-    _tabId = [[self uniqueID] copy];
     _openerId = nil;
     _browserState = browserState;
 
@@ -197,7 +191,6 @@
 
 - (id)copyWithZone:(NSZone*)zone {
   CRWSessionController* copy = [[[self class] alloc] init];
-  copy->_tabId = [_tabId copy];
   copy->_openerId = [_openerId copy];
   copy->_openedByDOM = _openedByDOM;
   copy->_openerNavigationIndex = _openerNavigationIndex;
@@ -251,13 +244,14 @@
 
 - (NSString*)description {
   return [NSString
-      stringWithFormat:
-          @"id: %@\nname: %@\nlast visit: %f\ncurrent index: %" PRIdNS
-          @"\nprevious index: %" PRIdNS @"\npending index: %" PRIdNS
-                                        @"\n%@\npending: %@\ntransient: %@\n",
-          _tabId, self.windowName, _lastVisitedTimestamp,
-          _currentNavigationIndex, _previousNavigationIndex, _pendingItemIndex,
-          _entries, _pendingEntry.get(), _transientEntry.get()];
+      stringWithFormat:@"name: %@\nlast visit: %f\ncurrent index: %" PRIdNS
+                       @"\nprevious index: %" PRIdNS
+                       @"\npending index: %" PRIdNS
+                       @"\n%@\npending: %@\ntransient: %@\n",
+                       self.windowName, _lastVisitedTimestamp,
+                       _currentNavigationIndex, _previousNavigationIndex,
+                       _pendingItemIndex, _entries, _pendingEntry.get(),
+                       _transientEntry.get()];
 }
 
 - (web::NavigationItemList)items {
@@ -698,17 +692,6 @@
 #pragma mark -
 #pragma mark Private methods
 
-- (NSString*)uniqueID {
-  CFUUIDRef uuidRef = CFUUIDCreate(NULL);
-  CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
-  CFRelease(uuidRef);
-
-  NSString* uuid =
-      [NSString stringWithString:base::mac::ObjCCastStrict<NSString>(
-                                     CFBridgingRelease(uuidStringRef))];
-  return uuid;
-}
-
 - (CRWSessionEntry*)sessionEntryWithURL:(const GURL&)url
                                referrer:(const web::Referrer&)referrer
                              transition:(ui::PageTransition)transition
diff --git a/ios/web/navigation/serializable_user_data_manager_impl.h b/ios/web/navigation/serializable_user_data_manager_impl.h
index 38fa1fb1..f29dc1f 100644
--- a/ios/web/navigation/serializable_user_data_manager_impl.h
+++ b/ios/web/navigation/serializable_user_data_manager_impl.h
@@ -26,9 +26,20 @@
   NSDictionary* data() { return data_; };
 
  private:
+  // Decodes the values that were previously encoded using CRWSessionStorage's
+  // NSCoding implementation and returns an NSDictionary using the new
+  // serialization keys.
+  // TODO(crbug.com/691800): Remove legacy support.
+  NSDictionary* GetDecodedLegacyValues(NSCoder* coder);
+
   // The dictionary passed on initialization.  After calling Decode(), this will
   // contain the data that is decoded from the NSCoder.
   base::scoped_nsobject<NSDictionary> data_;
+  // Some values that were previously persisted directly in CRWSessionStorage
+  // are now serialized using SerializableUserData, and this dictionary is used
+  // to decode these values. The keys are the legacy encoding keys, and the
+  // values are their corresponding serializable user data keys.
+  base::scoped_nsobject<NSDictionary> legacy_key_conversions_;
 };
 
 class SerializableUserDataManagerImpl : public SerializableUserDataManager {
diff --git a/ios/web/navigation/serializable_user_data_manager_impl.mm b/ios/web/navigation/serializable_user_data_manager_impl.mm
index e3dd717..b35e6f7 100644
--- a/ios/web/navigation/serializable_user_data_manager_impl.mm
+++ b/ios/web/navigation/serializable_user_data_manager_impl.mm
@@ -59,7 +59,8 @@
 }
 
 SerializableUserDataImpl::SerializableUserDataImpl()
-    : data_([[NSDictionary alloc] init]) {}
+    : data_([[NSDictionary alloc] init]),
+      legacy_key_conversions_(@{@"tabId" : @"TabID"}) {}
 
 SerializableUserDataImpl::~SerializableUserDataImpl() {}
 
@@ -71,9 +72,20 @@
 }
 
 void SerializableUserDataImpl::Decode(NSCoder* coder) {
-  NSDictionary* data = base::mac::ObjCCastStrict<NSDictionary>(
-      [coder decodeObjectForKey:kSerializedUserDataKey]);
-  data_.reset([data mutableCopy]);
+  NSMutableDictionary* data =
+      [[coder decodeObjectForKey:kSerializedUserDataKey] mutableCopy];
+  [data addEntriesFromDictionary:GetDecodedLegacyValues(coder)];
+  data_.reset([data copy]);
+}
+
+NSDictionary* SerializableUserDataImpl::GetDecodedLegacyValues(NSCoder* coder) {
+  NSMutableDictionary* legacy_values = [[NSMutableDictionary alloc] init];
+  for (NSString* legacy_key in [legacy_key_conversions_ allKeys]) {
+    id<NSCoding> value = [coder decodeObjectForKey:legacy_key];
+    NSString* new_key = [legacy_key_conversions_ objectForKey:legacy_key];
+    legacy_values[new_key] = value;
+  }
+  return legacy_values;
 }
 
 // static
diff --git a/ios/web/navigation/session_storage_builder.mm b/ios/web/navigation/session_storage_builder.mm
index ca9c3b1..e2dcc432 100644
--- a/ios/web/navigation/session_storage_builder.mm
+++ b/ios/web/navigation/session_storage_builder.mm
@@ -42,7 +42,6 @@
       [[CRWSessionStorage alloc] init];
   CRWSessionController* session_controller =
       navigation_manager->GetSessionController();
-  serialized_navigation_manager.tabID = session_controller.tabId;
   serialized_navigation_manager.openerID = session_controller.openerId;
   serialized_navigation_manager.openedByDOM = session_controller.openedByDOM;
   serialized_navigation_manager.openerNavigationIndex =
@@ -89,7 +88,6 @@
       [[CRWSessionController alloc] initWithNavigationItems:std::move(items)
                                                currentIndex:current_index
                                                browserState:nullptr]);
-  [session_controller setTabId:storage.tabID];
   [session_controller setOpenerId:storage.openerID];
   [session_controller setOpenedByDOM:storage.openedByDOM];
   [session_controller setOpenerNavigationIndex:storage.openerNavigationIndex];
diff --git a/ios/web/public/crw_session_storage.h b/ios/web/public/crw_session_storage.h
index fa74465..97d6948 100644
--- a/ios/web/public/crw_session_storage.h
+++ b/ios/web/public/crw_session_storage.h
@@ -18,7 +18,6 @@
 // TODO(crbug.com/685388): Investigate using code from the sessions component.
 @interface CRWSessionStorage : NSObject<NSCoding>
 
-@property(nonatomic, copy) NSString* tabID;
 @property(nonatomic, copy) NSString* openerID;
 @property(nonatomic, getter=isOpenedByDOM) BOOL openedByDOM;
 @property(nonatomic, assign) NSInteger openerNavigationIndex;
diff --git a/ios/web/public/crw_session_storage.mm b/ios/web/public/crw_session_storage.mm
index 7351a852..8c5b2254 100644
--- a/ios/web/public/crw_session_storage.mm
+++ b/ios/web/public/crw_session_storage.mm
@@ -21,7 +21,6 @@
 NSString* const kOpenedByDOMKey = @"openedByDOM";
 NSString* const kOpenerNavigationIndexKey = @"openerNavigationIndex";
 NSString* const kPreviousNavigationIndexKey = @"previousNavigationIndex";
-NSString* const kTabIDKey = @"tabId";
 NSString* const kWindowNameKey = @"windowName";
 }
 
@@ -34,7 +33,6 @@
 
 @implementation CRWSessionStorage
 
-@synthesize tabID = _tabID;
 @synthesize openerID = _openerID;
 @synthesize openedByDOM = _openedByDOM;
 @synthesize openerNavigationIndex = _openerNavigationIndex;
@@ -61,7 +59,6 @@
 - (instancetype)initWithCoder:(nonnull NSCoder*)decoder {
   self = [super init];
   if (self) {
-    _tabID = [[decoder decodeObjectForKey:kTabIDKey] copy];
     _windowName = [[decoder decodeObjectForKey:kWindowNameKey] copy];
     _openerID = [[decoder decodeObjectForKey:kOpenerIDKey] copy];
     _openedByDOM = [decoder decodeBoolForKey:kOpenedByDOMKey];
@@ -91,7 +88,6 @@
 }
 
 - (void)encodeWithCoder:(NSCoder*)coder {
-  [coder encodeObject:self.tabID forKey:kTabIDKey];
   [coder encodeObject:self.openerID forKey:kOpenerIDKey];
   [coder encodeBool:self.openedByDOM forKey:kOpenedByDOMKey];
   [coder encodeInt:self.openerNavigationIndex forKey:kOpenerNavigationIndexKey];
diff --git a/ipc/ipc_message_utils.cc b/ipc/ipc_message_utils.cc
index fb45218b..e7d8747 100644
--- a/ipc/ipc_message_utils.cc
+++ b/ipc/ipc_message_utils.cc
@@ -110,9 +110,7 @@
       break;
     }
     case base::Value::Type::BINARY: {
-      const base::BinaryValue* binary =
-          static_cast<const base::BinaryValue*>(value);
-      sizer->AddData(static_cast<int>(binary->GetSize()));
+      sizer->AddData(static_cast<int>(value->GetSize()));
       break;
     }
     case base::Value::Type::DICTIONARY: {
@@ -180,9 +178,7 @@
       break;
     }
     case base::Value::Type::BINARY: {
-      const base::BinaryValue* binary =
-          static_cast<const base::BinaryValue*>(value);
-      m->WriteData(binary->GetBuffer(), static_cast<int>(binary->GetSize()));
+      m->WriteData(value->GetBuffer(), static_cast<int>(value->GetSize()));
       break;
     }
     case base::Value::Type::DICTIONARY: {
diff --git a/media/base/android/media_drm_bridge.h b/media/base/android/media_drm_bridge.h
index dbb868b7..6daa240 100644
--- a/media/base/android/media_drm_bridge.h
+++ b/media/base/android/media_drm_bridge.h
@@ -17,6 +17,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner_helpers.h"
 #include "media/base/android/media_drm_bridge_cdm_context.h"
 #include "media/base/cdm_promise.h"
 #include "media/base/cdm_promise_adapter.h"
diff --git a/media/base/video_frame_metadata.cc b/media/base/video_frame_metadata.cc
index 3bc1086..0ce0b2cd 100644
--- a/media/base/video_frame_metadata.cc
+++ b/media/base/video_frame_metadata.cc
@@ -171,7 +171,7 @@
   if (dictionary_.GetWithoutPathExpansion(ToInternalKey(key),
                                           &internal_value) &&
       internal_value->GetType() == base::Value::Type::BINARY) {
-    return static_cast<const base::BinaryValue*>(internal_value);
+    return internal_value;
   }
   return nullptr;
 }
diff --git a/media/gpu/video_decode_accelerator_unittest.cc b/media/gpu/video_decode_accelerator_unittest.cc
index dea106b..cb1bed9 100644
--- a/media/gpu/video_decode_accelerator_unittest.cc
+++ b/media/gpu/video_decode_accelerator_unittest.cc
@@ -821,10 +821,7 @@
 
   // Flush decoder after all BitstreamBuffers are processed.
   if (encoded_data_next_pos_to_decode_ == encoded_data_.size()) {
-    // TODO(owenlin): We should not have to check the number of
-    // |outstanding_decodes_|. |decoder_| should be able to accept Flush()
-    // before it's done with outstanding decodes. (crbug.com/528183)
-    if (outstanding_decodes_ == 0) {
+    if (state_ != CS_FLUSHING) {
       decoder_->Flush();
       SetState(CS_FLUSHING);
     }
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
index c831ada..cbc20046 100644
--- a/mojo/public/cpp/bindings/BUILD.gn
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -124,6 +124,7 @@
     "pipe_control_message_handler.h",
     "pipe_control_message_handler_delegate.h",
     "pipe_control_message_proxy.h",
+    "raw_ptr_impl_ref_traits.h",
     "scoped_interface_endpoint_handle.h",
     "string_data_view.h",
     "string_traits.h",
@@ -132,6 +133,7 @@
     "string_traits_string_piece.h",
     "strong_associated_binding.h",
     "strong_binding.h",
+    "strong_binding_set.h",
     "struct_ptr.h",
     "sync_call_restrictions.h",
     "sync_handle_registry.h",
@@ -139,6 +141,7 @@
     "thread_safe_interface_ptr.h",
     "type_converter.h",
     "union_traits.h",
+    "unique_ptr_impl_ref_traits.h",
   ]
 
   public_deps = [
diff --git a/mojo/public/cpp/bindings/associated_binding_set.h b/mojo/public/cpp/bindings/associated_binding_set.h
index 34ab88d6e..59600c6 100644
--- a/mojo/public/cpp/bindings/associated_binding_set.h
+++ b/mojo/public/cpp/bindings/associated_binding_set.h
@@ -12,10 +12,12 @@
 
 namespace mojo {
 
-template <typename Interface>
-struct BindingSetTraits<AssociatedBinding<Interface>> {
+template <typename Interface, typename ImplRefTraits>
+struct BindingSetTraits<AssociatedBinding<Interface, ImplRefTraits>> {
   using ProxyType = AssociatedInterfacePtr<Interface>;
   using RequestType = AssociatedInterfaceRequest<Interface>;
+  using BindingType = AssociatedBinding<Interface, ImplRefTraits>;
+  using ImplPointerType = typename BindingType::ImplPointerType;
 };
 
 template <typename Interface, typename ContextType = void>
diff --git a/mojo/public/cpp/bindings/binding_set.h b/mojo/public/cpp/bindings/binding_set.h
index 5156995..919f9c0 100644
--- a/mojo/public/cpp/bindings/binding_set.h
+++ b/mojo/public/cpp/bindings/binding_set.h
@@ -23,10 +23,12 @@
 template <typename BindingType>
 struct BindingSetTraits;
 
-template <typename Interface>
-struct BindingSetTraits<Binding<Interface>> {
+template <typename Interface, typename ImplRefTraits>
+struct BindingSetTraits<Binding<Interface, ImplRefTraits>> {
   using ProxyType = InterfacePtr<Interface>;
   using RequestType = InterfaceRequest<Interface>;
+  using BindingType = Binding<Interface, ImplRefTraits>;
+  using ImplPointerType = typename BindingType::ImplPointerType;
 
   static RequestType MakeRequest(ProxyType* proxy) {
     return mojo::MakeRequest(proxy);
@@ -67,6 +69,7 @@
   using Traits = BindingSetTraits<BindingType>;
   using ProxyType = typename Traits::ProxyType;
   using RequestType = typename Traits::RequestType;
+  using ImplPointerType = typename Traits::ImplPointerType;
 
   BindingSetBase() {}
 
@@ -92,17 +95,20 @@
 
   // Adds a new binding to the set which binds |request| to |impl| with no
   // additional context.
-  BindingId AddBinding(Interface* impl, RequestType request) {
+  BindingId AddBinding(ImplPointerType impl, RequestType request) {
     static_assert(!ContextTraits::SupportsContext(),
                   "Context value required for non-void context type.");
-    return AddBindingImpl(impl, std::move(request), false);
+    return AddBindingImpl(std::move(impl), std::move(request), false);
   }
 
   // Adds a new binding associated with |context|.
-  BindingId AddBinding(Interface* impl, RequestType request, Context context) {
+  BindingId AddBinding(ImplPointerType impl,
+                       RequestType request,
+                       Context context) {
     static_assert(ContextTraits::SupportsContext(),
                   "Context value unsupported for void context type.");
-    return AddBindingImpl(impl, std::move(request), std::move(context));
+    return AddBindingImpl(std::move(impl), std::move(request),
+                          std::move(context));
   }
 
   // Removes a binding from the set. Note that this is safe to call even if the
@@ -120,10 +126,10 @@
   // Returns a proxy bound to one end of a pipe whose other end is bound to
   // |this|. If |id_storage| is not null, |*id_storage| will be set to the ID
   // of the added binding.
-  ProxyType CreateInterfacePtrAndBind(Interface* impl,
+  ProxyType CreateInterfacePtrAndBind(ImplPointerType impl,
                                       BindingId* id_storage = nullptr) {
     ProxyType proxy;
-    BindingId id = AddBinding(impl, Traits::MakeRequest(&proxy));
+    BindingId id = AddBinding(std::move(impl), Traits::MakeRequest(&proxy));
     if (id_storage)
       *id_storage = id;
     return proxy;
@@ -154,12 +160,12 @@
 
   class Entry {
    public:
-    Entry(Interface* impl,
+    Entry(ImplPointerType impl,
           RequestType request,
           BindingSetBase* binding_set,
           BindingId binding_id,
           Context context)
-        : binding_(impl, std::move(request)),
+        : binding_(std::move(impl), std::move(request)),
           binding_set_(binding_set),
           binding_id_(binding_id),
           context_(std::move(context)) {
@@ -216,13 +222,13 @@
       pre_dispatch_handler_.Run(*context);
   }
 
-  BindingId AddBindingImpl(Interface* impl,
+  BindingId AddBindingImpl(ImplPointerType impl,
                            RequestType request,
                            Context context) {
     BindingId id = next_binding_id_++;
     DCHECK_GE(next_binding_id_, 0u);
-    auto entry = base::MakeUnique<Entry>(
-        impl, std::move(request), this, id, std::move(context));
+    auto entry = base::MakeUnique<Entry>(std::move(impl), std::move(request),
+                                         this, id, std::move(context));
     bindings_.insert(std::make_pair(id, std::move(entry)));
     return id;
   }
diff --git a/mojo/public/cpp/bindings/strong_binding_set.h b/mojo/public/cpp/bindings/strong_binding_set.h
new file mode 100644
index 0000000..f6bcd52
--- /dev/null
+++ b/mojo/public/cpp/bindings/strong_binding_set.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_STRONG_BINDING_SET_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRONG_BINDING_SET_H_
+
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/unique_ptr_impl_ref_traits.h"
+
+namespace mojo {
+
+// This class manages a set of bindings. When the pipe a binding is bound to is
+// disconnected, the binding is automatically destroyed and removed from the
+// set, and the interface implementation is deleted. When the StrongBindingSet
+// is destructed, all outstanding bindings in the set are destroyed and all the
+// bound interface implementations are automatically deleted.
+template <typename Interface, typename ContextType = void>
+using StrongBindingSet =
+    BindingSetBase<Interface,
+                   Binding<Interface, UniquePtrImplRefTraits<Interface>>,
+                   ContextType>;
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_STRONG_BINDING_SET_H_
diff --git a/mojo/public/cpp/bindings/tests/binding_set_unittest.cc b/mojo/public/cpp/bindings/tests/binding_set_unittest.cc
index 790671a..b7f55b1a 100644
--- a/mojo/public/cpp/bindings/tests/binding_set_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/binding_set_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/run_loop.h"
 #include "mojo/public/cpp/bindings/associated_binding_set.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/strong_binding_set.h"
 #include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h"
 #include "mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -349,6 +350,67 @@
   run_loop.Run();
 }
 
+class PingInstanceCounter : public PingService {
+ public:
+  PingInstanceCounter() { ++instance_count; }
+  ~PingInstanceCounter() override { --instance_count; }
+
+  void Ping(const PingCallback& callback) override {}
+
+  static int instance_count;
+};
+int PingInstanceCounter::instance_count = 0;
+
+TEST_F(BindingSetTest, StrongBinding_Destructor) {
+  PingServicePtr ping_a, ping_b;
+  auto bindings = base::MakeUnique<StrongBindingSet<PingService>>();
+
+  bindings->AddBinding(base::MakeUnique<PingInstanceCounter>(),
+                       mojo::MakeRequest(&ping_a));
+  EXPECT_EQ(1, PingInstanceCounter::instance_count);
+
+  bindings->AddBinding(base::MakeUnique<PingInstanceCounter>(),
+                       mojo::MakeRequest(&ping_b));
+  EXPECT_EQ(2, PingInstanceCounter::instance_count);
+
+  bindings.reset();
+  EXPECT_EQ(0, PingInstanceCounter::instance_count);
+}
+
+TEST_F(BindingSetTest, StrongBinding_ConnectionError) {
+  PingServicePtr ping_a, ping_b;
+  StrongBindingSet<PingService> bindings;
+  bindings.AddBinding(base::MakeUnique<PingInstanceCounter>(),
+                      mojo::MakeRequest(&ping_a));
+  bindings.AddBinding(base::MakeUnique<PingInstanceCounter>(),
+                      mojo::MakeRequest(&ping_b));
+  EXPECT_EQ(2, PingInstanceCounter::instance_count);
+
+  ping_a.reset();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, PingInstanceCounter::instance_count);
+
+  ping_b.reset();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, PingInstanceCounter::instance_count);
+}
+
+TEST_F(BindingSetTest, StrongBinding_RemoveBinding) {
+  PingServicePtr ping_a, ping_b;
+  StrongBindingSet<PingService> bindings;
+  BindingId binding_id_a = bindings.AddBinding(
+      base::MakeUnique<PingInstanceCounter>(), mojo::MakeRequest(&ping_a));
+  BindingId binding_id_b = bindings.AddBinding(
+      base::MakeUnique<PingInstanceCounter>(), mojo::MakeRequest(&ping_b));
+  EXPECT_EQ(2, PingInstanceCounter::instance_count);
+
+  EXPECT_TRUE(bindings.RemoveBinding(binding_id_a));
+  EXPECT_EQ(1, PingInstanceCounter::instance_count);
+
+  EXPECT_TRUE(bindings.RemoveBinding(binding_id_b));
+  EXPECT_EQ(0, PingInstanceCounter::instance_count);
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/unique_ptr_impl_ref_traits.h b/mojo/public/cpp/bindings/unique_ptr_impl_ref_traits.h
new file mode 100644
index 0000000..f1ac097
--- /dev/null
+++ b/mojo/public/cpp/bindings/unique_ptr_impl_ref_traits.h
@@ -0,0 +1,22 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_UNIQUE_PTR_IMPL_REF_TRAITS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_UNIQUE_PTR_IMPL_REF_TRAITS_H_
+
+namespace mojo {
+
+// Traits for a binding's implementation reference type.
+// This corresponds to a unique_ptr reference type.
+template <typename Interface>
+struct UniquePtrImplRefTraits {
+  using PointerType = std::unique_ptr<Interface>;
+
+  static bool IsNull(const PointerType& ptr) { return !ptr; }
+  static Interface* GetRawPointer(PointerType* ptr) { return ptr->get(); }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_UNIQUE_PTR_IMPL_REF_TRAITS_H_
diff --git a/ppapi/shared_impl/private/ppb_x509_certificate_private_shared.cc b/ppapi/shared_impl/private/ppb_x509_certificate_private_shared.cc
index 4a16385..e69aecc 100644
--- a/ppapi/shared_impl/private/ppb_x509_certificate_private_shared.cc
+++ b/ppapi/shared_impl/private/ppb_x509_certificate_private_shared.cc
@@ -64,10 +64,8 @@
       return StringVar::StringToPPVar(val);
     }
     case base::Value::Type::BINARY: {
-      const base::BinaryValue* binary =
-          static_cast<const base::BinaryValue*>(value);
-      uint32_t size = static_cast<uint32_t>(binary->GetSize());
-      const char* buffer = binary->GetBuffer();
+      uint32_t size = static_cast<uint32_t>(value->GetSize());
+      const char* buffer = value->GetBuffer();
       PP_Var array_buffer =
           PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(size,
                                                                      buffer);
diff --git a/services/device/BUILD.gn b/services/device/BUILD.gn
index 44681f24..1fbbf47 100644
--- a/services/device/BUILD.gn
+++ b/services/device/BUILD.gn
@@ -5,6 +5,10 @@
 import("//services/service_manager/public/cpp/service.gni")
 import("//services/service_manager/public/service_manifest.gni")
 
+if (is_android) {
+  import("//build/config/android/rules.gni")
+}
+
 source_set("lib") {
   sources = [
     "device_service.cc",
@@ -13,17 +17,17 @@
 
   deps = [
     "//base",
-    "//device/time_zone_monitor",
     "//services/device/power_monitor",
+    "//services/device/time_zone_monitor",
     "//services/service_manager/public/cpp",
   ]
 
-  public_deps = [
-    # TODO(blundell): This dep shouldn't be necessary at all,
-    # but content_shell fails to link in the component build if
-    # this isn't here as a public_dep.
-    "//device/time_zone_monitor/public/interfaces",
-  ]
+  if (is_android) {
+    sources += [
+      "//services/device/android/register_jni.cc",
+      "//services/device/android/register_jni.h",
+    ]
+  }
 }
 
 source_set("tests") {
@@ -48,3 +52,12 @@
   name = "device"
   source = "manifest.json"
 }
+
+if (is_android) {
+  android_library("java") {
+    java_files = [ "time_zone_monitor/android/java/src/org/chromium/device/time_zone_monitor/TimeZoneMonitor.java" ]
+    deps = [
+      "//base:base_java",
+    ]
+  }
+}
diff --git a/services/device/android/register_jni.cc b/services/device/android/register_jni.cc
new file mode 100644
index 0000000..345a8699
--- /dev/null
+++ b/services/device/android/register_jni.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 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 "services/device/android/register_jni.h"
+
+#include "base/android/jni_android.h"
+#include "services/device/time_zone_monitor/android/time_zone_monitor_jni_registrar.h"
+
+namespace device {
+
+bool EnsureJniRegistered() {
+  static bool g_jni_init_done = false;
+
+  if (!g_jni_init_done) {
+    JNIEnv* env = base::android::AttachCurrentThread();
+
+    if (!android::RegisterTimeZoneMonitorJni(env))
+      return false;
+
+    g_jni_init_done = true;
+  }
+
+  return true;
+}
+
+}  // namespace device
diff --git a/services/device/android/register_jni.h b/services/device/android/register_jni.h
new file mode 100644
index 0000000..5874503
--- /dev/null
+++ b/services/device/android/register_jni.h
@@ -0,0 +1,16 @@
+// Copyright (c) 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 SERVICES_DEVICE_ANDROID_REGISTER_JNI_H_
+#define SERVICES_DEVICE_ANDROID_REGISTER_JNI_H_
+
+namespace device {
+
+// Register all JNI bindings necessary for device service.
+// Is expected to be called on initialization of device service.
+bool EnsureJniRegistered();
+
+}  // namespace device
+
+#endif  // SERVICES_DEVICE_ANDROID_REGISTER_JNI_H_
diff --git a/services/device/device_service.cc b/services/device/device_service.cc
index 4cf73987..7a225c4 100644
--- a/services/device/device_service.cc
+++ b/services/device/device_service.cc
@@ -8,15 +8,26 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "device/time_zone_monitor/time_zone_monitor.h"
 #include "services/device/power_monitor/power_monitor_message_broadcaster.h"
+#include "services/device/time_zone_monitor/time_zone_monitor.h"
 #include "services/service_manager/public/cpp/connection.h"
 #include "services/service_manager/public/cpp/interface_registry.h"
 
+#if defined(OS_ANDROID)
+#include "services/device/android/register_jni.h"
+#endif
+
 namespace device {
 
 std::unique_ptr<service_manager::Service> CreateDeviceService(
     scoped_refptr<base::SingleThreadTaskRunner> file_task_runner) {
+#if defined(OS_ANDROID)
+  if (!EnsureJniRegistered()) {
+    DLOG(ERROR) << "Failed to register JNI for Device Service";
+    return nullptr;
+  }
+#endif
+
   return base::MakeUnique<DeviceService>(std::move(file_task_runner));
 }
 
diff --git a/services/device/device_service.h b/services/device/device_service.h
index bb5fe94..a8ef5070 100644
--- a/services/device/device_service.h
+++ b/services/device/device_service.h
@@ -6,9 +6,9 @@
 #define SERVICES_DEVICE_DEVICE_SERVICE_H_
 
 #include "base/memory/ref_counted.h"
-#include "device/time_zone_monitor/public/interfaces/time_zone_monitor.mojom.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "services/device/public/interfaces/power_monitor.mojom.h"
+#include "services/device/public/interfaces/time_zone_monitor.mojom.h"
 #include "services/service_manager/public/cpp/interface_factory.h"
 #include "services/service_manager/public/cpp/service.h"
 
diff --git a/services/device/public/interfaces/BUILD.gn b/services/device/public/interfaces/BUILD.gn
index f207c69..5ef17d7 100644
--- a/services/device/public/interfaces/BUILD.gn
+++ b/services/device/public/interfaces/BUILD.gn
@@ -7,6 +7,7 @@
 mojom("interfaces") {
   sources = [
     "power_monitor.mojom",
+    "time_zone_monitor.mojom",
   ]
 
   public_deps = [
diff --git a/device/time_zone_monitor/public/interfaces/time_zone_monitor.mojom b/services/device/public/interfaces/time_zone_monitor.mojom
similarity index 100%
rename from device/time_zone_monitor/public/interfaces/time_zone_monitor.mojom
rename to services/device/public/interfaces/time_zone_monitor.mojom
diff --git a/device/time_zone_monitor/BUILD.gn b/services/device/time_zone_monitor/BUILD.gn
similarity index 71%
rename from device/time_zone_monitor/BUILD.gn
rename to services/device/time_zone_monitor/BUILD.gn
index 9369844..ecef1fc3 100644
--- a/device/time_zone_monitor/BUILD.gn
+++ b/services/device/time_zone_monitor/BUILD.gn
@@ -8,23 +8,20 @@
   import("//build/config/android/rules.gni")  # For generate_jni().
 }
 
-component("time_zone_monitor") {
+source_set("time_zone_monitor") {
+  visibility = [ "//services/device:lib" ]
+
   sources = [
-    "android/time_zone_monitor_jni_registrar.cc",
-    "android/time_zone_monitor_jni_registrar.h",
     "time_zone_monitor.cc",
     "time_zone_monitor.h",
     "time_zone_monitor_android.cc",
     "time_zone_monitor_android.h",
     "time_zone_monitor_chromeos.cc",
-    "time_zone_monitor_export.h",
     "time_zone_monitor_linux.cc",
     "time_zone_monitor_mac.mm",
     "time_zone_monitor_win.cc",
   ]
 
-  defines = [ "DEVICE_TIME_ZONE_MONITOR_IMPLEMENTATION" ]
-
   deps = [
     "//base",
     "//mojo/public/cpp/bindings",
@@ -32,10 +29,14 @@
   ]
 
   public_deps = [
-    "//device/time_zone_monitor/public/interfaces",
+    "//services/device/public/interfaces",
   ]
 
   if (is_android) {
+    sources += [
+      "android/time_zone_monitor_jni_registrar.cc",
+      "android/time_zone_monitor_jni_registrar.h",
+    ]
     deps += [ ":time_zone_monitor_jni_headers" ]
   }
 
@@ -54,16 +55,10 @@
 
 if (is_android) {
   generate_jni("time_zone_monitor_jni_headers") {
+    visibility = [ ":time_zone_monitor" ]
     sources = [
       "android/java/src/org/chromium/device/time_zone_monitor/TimeZoneMonitor.java",
     ]
     jni_package = "time_zone_monitor"
   }
-
-  android_library("java") {
-    java_files = [ "android/java/src/org/chromium/device/time_zone_monitor/TimeZoneMonitor.java" ]
-    deps = [
-      "//base:base_java",
-    ]
-  }
 }
diff --git a/device/time_zone_monitor/DEPS b/services/device/time_zone_monitor/DEPS
similarity index 100%
rename from device/time_zone_monitor/DEPS
rename to services/device/time_zone_monitor/DEPS
diff --git a/device/time_zone_monitor/OWNERS b/services/device/time_zone_monitor/OWNERS
similarity index 100%
rename from device/time_zone_monitor/OWNERS
rename to services/device/time_zone_monitor/OWNERS
diff --git a/device/time_zone_monitor/android/java/src/org/chromium/device/time_zone_monitor/TimeZoneMonitor.java b/services/device/time_zone_monitor/android/java/src/org/chromium/device/time_zone_monitor/TimeZoneMonitor.java
similarity index 100%
rename from device/time_zone_monitor/android/java/src/org/chromium/device/time_zone_monitor/TimeZoneMonitor.java
rename to services/device/time_zone_monitor/android/java/src/org/chromium/device/time_zone_monitor/TimeZoneMonitor.java
diff --git a/device/time_zone_monitor/android/time_zone_monitor_jni_registrar.cc b/services/device/time_zone_monitor/android/time_zone_monitor_jni_registrar.cc
similarity index 80%
rename from device/time_zone_monitor/android/time_zone_monitor_jni_registrar.cc
rename to services/device/time_zone_monitor/android/time_zone_monitor_jni_registrar.cc
index 55ab5d5a..6385d4f 100644
--- a/device/time_zone_monitor/android/time_zone_monitor_jni_registrar.cc
+++ b/services/device/time_zone_monitor/android/time_zone_monitor_jni_registrar.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "device/time_zone_monitor/android/time_zone_monitor_jni_registrar.h"
+#include "services/device/time_zone_monitor/android/time_zone_monitor_jni_registrar.h"
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_registrar.h"
-#include "device/time_zone_monitor/time_zone_monitor_android.h"
+#include "services/device/time_zone_monitor/time_zone_monitor_android.h"
 
 namespace device {
 namespace android {
diff --git a/services/device/time_zone_monitor/android/time_zone_monitor_jni_registrar.h b/services/device/time_zone_monitor/android/time_zone_monitor_jni_registrar.h
new file mode 100644
index 0000000..e6442ee
--- /dev/null
+++ b/services/device/time_zone_monitor/android/time_zone_monitor_jni_registrar.h
@@ -0,0 +1,18 @@
+// 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 SERVICES_DEVICE_TIME_ZONE_MONITOR_ANDROID_TIME_ZONE_MONITOR_JNI_REGISTRAR_H_
+#define SERVICES_DEVICE_TIME_ZONE_MONITOR_ANDROID_TIME_ZONE_MONITOR_JNI_REGISTRAR_H_
+
+#include <jni.h>
+
+namespace device {
+namespace android {
+
+bool RegisterTimeZoneMonitorJni(JNIEnv* env);
+
+}  // namespace android
+}  // namespace device
+
+#endif  // SERVICES_DEVICE_TIME_ZONE_MONITOR_ANDROID_TIME_ZONE_MONITOR_JNI_REGISTRAR_H_
diff --git a/device/time_zone_monitor/time_zone_monitor.cc b/services/device/time_zone_monitor/time_zone_monitor.cc
similarity index 96%
rename from device/time_zone_monitor/time_zone_monitor.cc
rename to services/device/time_zone_monitor/time_zone_monitor.cc
index 0e671b4..52e2a52 100644
--- a/device/time_zone_monitor/time_zone_monitor.cc
+++ b/services/device/time_zone_monitor/time_zone_monitor.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "device/time_zone_monitor/time_zone_monitor.h"
+#include "services/device/time_zone_monitor/time_zone_monitor.h"
 
 #include "base/logging.h"
 #include "build/build_config.h"
diff --git a/device/time_zone_monitor/time_zone_monitor.h b/services/device/time_zone_monitor/time_zone_monitor.h
similarity index 81%
rename from device/time_zone_monitor/time_zone_monitor.h
rename to services/device/time_zone_monitor/time_zone_monitor.h
index f79f94a..730aa5d0 100644
--- a/device/time_zone_monitor/time_zone_monitor.h
+++ b/services/device/time_zone_monitor/time_zone_monitor.h
@@ -2,17 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_TIME_ZONE_MONITOR_H_
-#define CONTENT_BROWSER_TIME_ZONE_MONITOR_H_
+#ifndef SERVICES_DEVICE_TIME_ZONE_MONITOR_TIME_ZONE_MONITOR_H_
+#define SERVICES_DEVICE_TIME_ZONE_MONITOR_TIME_ZONE_MONITOR_H_
 
 #include <memory>
 
 #include "base/macros.h"
 #include "base/threading/thread_checker.h"
-#include "device/time_zone_monitor/public/interfaces/time_zone_monitor.mojom.h"
-#include "device/time_zone_monitor/time_zone_monitor_export.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
+#include "services/device/public/interfaces/time_zone_monitor.mojom.h"
 
 template <class T>
 class scoped_refptr;
@@ -45,13 +44,12 @@
   // Returns a new TimeZoneMonitor object (likely a subclass) specific to the
   // platform. Inject |file_task_runner| to enable running blocking file
   // operations on it when necessary.
-  DEVICE_TIME_ZONE_MONITOR_EXPORT static std::unique_ptr<TimeZoneMonitor>
-  Create(scoped_refptr<base::SequencedTaskRunner> file_task_runner);
+  static std::unique_ptr<TimeZoneMonitor> Create(
+      scoped_refptr<base::SequencedTaskRunner> file_task_runner);
 
   ~TimeZoneMonitor() override;
 
-  DEVICE_TIME_ZONE_MONITOR_EXPORT void Bind(
-      device::mojom::TimeZoneMonitorRequest request);
+  void Bind(device::mojom::TimeZoneMonitorRequest request);
 
  protected:
   TimeZoneMonitor();
@@ -72,4 +70,4 @@
 
 }  // namespace device
 
-#endif  // CONTENT_BROWSER_TIME_ZONE_MONITOR_H_
+#endif  // SERVICES_DEVICE_TIME_ZONE_MONITOR_TIME_ZONE_MONITOR_H_
diff --git a/device/time_zone_monitor/time_zone_monitor_android.cc b/services/device/time_zone_monitor/time_zone_monitor_android.cc
similarity index 94%
rename from device/time_zone_monitor/time_zone_monitor_android.cc
rename to services/device/time_zone_monitor/time_zone_monitor_android.cc
index 0d1ea625..b15a90c 100644
--- a/device/time_zone_monitor/time_zone_monitor_android.cc
+++ b/services/device/time_zone_monitor/time_zone_monitor_android.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "device/time_zone_monitor/time_zone_monitor_android.h"
+#include "services/device/time_zone_monitor/time_zone_monitor_android.h"
 
 #include "base/android/context_utils.h"
 #include "base/android/jni_android.h"
diff --git a/device/time_zone_monitor/time_zone_monitor_android.h b/services/device/time_zone_monitor/time_zone_monitor_android.h
similarity index 75%
rename from device/time_zone_monitor/time_zone_monitor_android.h
rename to services/device/time_zone_monitor/time_zone_monitor_android.h
index 576b227..76cc17d 100644
--- a/device/time_zone_monitor/time_zone_monitor_android.h
+++ b/services/device/time_zone_monitor/time_zone_monitor_android.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_TIME_ZONE_MONITOR_ANDROID_H_
-#define CONTENT_BROWSER_TIME_ZONE_MONITOR_ANDROID_H_
+#ifndef SERVICES_DEVICE_TIME_ZONE_MONITOR_TIME_ZONE_MONITOR_ANDROID_H_
+#define SERVICES_DEVICE_TIME_ZONE_MONITOR_TIME_ZONE_MONITOR_ANDROID_H_
 
-#include "device/time_zone_monitor/time_zone_monitor.h"
+#include "services/device/time_zone_monitor/time_zone_monitor.h"
 
 #include <jni.h>
 
@@ -36,4 +36,4 @@
 
 }  // namespace device
 
-#endif  // CONTENT_BROWSER_TIME_ZONE_MONITOR_ANDROID_H_
+#endif  // SERVICES_DEVICE_TIME_ZONE_MONITOR_TIME_ZONE_MONITOR_ANDROID_H_
diff --git a/device/time_zone_monitor/time_zone_monitor_chromeos.cc b/services/device/time_zone_monitor/time_zone_monitor_chromeos.cc
similarity index 94%
rename from device/time_zone_monitor/time_zone_monitor_chromeos.cc
rename to services/device/time_zone_monitor/time_zone_monitor_chromeos.cc
index 0c59b44..b1b7c010d 100644
--- a/device/time_zone_monitor/time_zone_monitor_chromeos.cc
+++ b/services/device/time_zone_monitor/time_zone_monitor_chromeos.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "device/time_zone_monitor/time_zone_monitor.h"
+#include "services/device/time_zone_monitor/time_zone_monitor.h"
 
 #include "base/macros.h"
 #include "chromeos/settings/timezone_settings.h"
diff --git a/device/time_zone_monitor/time_zone_monitor_linux.cc b/services/device/time_zone_monitor/time_zone_monitor_linux.cc
similarity index 97%
rename from device/time_zone_monitor/time_zone_monitor_linux.cc
rename to services/device/time_zone_monitor/time_zone_monitor_linux.cc
index 9aee0b2..22ab258b 100644
--- a/device/time_zone_monitor/time_zone_monitor_linux.cc
+++ b/services/device/time_zone_monitor/time_zone_monitor_linux.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "device/time_zone_monitor/time_zone_monitor.h"
+#include "services/device/time_zone_monitor/time_zone_monitor.h"
 
 #include <stddef.h>
 #include <stdlib.h>
@@ -76,9 +76,7 @@
  private:
   friend class base::RefCountedThreadSafe<TimeZoneMonitorLinuxImpl>;
 
-  ~TimeZoneMonitorLinuxImpl() {
-    DCHECK(!owner_);
-  }
+  ~TimeZoneMonitorLinuxImpl() { DCHECK(!owner_); }
 
   void StartWatchingOnFileThread() {
     base::ThreadRestrictions::AssertIOAllowed();
diff --git a/device/time_zone_monitor/time_zone_monitor_mac.mm b/services/device/time_zone_monitor/time_zone_monitor_mac.mm
similarity index 94%
rename from device/time_zone_monitor/time_zone_monitor_mac.mm
rename to services/device/time_zone_monitor/time_zone_monitor_mac.mm
index 8cccb41..761bc1f1 100644
--- a/device/time_zone_monitor/time_zone_monitor_mac.mm
+++ b/services/device/time_zone_monitor/time_zone_monitor_mac.mm
@@ -5,7 +5,7 @@
 #import <Foundation/Foundation.h>
 
 #include "base/macros.h"
-#include "device/time_zone_monitor/time_zone_monitor.h"
+#include "services/device/time_zone_monitor/time_zone_monitor.h"
 
 namespace device {
 
diff --git a/device/time_zone_monitor/time_zone_monitor_win.cc b/services/device/time_zone_monitor/time_zone_monitor_win.cc
similarity index 94%
rename from device/time_zone_monitor/time_zone_monitor_win.cc
rename to services/device/time_zone_monitor/time_zone_monitor_win.cc
index d698911d..b4a2695 100644
--- a/device/time_zone_monitor/time_zone_monitor_win.cc
+++ b/services/device/time_zone_monitor/time_zone_monitor_win.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "device/time_zone_monitor/time_zone_monitor.h"
+#include "services/device/time_zone_monitor/time_zone_monitor.h"
 
 #include <windows.h>
 
diff --git a/services/shape_detection/DEPS b/services/shape_detection/DEPS
index 1987787..a2872a2 100644
--- a/services/shape_detection/DEPS
+++ b/services/shape_detection/DEPS
@@ -1,6 +1,5 @@
 include_rules = [
   "+media/capture/video/scoped_result_callback.h",
-  "+skia/ext/skia_utils_mac.h",
   "+third_party/skia/include",
   "+ui/gfx/codec",
   "+ui/gl/gl_switches.h"
diff --git a/services/shape_detection/barcode_detection_impl_mac.h b/services/shape_detection/barcode_detection_impl_mac.h
index 6a5609a..a7779897 100644
--- a/services/shape_detection/barcode_detection_impl_mac.h
+++ b/services/shape_detection/barcode_detection_impl_mac.h
@@ -7,7 +7,6 @@
 
 #include "base/mac/scoped_nsobject.h"
 #include "services/shape_detection/public/interfaces/barcodedetection.mojom.h"
-#include "third_party/skia/include/core/SkBitmap.h"
 
 @class CIDetector;
 
@@ -19,7 +18,9 @@
   BarcodeDetectionImplMac();
   ~BarcodeDetectionImplMac() override;
 
-  void Detect(const SkBitmap& bitmap,
+  void Detect(mojo::ScopedSharedBufferHandle frame_data,
+              uint32_t width,
+              uint32_t height,
               const shape_detection::mojom::BarcodeDetection::DetectCallback&
                   callback) override;
 
diff --git a/services/shape_detection/barcode_detection_impl_mac.mm b/services/shape_detection/barcode_detection_impl_mac.mm
index 2abbc03..ec0ac0b 100644
--- a/services/shape_detection/barcode_detection_impl_mac.mm
+++ b/services/shape_detection/barcode_detection_impl_mac.mm
@@ -50,20 +50,22 @@
 
 BarcodeDetectionImplMac::~BarcodeDetectionImplMac() {}
 
-void BarcodeDetectionImplMac::Detect(const SkBitmap& bitmap,
+void BarcodeDetectionImplMac::Detect(mojo::ScopedSharedBufferHandle frame_data,
+                                     uint32_t width,
+                                     uint32_t height,
                                      const DetectCallback& callback) {
   media::ScopedResultCallback<DetectCallback> scoped_callback(
       base::Bind(&RunCallbackWithBarcodes, callback),
       base::Bind(&RunCallbackWithNoBarcodes));
 
-  base::scoped_nsobject<CIImage> ci_image = CreateCIImageFromSkBitmap(bitmap);
+  base::scoped_nsobject<CIImage> ci_image =
+      CreateCIImageFromSharedMemory(std::move(frame_data), width, height);
   if (!ci_image)
     return;
 
   NSArray* const features = [detector_ featuresInImage:ci_image];
 
   std::vector<mojom::BarcodeDetectionResultPtr> results;
-  const int height = bitmap.height();
   for (CIQRCodeFeature* const f in features) {
     shape_detection::mojom::BarcodeDetectionResultPtr result =
         shape_detection::mojom::BarcodeDetectionResult::New();
diff --git a/services/shape_detection/barcode_detection_impl_mac_unittest.mm b/services/shape_detection/barcode_detection_impl_mac_unittest.mm
index d3c305c..55c2cd27 100644
--- a/services/shape_detection/barcode_detection_impl_mac_unittest.mm
+++ b/services/shape_detection/barcode_detection_impl_mac_unittest.mm
@@ -12,7 +12,6 @@
 #include "base/run_loop.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/utils/mac/SkCGUtils.h"
 #include "ui/gl/gl_switches.h"
 
 namespace shape_detection {
@@ -65,6 +64,7 @@
 
   const gfx::Size size([qr_code_image extent].size.width,
                        [qr_code_image extent].size.height);
+  const int num_bytes = size.GetArea() * 4 /* bytes per pixel */;
 
   base::scoped_nsobject<CIContext> context([[CIContext alloc] init]);
 
@@ -73,16 +73,30 @@
   EXPECT_EQ(static_cast<size_t>(size.width()), CGImageGetWidth(cg_image));
   EXPECT_EQ(static_cast<size_t>(size.height()), CGImageGetHeight(cg_image));
 
-  SkBitmap bitmap;
-  ASSERT_TRUE(SkCreateBitmapFromCGImage(&bitmap, cg_image));
+  base::ScopedCFTypeRef<CFDataRef> raw_cg_image_data(
+      CGDataProviderCopyData(CGImageGetDataProvider(cg_image)));
+  EXPECT_TRUE(CFDataGetBytePtr(raw_cg_image_data));
+  EXPECT_EQ(num_bytes, CFDataGetLength(raw_cg_image_data));
+
+  // Generate a new ScopedSharedBufferHandle of the aproppriate size, map it and
+  // copy the generated qr code image pixels into it.
+  mojo::ScopedSharedBufferHandle handle =
+      mojo::SharedBufferHandle::Create(num_bytes);
+  ASSERT_TRUE(handle->is_valid());
+
+  mojo::ScopedSharedBufferMapping mapping = handle->Map(num_bytes);
+  ASSERT_TRUE(mapping);
+
+  memcpy(mapping.get(), CFDataGetBytePtr(raw_cg_image_data), num_bytes);
 
   base::RunLoop run_loop;
   base::Closure quit_closure = run_loop.QuitClosure();
   // Send the image Detect() and expect the response in callback.
   EXPECT_CALL(*this, Detection(1, kInfoString))
       .WillOnce(RunClosure(quit_closure));
-  impl_.Detect(bitmap, base::Bind(&BarcodeDetectionImplMacTest::DetectCallback,
-                                  base::Unretained(this)));
+  impl_.Detect(std::move(handle), size.width(), size.height(),
+               base::Bind(&BarcodeDetectionImplMacTest::DetectCallback,
+                          base::Unretained(this)));
 
   run_loop.Run();
 }
diff --git a/services/shape_detection/detection_utils_mac.h b/services/shape_detection/detection_utils_mac.h
index 69e46426..98e3c0f 100644
--- a/services/shape_detection/detection_utils_mac.h
+++ b/services/shape_detection/detection_utils_mac.h
@@ -9,14 +9,15 @@
 
 #include "base/mac/scoped_nsobject.h"
 #include "services/shape_detection/public/interfaces/barcodedetection.mojom.h"
-#include "third_party/skia/include/core/SkBitmap.h"
 
 namespace shape_detection {
 
 // Takes a ScopedSharedBufferHandle with dimensions and produces a new CIImage
 // with the same contents, or a null scoped_nsobject is something goes wrong.
-base::scoped_nsobject<CIImage> CreateCIImageFromSkBitmap(
-    const SkBitmap& bitmap);
+base::scoped_nsobject<CIImage> CreateCIImageFromSharedMemory(
+    mojo::ScopedSharedBufferHandle frame_data,
+    uint32_t width,
+    uint32_t height);
 
 }  // namespace shape_detection
 
diff --git a/services/shape_detection/detection_utils_mac.mm b/services/shape_detection/detection_utils_mac.mm
index 554cd7e5..4e87812e99 100644
--- a/services/shape_detection/detection_utils_mac.mm
+++ b/services/shape_detection/detection_utils_mac.mm
@@ -9,31 +9,61 @@
 #include "base/memory/shared_memory.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "services/shape_detection/barcode_detection_impl.h"
-#include "skia/ext/skia_utils_mac.h"
-#include "third_party/skia/include/utils/mac/SkCGUtils.h"
 
 namespace shape_detection {
 
-base::scoped_nsobject<CIImage> CreateCIImageFromSkBitmap(
-    const SkBitmap& bitmap) {
+// These formats are available but not public until Mac 10.11.
+#if !defined(MAC_OS_X_VERSION_10_11) || \
+    MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_11
+const int kCIFormatRGBA8 = 24;
+#else
+//static_assert(kCIFormatRGBA8 == 24, "RGBA8 format enum index.");
+#endif
+
+base::scoped_nsobject<CIImage> CreateCIImageFromSharedMemory(
+    mojo::ScopedSharedBufferHandle frame_data,
+    uint32_t width,
+    uint32_t height) {
   base::CheckedNumeric<uint32_t> num_pixels =
-      base::CheckedNumeric<uint32_t>(bitmap.width()) * bitmap.height();
+      base::CheckedNumeric<uint32_t>(width) * height;
   base::CheckedNumeric<uint32_t> num_bytes = num_pixels * 4;
   if (!num_bytes.IsValid()) {
     DLOG(ERROR) << "Data overflow";
     return base::scoped_nsobject<CIImage>();
   }
 
-  // First convert SkBitmap to CGImageRef.
-  base::ScopedCFTypeRef<CGImageRef> cg_image(
-      SkCreateCGImageRefWithColorspace(bitmap, NULL));
-  if (!cg_image) {
-    DLOG(ERROR) << "Failed to create CGImageRef";
+  base::SharedMemoryHandle memory_handle;
+  size_t memory_size = 0;
+  bool read_only_flag = false;
+  const MojoResult result = mojo::UnwrapSharedMemoryHandle(
+      std::move(frame_data), &memory_handle, &memory_size, &read_only_flag);
+  DCHECK_EQ(MOJO_RESULT_OK, result) << "Failed to unwrap SharedBufferHandle";
+  if (!memory_size || memory_size != num_bytes.ValueOrDie()) {
+    DLOG(ERROR) << "Invalid image size";
     return base::scoped_nsobject<CIImage>();
   }
 
-  base::scoped_nsobject<CIImage> ci_image(
-      [[CIImage alloc] initWithCGImage:cg_image]);
+  auto shared_memory =
+      base::MakeUnique<base::SharedMemory>(memory_handle, true /* read_only */);
+  if (!shared_memory->Map(memory_size)) {
+    DLOG(ERROR) << "Failed to map bytes from shared memory";
+    return base::scoped_nsobject<CIImage>();
+  }
+
+  NSData* byte_data = [NSData dataWithBytesNoCopy:shared_memory->memory()
+                                           length:num_bytes.ValueOrDie()
+                                     freeWhenDone:NO];
+
+  base::ScopedCFTypeRef<CGColorSpaceRef> colorspace(
+      CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
+
+  // CIImage will return nil if RGBA8 is not supported in a certain version.
+  base::scoped_nsobject<CIImage> ci_image([[CIImage alloc]
+      initWithBitmapData:byte_data
+             bytesPerRow:width * 4
+                    size:CGSizeMake(width, height)
+                  format:kCIFormatRGBA8
+              colorSpace:colorspace]);
   if (!ci_image) {
     DLOG(ERROR) << "Failed to create CIImage";
     return base::scoped_nsobject<CIImage>();
diff --git a/services/shape_detection/face_detection_impl_mac.h b/services/shape_detection/face_detection_impl_mac.h
index 459ffa7..fb978c7 100644
--- a/services/shape_detection/face_detection_impl_mac.h
+++ b/services/shape_detection/face_detection_impl_mac.h
@@ -7,7 +7,6 @@
 
 #include "base/mac/scoped_nsobject.h"
 #include "services/shape_detection/public/interfaces/facedetection.mojom.h"
-#include "third_party/skia/include/core/SkBitmap.h"
 
 @class CIDetector;
 
@@ -19,7 +18,9 @@
       shape_detection::mojom::FaceDetectorOptionsPtr options);
   ~FaceDetectionImplMac() override;
 
-  void Detect(const SkBitmap& bitmap,
+  void Detect(mojo::ScopedSharedBufferHandle frame_data,
+              uint32_t width,
+              uint32_t height,
               const shape_detection::mojom::FaceDetection::DetectCallback&
                   callback) override;
 
diff --git a/services/shape_detection/face_detection_impl_mac.mm b/services/shape_detection/face_detection_impl_mac.mm
index 8bece619..151e0a7c 100644
--- a/services/shape_detection/face_detection_impl_mac.mm
+++ b/services/shape_detection/face_detection_impl_mac.mm
@@ -47,19 +47,21 @@
 
 FaceDetectionImplMac::~FaceDetectionImplMac() {}
 
-void FaceDetectionImplMac::Detect(const SkBitmap& bitmap,
+void FaceDetectionImplMac::Detect(mojo::ScopedSharedBufferHandle frame_data,
+                                  uint32_t width,
+                                  uint32_t height,
                                   const DetectCallback& callback) {
   media::ScopedResultCallback<DetectCallback> scoped_callback(
       base::Bind(&RunCallbackWithFaces, callback),
       base::Bind(&RunCallbackWithNoFaces));
 
-  base::scoped_nsobject<CIImage> ci_image = CreateCIImageFromSkBitmap(bitmap);
+  base::scoped_nsobject<CIImage> ci_image =
+      CreateCIImageFromSharedMemory(std::move(frame_data), width, height);
   if (!ci_image)
     return;
 
   NSArray* const features = [detector_ featuresInImage:ci_image];
 
-  const int height = bitmap.height();
   shape_detection::mojom::FaceDetectionResultPtr faces =
       shape_detection::mojom::FaceDetectionResult::New();
   for (CIFaceFeature* const f in features) {
diff --git a/services/shape_detection/face_detection_impl_mac_unittest.mm b/services/shape_detection/face_detection_impl_mac_unittest.mm
index 9b9c569..e07f700f 100644
--- a/services/shape_detection/face_detection_impl_mac_unittest.mm
+++ b/services/shape_detection/face_detection_impl_mac_unittest.mm
@@ -76,12 +76,24 @@
   const int num_bytes = size.GetArea() * 4 /* bytes per pixel */;
   ASSERT_EQ(num_bytes, image->computeSize64());
 
+  // Generate a new ScopedSharedBufferHandle of the aproppriate size, map it and
+  // copy the image pixels into it.
+  mojo::ScopedSharedBufferHandle handle =
+      mojo::SharedBufferHandle::Create(num_bytes);
+  ASSERT_TRUE(handle->is_valid());
+
+  mojo::ScopedSharedBufferMapping mapping = handle->Map(num_bytes);
+  ASSERT_TRUE(mapping);
+
+  memcpy(mapping.get(), image->getPixels(), num_bytes);
+
   base::RunLoop run_loop;
   base::Closure quit_closure = run_loop.QuitClosure();
   // Send the image to Detect() and expect the response in callback.
   EXPECT_CALL(*this, Detection(1)).WillOnce(RunClosure(quit_closure));
-  impl_->Detect(*image, base::Bind(&FaceDetectionImplMacTest::DetectCallback,
-                                   base::Unretained(this)));
+  impl_->Detect(std::move(handle), size.width(), size.height(),
+                base::Bind(&FaceDetectionImplMacTest::DetectCallback,
+                           base::Unretained(this)));
 
   run_loop.Run();
 }
diff --git a/services/shape_detection/public/interfaces/BUILD.gn b/services/shape_detection/public/interfaces/BUILD.gn
index 44522415..953742864 100644
--- a/services/shape_detection/public/interfaces/BUILD.gn
+++ b/services/shape_detection/public/interfaces/BUILD.gn
@@ -14,7 +14,6 @@
   ]
 
   public_deps = [
-    "//skia/public/interfaces",
     "//ui/gfx/geometry/mojo",
   ]
 }
diff --git a/services/shape_detection/public/interfaces/barcodedetection.mojom b/services/shape_detection/public/interfaces/barcodedetection.mojom
index 5ae9aef5..27f95ecd 100644
--- a/services/shape_detection/public/interfaces/barcodedetection.mojom
+++ b/services/shape_detection/public/interfaces/barcodedetection.mojom
@@ -6,7 +6,6 @@
 
 module shape_detection.mojom;
 
-import "skia/public/interfaces/bitmap.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 
 struct BarcodeDetectionResult {
@@ -18,7 +17,9 @@
 };
 
 interface BarcodeDetection {
-  // |bitmap_data| contains tightly packed image pixels in row-major order.
-  Detect(skia.mojom.Bitmap bitmap_data)
+  // |frame_data| contains tightly packed image pixels in ARGB32 format,
+  // row-major order.
+  // TODO(mcasas): Consider using mojo::Bitmap here, https://crbug.com/665488.
+  Detect(handle<shared_buffer> frame_data, uint32 width, uint32 height)
     => (array<BarcodeDetectionResult> results);
 };
diff --git a/services/shape_detection/public/interfaces/facedetection.mojom b/services/shape_detection/public/interfaces/facedetection.mojom
index 0ee1390..38102f8 100644
--- a/services/shape_detection/public/interfaces/facedetection.mojom
+++ b/services/shape_detection/public/interfaces/facedetection.mojom
@@ -6,7 +6,6 @@
 
 module shape_detection.mojom;
 
-import "skia/public/interfaces/bitmap.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 
 // Since "//ui/gfx/geometry/mojo" is not exposed to blink, we need to declare
@@ -22,7 +21,9 @@
 };
 
 interface FaceDetection {
-  // |bitmap_data| contains tightly packed image pixels in row-major order.
-  Detect(skia.mojom.Bitmap bitmap_data)
+  // |frame_data| contains tightly packed image pixels in ARGB32 format,
+  // row-major order.
+  // TODO(mcasas): Consider using mojo::Bitmap here, https://crbug.com/665488.
+  Detect(handle<shared_buffer> frame_data, uint32 width, uint32 height)
     => (FaceDetectionResult result);
 };
diff --git a/services/shape_detection/public/interfaces/textdetection.mojom b/services/shape_detection/public/interfaces/textdetection.mojom
index 7a0fed5..6e8bb01 100644
--- a/services/shape_detection/public/interfaces/textdetection.mojom
+++ b/services/shape_detection/public/interfaces/textdetection.mojom
@@ -4,7 +4,6 @@
 
 module shape_detection.mojom;
 
-import "skia/public/interfaces/bitmap.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 
 struct TextDetectionResult {
@@ -13,7 +12,8 @@
 };
 
 interface TextDetection {
-  // |bitmap_data| contains tightly packed image pixels in row-major order.
-  Detect(skia.mojom.Bitmap bitmap_data)
+  // |frame_data| contains tightly packed image pixels in ARGB32 format,
+  // row-major order.
+  Detect(handle<shared_buffer> frame_data, uint32 width, uint32 height)
     => (array<TextDetectionResult> results);
 };
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index fd8bb9b..4796d08 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -7121,23 +7121,6 @@
         },
         "test": "gles2_conform_test",
         "use_xvfb": false
-      },
-      {
-        "args": [
-          "--gtest_filter=*Detection*",
-          "--use-gpu-in-tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "1002:679e",
-              "os": "Mac-10.10"
-            }
-          ]
-        },
-        "test": "service_unittests",
-        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -7482,23 +7465,6 @@
         },
         "test": "gles2_conform_test",
         "use_xvfb": false
-      },
-      {
-        "args": [
-          "--gtest_filter=*Detection*",
-          "--use-gpu-in-tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "1002:679e",
-              "os": "Mac-10.10"
-            }
-          ]
-        },
-        "test": "service_unittests",
-        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 856ab253..60d4c493 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -751,6 +751,7 @@
 # future, as foolip@ is in the process of performing a significant fullscreen
 # refactor. See [crbug.com/676432] for a minimized repro.
 crbug.com/676432 fullscreen/full-screen-iframe-zIndex.html [ Skip ]
+crbug.com/676432 virtual/android/fullscreen/full-screen-iframe-zIndex.html [ Skip ]
 
 # TODO(xiaochengh): We should either convert the following tests with
 # spellcheck_test, or remove if they become redundant or inapplicable.
diff --git a/third_party/WebKit/LayoutTests/animations/underlying-background-position-edge.html b/third_party/WebKit/LayoutTests/animations/underlying-background-position-edge.html
new file mode 100644
index 0000000..e1268c743
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/underlying-background-position-edge.html
@@ -0,0 +1,10 @@
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<div id="target"></div>
+<script>
+test(() => {
+  target.style.backgroundPosition = 'right 20px bottom 20px';
+  target.animate({backgroundPosition: 'left 20px top 20px'}, {iterationStart: 0.5, fill: 'forwards'});
+  assert_equals(getComputedStyle(target).backgroundPosition, '50% 50%');
+}, 'Don\'t ignore the specified edge of the underlying background-position value in animations');
+</script>
diff --git a/third_party/WebKit/LayoutTests/editing/execCommand/format-block-multiple-paragraphs-expected.txt b/third_party/WebKit/LayoutTests/editing/execCommand/format-block-multiple-paragraphs-expected.txt
deleted file mode 100644
index 0bafcd05..0000000
--- a/third_party/WebKit/LayoutTests/editing/execCommand/format-block-multiple-paragraphs-expected.txt
+++ /dev/null
@@ -1,232 +0,0 @@
-This tests ensures formatBlock do not make multiple elements when formatting multiple paragraphs.
-
-Formatting:
-| "
-"
-| <div>
-|   "<#selection-anchor>hello"
-| <div>
-|   "world"
-| <div>
-|   "WebKit<#selection-focus>"
-| "
-"
-
-by p yields:
-| "
-"
-| <p>
-|   "<#selection-anchor>hello"
-|   <br>
-|   "world"
-|   <br>
-|   "WebKit<#selection-focus>"
-| "
-"
-
-Formatting:
-| "
-"
-| <p>
-|   "<#selection-anchor>hello"
-| <p>
-|   "world<#selection-focus>"
-| "
-"
-
-by blockquote yields:
-| "
-"
-| <blockquote>
-|   "<#selection-anchor>hello"
-|   <br>
-|   "world<#selection-focus>"
-| "
-"
-
-Formatting:
-| "
-"
-| <div>
-|   "<#selection-anchor>hello"
-|   <pre>
-|     "world<#selection-focus>"
-| "
-"
-
-by p yields:
-| "
-"
-| <p>
-|   "<#selection-anchor>hello"
-|   <br>
-|   "world<#selection-focus>"
-| "
-"
-
-Formatting:
-| "
-"
-| <h1>
-|   "<#selection-anchor>hello"
-| <div>
-|   <h2>
-|     "world"
-|   <h3>
-|     "WebKit<#selection-focus>"
-| "
-"
-
-by pre yields:
-| "
-"
-| <pre>
-|   "<#selection-anchor>hello"
-|   <br>
-|   "world"
-|   <br>
-|   "WebKit<#selection-focus>"
-| "
-"
-
-Formatting:
-| "
-"
-| <div>
-|   "hello"
-|   <p>
-|     "<#selection-anchor>world"
-|   "webki<#selection-focus>t"
-| "
-"
-
-by h1 yields:
-| "
-"
-| <div>
-|   "hello"
-|   <h1>
-|     "<#selection-anchor>world"
-|     <br>
-|     "webkit<#selection-focus>"
-| "
-"
-
-Formatting:
-| "
-"
-| <pre>
-|   "<#selection-anchor>hello
-world<#selection-focus>
-webkit
-"
-| "
-"
-
-by blockquote yields:
-| "
-"
-| <pre>
-|   <blockquote>
-|     "<#selection-anchor>hello"
-|     <br>
-|     "world<#selection-focus>"
-|   "webkit
-"
-| "
-"
-
-Formatting:
-| "
-"
-| <pre>
-|   "hello
-<#selection-anchor>world
-webki<#selection-focus>t
-"
-| "
-"
-
-by blockquote yields:
-| "
-"
-| <pre>
-|   "hello
-"
-|   <blockquote>
-|     "<#selection-anchor>world"
-|     <br>
-|     "webki<#selection-focus>t"
-| "
-"
-
-Formatting:
-| "
-<#selection-anchor>hello"
-| <p>
-|   "world<#selection-focus>"
-| <p>
-|   "webkit"
-| "
-
-"
-
-by pre yields:
-| <pre>
-|   "<#selection-anchor>
-hello"
-|   <br>
-|   "worl<#selection-focus>d"
-| <p>
-|   "webkit"
-| "
-
-"
-
-Formatting:
-| "
-"
-| <div>
-|   "hello"
-| <div>
-|   "<#selection-anchor>world"
-| <div>
-|   "webki<#selection-focus>t"
-| "
-"
-
-by pre yields:
-| "
-"
-| <div>
-|   "hello"
-| <pre>
-|   "<#selection-anchor>world"
-|   <br>
-|   "webki<#selection-focus>t"
-| "
-"
-
-Formatting:
-| "
-"
-| <ul>
-|   <li>
-|     "<#selection-anchor>hello"
-|   <li>
-|     "world<#selection-focus>"
-| "
-"
-
-by blockquote yields:
-| "
-"
-| <blockquote>
-|   <ul>
-|     <li>
-|       "<#selection-anchor>hello"
-|   <ul>
-|     <li>
-|       "world<#selection-focus>"
-| "
-"
diff --git a/third_party/WebKit/LayoutTests/editing/execCommand/format-block-multiple-paragraphs.html b/third_party/WebKit/LayoutTests/editing/execCommand/format-block-multiple-paragraphs.html
index 12c1798..761140c0 100644
--- a/third_party/WebKit/LayoutTests/editing/execCommand/format-block-multiple-paragraphs.html
+++ b/third_party/WebKit/LayoutTests/editing/execCommand/format-block-multiple-paragraphs.html
@@ -1,82 +1,137 @@
-<!DOCTYPE html>
-<html>
-<body>
-<script src="../../resources/dump-as-markup.js"></script>
-<div id="test0" contenteditable>
-<div>hello</div><div>world</div><div>WebKit</div>
-</div>
-<div id="test1" contenteditable>
-<p>hello</p><p>world</p>
-</div>
-<div id="test2" contenteditable>
-<div>hello<pre>world</pre></div>
-</div>
-<div id="test3" contenteditable>
-<h1>hello</h1><div><h2>world</h2><h3>WebKit</h3></div>
-</div>
-<div id="test4" contenteditable>
-<div>hello<p>world</p>webkit</div>
-</div>
-<div id="test5" contenteditable>
-<pre>hello
-world
-webkit
-</pre>
-</div>
-<div id="test6" contenteditable>
-<pre>hello
-world
-webkit
-</pre>
-</div>
-<div id="test7" contenteditable>
-hello<p>world</p><p>webkit</p>
-</pre>
-</div>
-<div id="test8" contenteditable>
-<div>hello</div><div>world</div><div>webkit</div>
-</div>
-<div id="test9" contenteditable>
-<ul><li>hello</li><li>world</li></ul>
-</div>
+<!doctype html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../assert_selection.js"></script>
 <script>
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '^<div>hello</div><div>world</div><div>Blink</div>|',
+    '</div>',
+  ].join(''),
+  'formatBlock P',
+  [
+    '<div contenteditable>',
+      '<p>^hello<br>world<br>Blink|</p>',
+    '</div>',
+  ].join('')), '0 select all children with P');
 
-Markup.description('This tests ensures formatBlock do not make multiple elements when formatting multiple paragraphs.')
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '^<p>hello</p><p>world</p>|',
+    '</div>',
+  ].join(''),
+  'formatBlock blockquote',
+  [
+    '<div contenteditable>',
+      '<blockquote>hello<br>world|</blockquote>',
+    '</div>',
+  ].join('')), '1 select all children BLOCKQUOTE');
 
-function testIndentation(containerId, selector, value) {
-    var container = document.getElementById(containerId);
-    selector(container);
-    Markup.dump(container, 'Formatting');
-    document.execCommand('FormatBlock', false, value);
-    Markup.dump(container, 'by ' + value + ' yields');
-}
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '^<div>hello<pre>world</pre></div>|',
+    '</div>',
+  ].join(''),
+  'formatBlock blockquote',
+  [
+    '<div contenteditable>',
+      '<blockquote>^hello<br>world|</blockquote>',
+    '</div>',
+  ].join('')), '2 select all children P');
 
-function selectAll(container) {
-    window.getSelection().selectAllChildren(container);
-}
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '^<h1>hello</h1><div><h2>world</h2><h3>Blink</h3></div>|',
+    '</div>',
+  ].join(''),
+  'formatBlock blockquote',
+  [
+    '<div contenteditable>',
+      '<blockquote>hello<br>world<br>Blink|</blockquote>',
+    '</div>',
+  ].join('')), '3 select all children PRE');
 
-function selectorForLines(first, last) {
-    return function (container) {
-        window.getSelection().collapse(container, 0);
-        for (var i = 0; i < first - 1; i++)
-            window.getSelection().modify('move', 'forward', 'line');
-        for (var i = 0; i < Math.abs(last - first, 0) + 1; i++)
-            window.getSelection().modify('extend', 'forward', 'line');
-        window.getSelection().modify('extend', 'backward', 'character');
-    }
-}
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '<div>hello<p>^world</p>Blin|k</div>',
+    '</div>',
+  ].join(''),
+  'formatBlock H1',
+  [
+    '<div contenteditable>',
+      'hello<h1>^world<br>Blink|</h1>',
+    '</div>',
+  ].join('')), '4 select for lines 2, 3 with H1');
 
-testIndentation('test0', selectAll, 'p');
-testIndentation('test1', selectAll, 'blockquote');
-testIndentation('test2', selectAll, 'p');
-testIndentation('test3', selectAll, 'pre');
-testIndentation('test4', selectorForLines(2, 3), 'h1');
-testIndentation('test5', selectorForLines(1, 2), 'blockquote');
-testIndentation('test6', selectorForLines(2, 3), 'blockquote');
-testIndentation('test7', selectorForLines(1, 2), 'pre');
-testIndentation('test8', selectorForLines(2, 3), 'pre');
-testIndentation('test9', selectAll, 'blockquote');
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '<pre>^hello\nworld|\nBlink\n</pre>',
+    '</div>',
+  ].join(''),
+  'formatBlock BLOCKQUOTE',
+  [
+    '<div contenteditable>',
+      '<pre><blockquote>^hello<br>world|</blockquote>Blink\n</pre>',
+    '</div>',
+  ].join('')), '5 select for lines 1, 2 with BLOCKQUOTE');
 
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '<pre>hello\n^world\nBlin|k\n</pre>',
+    '</div>',
+  ].join(''),
+  'formatBlock BLOCKQUOTE',
+  [
+    '<div contenteditable>',
+      '<pre>hello\n<blockquote>^world<br>Blin|k</blockquote></pre>',
+    '</div>',
+  ].join('')), '6 select for lines 2, 3 with BLOCKQUOTE');
+
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '^hello<p>world|</p><p>webkit</p>',
+    '</div>',
+  ].join(''),
+  'formatBlock PRE',
+  [
+    '<div contenteditable>',
+      '<pre>^hello<br>world|</pre><p>webkit</p>',
+    '</div>',
+  ].join('')), '7 select for lines 2, 3 with PRE');
+
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '<div>hello</div><div>^world</div><div>blin|k</div>',
+    '</div>',
+  ].join(''),
+  'formatBlock PRE',
+  [
+    '<div contenteditable>',
+      '<div>hello</div><pre>^world<br>blin|k</pre>',
+    '</div>',
+  ].join('')), '8 select for lines 2, 3 with PRE');
+
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '^<ul><li>hello</li><li>world</li></ul>|',
+    '</div>',
+  ].join(''),
+  'formatBlock BLOCKQUOTE',
+  [
+    '<div contenteditable>',
+      '<blockquote>',
+        '<ul><li>^hello</li></ul><ul><li>world|</li></ul>',
+      '</blockquote>',
+    '</div>',
+  ].join('')), '9 select all children with BLOCKQUOTE');
 </script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/editing/execCommand/format-block-table-expected.txt b/third_party/WebKit/LayoutTests/editing/execCommand/format-block-table-expected.txt
deleted file mode 100644
index 7f6ddde..0000000
--- a/third_party/WebKit/LayoutTests/editing/execCommand/format-block-table-expected.txt
+++ /dev/null
@@ -1,124 +0,0 @@
-
-Formatting:
-| "
-"
-| <table>
-|   <tbody>
-|     <tr>
-|       <td>
-|         "hello"
-|       <td>
-|         "world"
-| "
-"
-
-by p yields:
-| "
-"
-| <p>
-|   <#selection-anchor>
-|   <table>
-|     <tbody>
-|       <tr>
-|         <td>
-|           "hello"
-|         <td>
-|           "world"
-|   <#selection-focus>
-| "
-"
-
-Formatting:
-| "
-"
-| <table>
-|   <tbody>
-|     <tr>
-|       <td>
-|         "<#selection-anchor>hello<#selection-focus>"
-|       <td>
-|         "world"
-| "
-"
-
-by blockquote yields:
-| "
-"
-| <table>
-|   <tbody>
-|     <tr>
-|       <td>
-|         <blockquote>
-|           "<#selection-anchor>hello<#selection-focus>"
-|       <td>
-|         "world"
-| "
-"
-
-Formatting:
-| "
-"
-| <table>
-|   <tbody>
-|     <tr>
-|       <td>
-|         <p>
-|           "<#selection-anchor>hello"
-|         <div>
-|           "world<#selection-focus>"
-|       <td>
-|         "WebKit"
-| "
-"
-
-by h3 yields:
-| "
-"
-| <table>
-|   <tbody>
-|     <tr>
-|       <td>
-|         <h3>
-|           "<#selection-anchor>hello"
-|           <br>
-|           "world<#selection-focus>"
-|       <td>
-|         "WebKit"
-| "
-"
-
-Formatting:
-| "
-"
-| <table>
-|   <tbody>
-|     <tr>
-|       <td>
-|         <ul>
-|           <li>
-|             "<#selection-anchor>hello"
-|           <li>
-|             "world<#selection-focus>"
-|       <td>
-|         "WebKit"
-| "
-"
-
-by address yields:
-| "
-"
-| <table>
-|   <tbody>
-|     <tr>
-|       <td>
-|         <address>
-|           <ul>
-|             <li>
-|               "<#selection-anchor>hello"
-|           <ul>
-|             <li>
-|               "world<#selection-focus>"
-|       <td>
-|         "WebKit"
-| "
-"
diff --git a/third_party/WebKit/LayoutTests/editing/execCommand/format-block-table.html b/third_party/WebKit/LayoutTests/editing/execCommand/format-block-table.html
index 28ba100..d5e6d21 100644
--- a/third_party/WebKit/LayoutTests/editing/execCommand/format-block-table.html
+++ b/third_party/WebKit/LayoutTests/editing/execCommand/format-block-table.html
@@ -1,42 +1,70 @@
-<!DOCTYPE html>
-<html>
-<body>
-<script src="../../resources/dump-as-markup.js"></script>
-<div id="test0" contenteditable>
-<table><tr><td>hello</td><td>world</td></tr></table>
-</div>
-<div id="test1" contenteditable>
-<table><tr><td>hello</td><td>world</td></tr></table>
-</div>
-<div id="test2" contenteditable>
-<table><tr><td><p>hello</p><div>world</div></td><td>WebKit</td></tr></table>
-</div>
-<div id="test3" contenteditable>
-<table><tr><td><ul><li>hello</li><li>world</li></ul></td><td>WebKit</td></tr></table>
-</div>
+<!doctype html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../assert_selection.js"></script>
 <script>
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '^<table><tr><td>hello</td><td>world</td></tr></table>|',
+    '</div>',
+  ].join(''),
+  'formatBlock P',
+  [
+    '<div contenteditable>',
+      '<p>^<table><tbody><tr><td>hello</td><td>world</td></tr></tbody></table>|</p>',
+    '</div>',
+  ].join('')), '0 select all children with P');
 
-function testIndentation(containerId, selector, value) {
-    var container = document.getElementById(containerId);
-    selector(container);
-    Markup.dump(container, 'Formatting');
-    document.execCommand('FormatBlock', false, value);
-    Markup.dump(container, 'by ' + value + ' yields');
-}
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '<table><tr><td>^hello|</td><td>world</td></tr></table>',
+    '</div>',
+  ].join(''),
+  'formatBlock P',
+  [
+    '<div contenteditable>',
+      '<table><tbody><tr>',
+        '<td><p>^hello|</p></td><td>world</td>',
+      '</tr></tbody></table>',
+    '</div>',
+  ].join('')), '1 select first cell with BLOCKQUOTE');
 
-function selectAll(container) {
-    window.getSelection().selectAllChildren(container);
-}
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '<table><tr>',
+        '<td>^<p>hello</p>|<div>world</div></td><td>Blink</td>',
+      '</tr></table>',
+    '</div>',
+  ].join(''),
+  'formatBlock BLOCKQUOTE',
+  [
+    '<div contenteditable>',
+      '<table><tbody><tr>',
+        '<td><blockquote>^hello|</blockquote><div>world</div></td><td>Blink</td>',
+      '</tr></tbody></table>',
+    '</div>',
+  ].join('')), '2 select first cell with BLOCKQUOTE');
 
-function selectFirstCell(container) {
-    window.getSelection().selectAllChildren(container.getElementsByTagName('td')[0]);
-}
-
-testIndentation('test0', selectAll, 'p');
-testIndentation('test1', selectFirstCell, 'blockquote');
-testIndentation('test2', selectFirstCell, 'h3');
-testIndentation('test3', selectFirstCell, 'address');
-
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '<table><tr>',
+        '<td>^<ul><li>hello</li><li>world</li></ul>|</td><td>Blink</td>',
+      '</tr></table>',
+    '</div>',
+  ].join(''),
+  'formatBlock ADDRESS',
+  [
+    '<div contenteditable>',
+      '<table><tbody><tr>',
+        '<td><address>',
+          '<ul><li>^hello</li></ul><ul><li>world|</li></ul>',
+        '</address></td>',
+        '<td>Blink</td>',
+      '</tr></tbody></table>',
+    '</div>',
+  ].join('')), '3 select first cell with ADDRESS');
 </script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/editing/execCommand/format-block-with-block-expected.txt b/third_party/WebKit/LayoutTests/editing/execCommand/format-block-with-block-expected.txt
deleted file mode 100644
index fa2537c..0000000
--- a/third_party/WebKit/LayoutTests/editing/execCommand/format-block-with-block-expected.txt
+++ /dev/null
@@ -1,56 +0,0 @@
-This test verifies that formatBlock adds the containing block element's inline style to the block-style element that replaces it.
-
-Before FormatBlock:
-| "
-"
-| <div>
-|   style="color: green"
-|   "<#selection-anchor>hello<#selection-focus>"
-| "
-"
-
-After FormatBlock:
-| "
-"
-| <h1>
-|   style="color: green"
-|   "<#selection-anchor>hello<#selection-focus>"
-| "
-"
-
-Before FormatBlock:
-| "
-"
-| <div>
-|   style="color: green"
-|   <#selection-caret>
-|   <br>
-| "
-"
-
-After FormatBlock:
-| "
-"
-| <h1>
-|   style="color: green"
-|   <#selection-caret>
-|   <br>
-| "
-"
-
-Before FormatBlock:
-| "
-"
-| <span>
-|   style="color: green"
-|   "<#selection-anchor>hello<#selection-focus>"
-| "
-"
-
-After FormatBlock:
-| "
-"
-| <h1>
-|   <span>
-|     style="color: green"
-|     "<#selection-anchor>hello<#selection-focus>"
diff --git a/third_party/WebKit/LayoutTests/editing/execCommand/format-block-with-block.html b/third_party/WebKit/LayoutTests/editing/execCommand/format-block-with-block.html
index c1713f8..dddbf1a 100644
--- a/third_party/WebKit/LayoutTests/editing/execCommand/format-block-with-block.html
+++ b/third_party/WebKit/LayoutTests/editing/execCommand/format-block-with-block.html
@@ -1,32 +1,44 @@
-<!DOCTYPE html>
-<html>
-<body>
-<script src="../../resources/dump-as-markup.js"></script>
-<div id="testBlock" contenteditable>
-<div style="color: green">hello</div>
-</div>
-<div id="testBlockWithBR" contenteditable>
-<div style="color: green"><br></div>
-</div>
-<div id="testInline" contenteditable>
-<span style="color: green">hello</span>
-</div>
+<!doctype html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../assert_selection.js"></script>
 <script>
-function verify(id)
-{
-    var testElement = document.getElementById(id);
-    window.getSelection().selectAllChildren(testElement);
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '^<div style="color: green">hello</div>|',
+    '</div>',
+  ].join(''),
+  'formatBlock h1',
+  [
+    '<div contenteditable>',
+      '<h1 style="color: green">^hello|</h1>',
+    '</div>',
+  ].join('')), 'Block');
 
-    Markup.dump(testElement, 'Before FormatBlock');
-    document.execCommand('FormatBlock', false, 'h1');
-    Markup.dump(testElement, 'After FormatBlock');
-}
-Markup.description("This test verifies that formatBlock adds the containing block element's inline style to the block-style element that replaces it.");
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '^<div style="color: green"><br></div>|',
+    '</div>',
+  ].join(''),
+  'formatBlock h1',
+  [
+    '<div contenteditable>',
+      '<h1 style="color: green">|<br></h1>',
+    '</div>',
+  ].join('')), 'Block with BR');
 
-verify("testBlock");
-verify("testBlockWithBR");
-verify("testInline");
-    
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '^<span style="color: green">hello</span>|',
+    '</div>',
+  ].join(''),
+  'formatBlock h1',
+  [
+    '<div contenteditable>',
+      '<h1><span style="color: green">^hello|</span></h1>',
+    '</div>',
+  ].join('')), 'Inline');
 </script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/editing/execCommand/indent-pre-paragraphs-expected.txt b/third_party/WebKit/LayoutTests/editing/execCommand/indent-pre-paragraphs-expected.txt
deleted file mode 100644
index b8f93239..0000000
--- a/third_party/WebKit/LayoutTests/editing/execCommand/indent-pre-paragraphs-expected.txt
+++ /dev/null
@@ -1,154 +0,0 @@
-
-Indenting:
-| "
-"
-| <pre>
-|   "<#selection-anchor>hello<#selection-focus>"
-| "
-"
-
-yields:
-| "
-"
-| <blockquote>
-|   style="margin: 0 0 0 40px; border: none; padding: 0px;"
-|   <pre>
-|     "<#selection-anchor>hello<#selection-focus>"
-| "
-"
-
-Indenting:
-| "
-"
-| <pre>
-|   "<#selection-anchor>hello
-
-world
-
-webkit<#selection-focus>
-"
-| "
-"
-
-yields:
-| "
-"
-| <blockquote>
-|   style="margin: 0 0 0 40px; border: none; padding: 0px;"
-|   <pre>
-|     "<#selection-anchor>hello"
-|   <pre>
-|     "
-"
-|   <pre>
-|     "world"
-|   <pre>
-|     "
-"
-|   <pre>
-|     "webkit<#selection-focus>"
-| "
-"
-
-Indenting:
-| "
-"
-| <pre>
-|   "<#selection-anchor>hello
-world
-webkit<#selection-focus>
-"
-| "
-"
-
-yields:
-| "
-"
-| <blockquote>
-|   style="margin: 0 0 0 40px; border: none; padding: 0px;"
-|   <pre>
-|     "<#selection-anchor>hello"
-|   <pre>
-|     "world"
-|   <pre>
-|     "webkit<#selection-focus>"
-| "
-"
-
-Indenting:
-| "
-"
-| <pre>
-|   "<#selection-anchor>hello<#selection-focus>
-
-world
-"
-| "
-"
-
-yields:
-| "
-"
-| <blockquote>
-|   style="margin: 0 0 0 40px; border: none; padding: 0px;"
-|   <pre>
-|     "<#selection-anchor>hello<#selection-focus>"
-| <pre>
-|   "
-world
-"
-| "
-"
-
-Indenting:
-| "
-"
-| <pre>
-|   "hello
-<#selection-caret>
-world
-"
-| "
-"
-
-yields:
-| "
-"
-| <pre>
-|   "hello<#selection-caret>
-"
-| <blockquote>
-|   style="margin: 0 0 0 40px; border: none; padding: 0px;"
-|   <pre>
-|     "
-"
-| <pre>
-|   "world
-"
-| "
-"
-
-Indenting:
-| "
-"
-| <pre>
-|   "hello
-
-<#selection-anchor>worl<#selection-focus>d
-"
-| "
-"
-
-yields:
-| "
-"
-| <pre>
-|   "hello
-<#selection-anchor>
-"
-| <blockquote>
-|   style="margin: 0 0 0 40px; border: none; padding: 0px;"
-|   <pre>
-|     "worl<#selection-focus>d"
-| "
-"
diff --git a/third_party/WebKit/LayoutTests/editing/execCommand/indent-pre-paragraphs.html b/third_party/WebKit/LayoutTests/editing/execCommand/indent-pre-paragraphs.html
index c9228e5..dc29c31 100644
--- a/third_party/WebKit/LayoutTests/editing/execCommand/indent-pre-paragraphs.html
+++ b/third_party/WebKit/LayoutTests/editing/execCommand/indent-pre-paragraphs.html
@@ -1,83 +1,100 @@
-<!DOCTYPE html>
-<html>
-<body>
-<script src="../../resources/dump-as-markup.js"></script>
-<div id="test0" contenteditable>
-<pre>hello</pre>
-</div>
-
-<div id="test1" contenteditable>
-<pre>
-hello
-
-world
-
-webkit
-</pre>
-</div>
-
-<div id="test2" contenteditable>
-<pre>
-hello
-world
-webkit
-</pre>
-</div>
-
-<div id="test3" contenteditable>
-<pre>
-hello
-
-world
-</pre>
-</div>
-
-<div id="test4" contenteditable>
-<pre>
-hello
-
-world
-</pre>
-</div>
-
-<div id="test5" contenteditable>
-<pre>
-hello
-
-world
-</pre>
-</div>
+<!doctype html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../assert_selection.js"></script>
 <script>
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '^<pre>hello</pre>|',
+    '</div>',
+  ].join(''),
+  'indent',
+  [
+    '<div contenteditable>',
+      '<blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;">',
+        '<pre>^hello|</pre>',
+      '</blockquote>',
+    '</div>',
+  ].join('')), '0 select all children');
 
-function testIndentation(containerId, selector) {
-    var container = document.getElementById(containerId);
-    selector(container);
-    Markup.dump(container, 'Indenting');
-    document.execCommand('indent', false, null);
-    Markup.dump(container, 'yields');
-}
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '^<pre>\nhello\n\nworld\n\nblink\n</pre>|',
+    '</div>',
+  ].join(''),
+  'indent',
+  [
+    '<div contenteditable>',
+      '<blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;">',
+        '<pre>^hello</pre>',
+        '<pre>\n</pre><pre>world</pre><pre>\n</pre><pre>blink|</pre>',
+      '</blockquote>',
+    '</div>',
+  ].join('')), '1 select all children with blank lines');
 
-function selectAll(container) {
-    window.getSelection().selectAllChildren(container);
-}
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '^<pre>\nhello\nworld\nblink\n</pre>|',
+    '</div>',
+  ].join(''),
+  'indent',
+  [
+    '<div contenteditable>',
+      '<blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;">',
+        '<pre>^hello</pre><pre>world</pre><pre>blink|</pre>',
+      '</blockquote>',
+    '</div>',
+  ].join('')), '2 select all children with newlines');
 
-function selectorForLineN(line) {
-    return function (container) {
-        window.getSelection().collapse(container, 0);
-        for (var i = 0; i < line - 1; i++)
-            window.getSelection().modify('move', 'forward', 'line');
-        window.getSelection().modify('extend', 'forward', 'line');
-        window.getSelection().modify('extend', 'backward', 'character');
-    }
-}
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '<pre>\n^hello|\n\nworld\n</pre>',
+    '</div>',
+  ].join(''),
+  'indent',
+  [
+    '<div contenteditable>',
+      '<blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;">',
+        '<pre>^hello|</pre>',
+      '</blockquote>',
+      '<pre>\nworld\n</pre>',
+    '</div>',
+  ].join('')), '3 select line 1');
 
-testIndentation('test0', selectAll);
-testIndentation('test1', selectAll);
-testIndentation('test2', selectAll);
-testIndentation('test3', selectorForLineN(1));
-testIndentation('test4', selectorForLineN(2));
-testIndentation('test5', selectorForLineN(3));
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '<pre>\nhello\n|\nworld\n</pre>',
+    '</div>',
+  ].join(''),
+  'indent',
+  [
+    '<div contenteditable>',
+      '<pre>hello|\n</pre>',
+      '<blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;">',
+        '<pre>\n</pre>',
+      '</blockquote>',
+      '<pre>world\n</pre>',
+    '</div>',
+  ].join('')), '4 select line 2');
 
+test(() => assert_selection(
+  [
+    '<div contenteditable>',
+      '<pre>\nhello\n\n^worl|d\n</pre>',
+    '</div>',
+  ].join(''),
+  'indent',
+  [
+    '<div contenteditable>',
+      '<pre>hello\n^\n</pre>',
+      '<blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;">',
+        '<pre>worl|d</pre>',
+      '</blockquote>',
+    '</div>',
+  ].join('')), '5 select line 3');
 </script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/editing/selection/setBaseAndExtent-revert-selection-expected.txt b/third_party/WebKit/LayoutTests/editing/selection/setBaseAndExtent-revert-selection-expected.txt
deleted file mode 100644
index 3c6a5a21..0000000
--- a/third_party/WebKit/LayoutTests/editing/selection/setBaseAndExtent-revert-selection-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-Test for bug 20117: setBaseAndExtent fails to reverse the current selection.
-
-Div X
-Div Y
-Div Z
-PASS
diff --git a/third_party/WebKit/LayoutTests/editing/selection/setBaseAndExtent-revert-selection.html b/third_party/WebKit/LayoutTests/editing/selection/setBaseAndExtent-revert-selection.html
index 5bb90445..b3e719e 100644
--- a/third_party/WebKit/LayoutTests/editing/selection/setBaseAndExtent-revert-selection.html
+++ b/third_party/WebKit/LayoutTests/editing/selection/setBaseAndExtent-revert-selection.html
@@ -1,33 +1,40 @@
-<html>
-  <head>
-    <script>
-      var output = '';
-   
-      function selectNodes(a, b) {
-        window.getSelection().setBaseAndExtent(a, 1, b, 1);
-        output += window.getSelection().anchorNode.parentNode.id + ' ';   
-      }
+<!doctype html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../assert_selection.js"></script>
+<script>
+// Test for https://bugs.webkit.org/show_bug.cgi?id=20117
+test(() => assert_selection(
+  [
+    '<div id="x">Div X</div>',
+    '<div id="y">Div Y</div>',
+    '<div id="z">Div Z</div>',
+  ].join(''),
+  selection => {
+    const x = selection.document.getElementById('x');
+    const y = selection.document.getElementById('y');
+    selection.setBaseAndExtent(x, 1, y, 1);
+  },
+  [
+    '<div id="x">Div X^</div>',
+    '<div id="y">Div Y|</div>',
+    '<div id="z">Div Z</div>',
+  ].join('')), 'anchor < focus');
 
-      if (window.testRunner)
-        testRunner.dumpAsText();
-
-      function doIt() {
-        try {
-          var x = document.getElementById('x');
-          var y = document.getElementById('y');
-          selectNodes(x, y);
-          selectNodes(y, x);
-          document.getElementById('result').innerHTML = (output == "x y " ? "PASS" : ("FAIL: " + output));
-        } catch (e) { alert(e); }
-      }
-    </script>
-  </head>
-  <body onload="doIt()">
-    <p>Test for <a href="https://bugs.webkit.org/show_bug.cgi?id=20117">bug 20117</a>:
-    setBaseAndExtent fails to reverse the current selection.</p>
-    <div id='x'>Div X</div>
-    <div id='y'>Div Y</div>
-    <div id='z'>Div Z</div>
-    <div id='result'>FAIL (script didn't run)</div>
-  </body>
-</html>
+test(() => assert_selection(
+  [
+    '<div id="x">Div X</div>',
+    '<div id="y">Div Y</div>',
+    '<div id="z">Div Z</div>',
+  ].join(''),
+  selection => {
+    const x = selection.document.getElementById('x');
+    const y = selection.document.getElementById('y');
+    selection.setBaseAndExtent(y, 1, x, 1);
+  },
+  [
+    '<div id="x">Div X|</div>',
+    '<div id="y">Div Y^</div>',
+    '<div id="z">Div Z</div>',
+  ].join('')), 'anchor > focus');
+</script>
diff --git a/third_party/WebKit/LayoutTests/shapedetection/resources/mock-barcodedetection.js b/third_party/WebKit/LayoutTests/shapedetection/resources/mock-barcodedetection.js
index 129c660ee..1f6a479 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/resources/mock-barcodedetection.js
+++ b/third_party/WebKit/LayoutTests/shapedetection/resources/mock-barcodedetection.js
@@ -18,8 +18,8 @@
           handle => this.bindingSet_.addBinding(this, handle));
     }
 
-    detect(bitmap_data) {
-      let receivedStruct = new Uint8Array(bitmap_data.pixel_data);
+    detect(frame_data, width, height) {
+      let receivedStruct = mojo.mapBuffer(frame_data, 0, width*height*4, 0);
       this.buffer_data_ = new Uint32Array(receivedStruct.buffer);
       return Promise.resolve({
         results: [
@@ -45,6 +45,7 @@
           },
         ],
       });
+      mojo.unmapBuffer(receivedStruct.buffer);
     }
 
     getFrameData() {
diff --git a/third_party/WebKit/LayoutTests/shapedetection/resources/mock-facedetection.js b/third_party/WebKit/LayoutTests/shapedetection/resources/mock-facedetection.js
index c02fda2..1faf89f 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/resources/mock-facedetection.js
+++ b/third_party/WebKit/LayoutTests/shapedetection/resources/mock-facedetection.js
@@ -44,8 +44,8 @@
                                            request);
     }
 
-    detect(bitmap_data) {
-      let receivedStruct = new Uint8Array(bitmap_data.pixel_data);
+    detect(frame_data, width, height) {
+      let receivedStruct = mojo.mapBuffer(frame_data, 0, width*height*4, 0);
       this.buffer_data_ = new Uint32Array(receivedStruct.buffer);
       return Promise.resolve({
         result: {
@@ -56,6 +56,7 @@
           ]
         }
       });
+      mojo.unmapBuffer(receivedStruct.buffer);
     }
   }
   return new MockFaceDetectionProvider();
diff --git a/third_party/WebKit/LayoutTests/shapedetection/resources/mock-textdetection.js b/third_party/WebKit/LayoutTests/shapedetection/resources/mock-textdetection.js
index a622605..08195c9 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/resources/mock-textdetection.js
+++ b/third_party/WebKit/LayoutTests/shapedetection/resources/mock-textdetection.js
@@ -17,8 +17,8 @@
           handle => this.bindingSet_.addBinding(this, handle));
     }
 
-    detect(bitmap_data) {
-      let receivedStruct = new Uint8Array(bitmap_data.pixel_data);
+    detect(frame_data, width, height) {
+      let receivedStruct = mojo.mapBuffer(frame_data, 0, width*height*4, 0);
       this.buffer_data_ = new Uint32Array(receivedStruct.buffer);
       return Promise.resolve({
         results: [
@@ -32,6 +32,7 @@
           },
         ],
       });
+      mojo.unmapBuffer(receivedStruct.buffer);
     }
 
     getFrameData() {
diff --git a/third_party/WebKit/PerformanceTests/Skipped b/third_party/WebKit/PerformanceTests/Skipped
index bea9be16..88a88cc 100644
--- a/third_party/WebKit/PerformanceTests/Skipped
+++ b/third_party/WebKit/PerformanceTests/Skipped
@@ -50,9 +50,3 @@
 
 # Assumes window.eventSender is always supported; it only is for "content-shell" performance runs.
 DOM/click_webkit_user_select_none.html
-
-# Times out on reference build - crbug.com/579107
-ShadowDOM/SlotDistibutedNextPrevious.html
-ShadowDOM/v1-distribution.html
-ShadowDOM/v1-host-child-append.html
-ShadowDOM/v1-slot-append.html
diff --git a/third_party/WebKit/Source/core/animation/LengthListPropertyFunctions.cpp b/third_party/WebKit/Source/core/animation/LengthListPropertyFunctions.cpp
index ba7c629..6f65bbf 100644
--- a/third_party/WebKit/Source/core/animation/LengthListPropertyFunctions.cpp
+++ b/third_party/WebKit/Source/core/animation/LengthListPropertyFunctions.cpp
@@ -41,15 +41,12 @@
 
 struct FillLayerMethods {
   FillLayerMethods(CSSPropertyID property) {
-    isSet = nullptr;
-    getLength = nullptr;
-    setLength = nullptr;
-    clear = nullptr;
     switch (property) {
       case CSSPropertyBackgroundPositionX:
       case CSSPropertyWebkitMaskPositionX:
         isSet = &FillLayer::isXPositionSet;
         getLength = &FillLayer::xPosition;
+        getEdge = &FillLayer::backgroundXOrigin;
         setLength = &FillLayer::setXPosition;
         clear = &FillLayer::clearXPosition;
         break;
@@ -57,6 +54,7 @@
       case CSSPropertyWebkitMaskPositionY:
         isSet = &FillLayer::isYPositionSet;
         getLength = &FillLayer::yPosition;
+        getEdge = &FillLayer::backgroundYOrigin;
         setLength = &FillLayer::setYPosition;
         clear = &FillLayer::clearYPosition;
         break;
@@ -66,10 +64,11 @@
     }
   }
 
-  bool (FillLayer::*isSet)() const;
-  const Length& (FillLayer::*getLength)() const;
-  void (FillLayer::*setLength)(const Length&);
-  void (FillLayer::*clear)();
+  bool (FillLayer::*isSet)() const = nullptr;
+  const Length& (FillLayer::*getLength)() const = nullptr;
+  BackgroundEdgeOrigin (FillLayer::*getEdge)() const = nullptr;
+  void (FillLayer::*setLength)(const Length&) = nullptr;
+  void (FillLayer::*clear)() = nullptr;
 };
 
 }  // namespace
@@ -164,6 +163,14 @@
       FillLayerMethods fillLayerMethods(property);
       while (fillLayer && (fillLayer->*fillLayerMethods.isSet)()) {
         result.push_back((fillLayer->*fillLayerMethods.getLength)());
+        switch ((fillLayer->*fillLayerMethods.getEdge)()) {
+          case RightEdge:
+          case BottomEdge:
+            result.back() = result.back().subtractFromOneHundredPercent();
+            break;
+          default:
+            break;
+        }
         fillLayer = fillLayer->next();
       }
       return true;
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp b/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp
index 5471152..96fe94d 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp
@@ -444,7 +444,6 @@
     const ClipRectsContext& context,
     bool isForeground) const {
   DCHECK(m_geometryMapper);
-  LayoutRect source(LayoutRect::infiniteIntRect());
   const auto* properties = m_layer.layoutObject()->paintProperties();
   // TODO(chrishtr): fix the underlying bug that causes this situation.
   DCHECK(properties && properties->localBorderBoxProperties());
@@ -476,8 +475,8 @@
   }
 
   FloatClipRect clippedRectInRootLayerSpace =
-      m_geometryMapper->sourceToDestinationVisualRect(
-          FloatRect(source), propertyTreeState, destinationPropertyTreeState);
+      m_geometryMapper->sourceToDestinationClipRect(
+          propertyTreeState, destinationPropertyTreeState);
   ClipRect clipRect(LayoutRect(clippedRectInRootLayerSpace.rect()));
   if (clippedRectInRootLayerSpace.hasRadius())
     clipRect.setHasRadius(true);
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerClipperTest.cpp b/third_party/WebKit/Source/core/paint/PaintLayerClipperTest.cpp
index 241735cd..b612cff 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerClipperTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerClipperTest.cpp
@@ -33,7 +33,8 @@
   }
 
   bool geometryMapperCacheEmpty(const PaintLayerClipper& clipper) {
-    return clipper.m_geometryMapper->m_data.isEmpty();
+    return clipper.m_geometryMapper->m_transformCache.isEmpty() &&
+           clipper.m_geometryMapper->m_clipCache.isEmpty();
   }
 };
 
diff --git a/third_party/WebKit/Source/modules/shapedetection/BUILD.gn b/third_party/WebKit/Source/modules/shapedetection/BUILD.gn
index 663f27599..8102c764 100644
--- a/third_party/WebKit/Source/modules/shapedetection/BUILD.gn
+++ b/third_party/WebKit/Source/modules/shapedetection/BUILD.gn
@@ -24,6 +24,5 @@
 
   public_deps = [
     "//services/shape_detection/public/interfaces:interfaces_blink",
-    "//skia/public/interfaces:interfaces_blink",
   ]
 }
diff --git a/third_party/WebKit/Source/modules/shapedetection/BarcodeDetector.cpp b/third_party/WebKit/Source/modules/shapedetection/BarcodeDetector.cpp
index 7dce489..9f8beca 100644
--- a/third_party/WebKit/Source/modules/shapedetection/BarcodeDetector.cpp
+++ b/third_party/WebKit/Source/modules/shapedetection/BarcodeDetector.cpp
@@ -26,8 +26,11 @@
                 wrapWeakPersistent(this))));
 }
 
-ScriptPromise BarcodeDetector::doDetect(ScriptPromiseResolver* resolver,
-                                        skia::mojom::blink::BitmapPtr bitmap) {
+ScriptPromise BarcodeDetector::doDetect(
+    ScriptPromiseResolver* resolver,
+    mojo::ScopedSharedBufferHandle sharedBufferHandle,
+    int imageWidth,
+    int imageHeight) {
   ScriptPromise promise = resolver->promise();
   if (!m_barcodeService) {
     resolver->reject(DOMException::create(
@@ -36,9 +39,10 @@
   }
   m_barcodeServiceRequests.insert(resolver);
   m_barcodeService->Detect(
-      std::move(bitmap), convertToBaseCallback(WTF::bind(
-                             &BarcodeDetector::onDetectBarcodes,
-                             wrapPersistent(this), wrapPersistent(resolver))));
+      std::move(sharedBufferHandle), imageWidth, imageHeight,
+      convertToBaseCallback(WTF::bind(&BarcodeDetector::onDetectBarcodes,
+                                      wrapPersistent(this),
+                                      wrapPersistent(resolver))));
   return promise;
 }
 
diff --git a/third_party/WebKit/Source/modules/shapedetection/BarcodeDetector.h b/third_party/WebKit/Source/modules/shapedetection/BarcodeDetector.h
index 7e3fa4af8..2d0e598 100644
--- a/third_party/WebKit/Source/modules/shapedetection/BarcodeDetector.h
+++ b/third_party/WebKit/Source/modules/shapedetection/BarcodeDetector.h
@@ -29,7 +29,9 @@
   ~BarcodeDetector() override = default;
 
   ScriptPromise doDetect(ScriptPromiseResolver*,
-                         skia::mojom::blink::BitmapPtr) override;
+                         mojo::ScopedSharedBufferHandle,
+                         int imageWidth,
+                         int imageHeight) override;
   void onDetectBarcodes(
       ScriptPromiseResolver*,
       Vector<shape_detection::mojom::blink::BarcodeDetectionResultPtr>);
diff --git a/third_party/WebKit/Source/modules/shapedetection/DEPS b/third_party/WebKit/Source/modules/shapedetection/DEPS
index 76ba38ac..372894a 100644
--- a/third_party/WebKit/Source/modules/shapedetection/DEPS
+++ b/third_party/WebKit/Source/modules/shapedetection/DEPS
@@ -10,7 +10,6 @@
     "+platform",
     "+public/platform",
     "+services/shape_detection",
-    "+skia/public/interfaces/bitmap.mojom-blink.h",
     "+third_party/skia/include/core/SkImage.h",
     "+third_party/skia/include/core/SkImageInfo.h",
     "+ui/gfx/geometry",
diff --git a/third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp b/third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp
index ce92d34..ccda92a 100644
--- a/third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp
+++ b/third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp
@@ -35,8 +35,11 @@
       &FaceDetector::onFaceServiceConnectionError, wrapWeakPersistent(this))));
 }
 
-ScriptPromise FaceDetector::doDetect(ScriptPromiseResolver* resolver,
-                                     skia::mojom::blink::BitmapPtr bitmap) {
+ScriptPromise FaceDetector::doDetect(
+    ScriptPromiseResolver* resolver,
+    mojo::ScopedSharedBufferHandle sharedBufferHandle,
+    int imageWidth,
+    int imageHeight) {
   ScriptPromise promise = resolver->promise();
   if (!m_faceService) {
     resolver->reject(DOMException::create(
@@ -44,7 +47,7 @@
     return promise;
   }
   m_faceServiceRequests.insert(resolver);
-  m_faceService->Detect(std::move(bitmap),
+  m_faceService->Detect(std::move(sharedBufferHandle), imageWidth, imageHeight,
                         convertToBaseCallback(WTF::bind(
                             &FaceDetector::onDetectFaces, wrapPersistent(this),
                             wrapPersistent(resolver))));
diff --git a/third_party/WebKit/Source/modules/shapedetection/FaceDetector.h b/third_party/WebKit/Source/modules/shapedetection/FaceDetector.h
index 2047879c..84f6b9f 100644
--- a/third_party/WebKit/Source/modules/shapedetection/FaceDetector.h
+++ b/third_party/WebKit/Source/modules/shapedetection/FaceDetector.h
@@ -31,7 +31,9 @@
   ~FaceDetector() override = default;
 
   ScriptPromise doDetect(ScriptPromiseResolver*,
-                         skia::mojom::blink::BitmapPtr) override;
+                         mojo::ScopedSharedBufferHandle,
+                         int imageWidth,
+                         int imageHeight) override;
   void onDetectFaces(ScriptPromiseResolver*,
                      shape_detection::mojom::blink::FaceDetectionResultPtr);
   void onFaceServiceConnectionError();
diff --git a/third_party/WebKit/Source/modules/shapedetection/ShapeDetector.cpp b/third_party/WebKit/Source/modules/shapedetection/ShapeDetector.cpp
index ec32288..2fc9609 100644
--- a/third_party/WebKit/Source/modules/shapedetection/ShapeDetector.cpp
+++ b/third_party/WebKit/Source/modules/shapedetection/ShapeDetector.cpp
@@ -22,19 +22,28 @@
 
 namespace {
 
-skia::mojom::blink::BitmapPtr createBitmapFromData(int width,
-                                                   int height,
-                                                   Vector<uint8_t> bitmapData) {
-  skia::mojom::blink::BitmapPtr bitmap = skia::mojom::blink::Bitmap::New();
+mojo::ScopedSharedBufferHandle getSharedBufferOnData(
+    ScriptPromiseResolver* resolver,
+    uint8_t* data,
+    int size) {
+  DCHECK(data);
+  DCHECK(size);
+  ScriptPromise promise = resolver->promise();
 
-  bitmap->color_type = (kN32_SkColorType == kRGBA_8888_SkColorType)
-                           ? skia::mojom::ColorType::RGBA_8888
-                           : skia::mojom::ColorType::BGRA_8888;
-  bitmap->width = width;
-  bitmap->height = height;
-  bitmap->pixel_data = std::move(bitmapData);
+  mojo::ScopedSharedBufferHandle sharedBufferHandle =
+      mojo::SharedBufferHandle::Create(size);
+  if (!sharedBufferHandle->is_valid()) {
+    resolver->reject(
+        DOMException::create(InvalidStateError, "Internal allocation error"));
+    return sharedBufferHandle;
+  }
 
-  return bitmap;
+  const mojo::ScopedSharedBufferMapping mappedBuffer =
+      sharedBufferHandle->Map(size);
+  DCHECK(mappedBuffer.get());
+  memcpy(mappedBuffer.get(), data, size);
+
+  return sharedBufferHandle;
 }
 
 }  // anonymous namespace
@@ -127,13 +136,13 @@
     return promise;
   }
 
-  WTF::Vector<uint8_t> bitmapData;
-  bitmapData.append(pixelDataPtr,
-                    static_cast<int>(allocationSize.ValueOrDefault(0)));
+  mojo::ScopedSharedBufferHandle sharedBufferHandle = getSharedBufferOnData(
+      resolver, pixelDataPtr, allocationSize.ValueOrDefault(0));
+  if (!sharedBufferHandle->is_valid())
+    return promise;
 
-  return doDetect(resolver,
-                  createBitmapFromData(image->width(), image->height(),
-                                       std::move(bitmapData)));
+  return doDetect(resolver, std::move(sharedBufferHandle), image->width(),
+                  image->height());
 }
 
 ScriptPromise ShapeDetector::detectShapesOnImageData(
@@ -148,12 +157,14 @@
 
   uint8_t* const data = imageData->data()->data();
   WTF::CheckedNumeric<int> allocationSize = imageData->size().area() * 4;
-  WTF::Vector<uint8_t> bitmapData;
-  bitmapData.append(data, static_cast<int>(allocationSize.ValueOrDefault(0)));
 
-  return doDetect(resolver,
-                  createBitmapFromData(imageData->width(), imageData->height(),
-                                       std::move(bitmapData)));
+  mojo::ScopedSharedBufferHandle sharedBufferHandle =
+      getSharedBufferOnData(resolver, data, allocationSize.ValueOrDefault(0));
+  if (!sharedBufferHandle->is_valid())
+    return promise;
+
+  return doDetect(resolver, std::move(sharedBufferHandle), imageData->width(),
+                  imageData->height());
 }
 
 ScriptPromise ShapeDetector::detectShapesOnImageElement(
@@ -194,10 +205,26 @@
 
   const SkImageInfo skiaInfo =
       SkImageInfo::MakeN32(image->width(), image->height(), image->alphaType());
-  size_t rowBytes = skiaInfo.minRowBytes();
 
-  Vector<uint8_t> bitmapData(skiaInfo.getSafeSize(rowBytes));
-  const SkPixmap pixmap(skiaInfo, bitmapData.data(), rowBytes);
+  const uint32_t allocationSize = skiaInfo.getSafeSize(skiaInfo.minRowBytes());
+
+  mojo::ScopedSharedBufferHandle sharedBufferHandle =
+      mojo::SharedBufferHandle::Create(allocationSize);
+  if (!sharedBufferHandle.is_valid()) {
+    DLOG(ERROR) << "Requested allocation : " << allocationSize
+                << "B, larger than |mojo::edk::kMaxSharedBufferSize| == 16MB ";
+    // TODO(xianglu): For now we reject the promise if the image is too large.
+    // But consider resizing the image to remove restriction on the user side.
+    // Also, add LayoutTests for this case later.
+    resolver->reject(
+        DOMException::create(InvalidStateError, "Image exceeds size limit."));
+    return promise;
+  }
+
+  const mojo::ScopedSharedBufferMapping mappedBuffer =
+      sharedBufferHandle->Map(allocationSize);
+
+  const SkPixmap pixmap(skiaInfo, mappedBuffer.get(), skiaInfo.minRowBytes());
   if (!image->readPixels(pixmap, 0, 0)) {
     resolver->reject(DOMException::create(
         InvalidStateError,
@@ -205,9 +232,8 @@
     return promise;
   }
 
-  return doDetect(
-      resolver, createBitmapFromData(img->naturalWidth(), img->naturalHeight(),
-                                     std::move(bitmapData)));
+  return doDetect(resolver, std::move(sharedBufferHandle), img->naturalWidth(),
+                  img->naturalHeight());
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/shapedetection/ShapeDetector.h b/third_party/WebKit/Source/modules/shapedetection/ShapeDetector.h
index cd55e5cc..74b8e70e 100644
--- a/third_party/WebKit/Source/modules/shapedetection/ShapeDetector.h
+++ b/third_party/WebKit/Source/modules/shapedetection/ShapeDetector.h
@@ -10,7 +10,6 @@
 #include "core/imagebitmap/ImageBitmapFactories.h"
 #include "modules/ModulesExport.h"
 #include "modules/canvas2d/CanvasRenderingContext2D.h"
-#include "skia/public/interfaces/bitmap.mojom-blink.h"
 
 namespace blink {
 
@@ -28,7 +27,9 @@
                                            const HTMLImageElement*);
 
   virtual ScriptPromise doDetect(ScriptPromiseResolver*,
-                                 skia::mojom::blink::BitmapPtr) = 0;
+                                 mojo::ScopedSharedBufferHandle,
+                                 int imageWidth,
+                                 int imageHeight) = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/shapedetection/TextDetector.cpp b/third_party/WebKit/Source/modules/shapedetection/TextDetector.cpp
index e53f3c53..d887f9db 100644
--- a/third_party/WebKit/Source/modules/shapedetection/TextDetector.cpp
+++ b/third_party/WebKit/Source/modules/shapedetection/TextDetector.cpp
@@ -24,8 +24,11 @@
       &TextDetector::onTextServiceConnectionError, wrapWeakPersistent(this))));
 }
 
-ScriptPromise TextDetector::doDetect(ScriptPromiseResolver* resolver,
-                                     skia::mojom::blink::BitmapPtr bitmap) {
+ScriptPromise TextDetector::doDetect(
+    ScriptPromiseResolver* resolver,
+    mojo::ScopedSharedBufferHandle sharedBufferHandle,
+    int imageWidth,
+    int imageHeight) {
   ScriptPromise promise = resolver->promise();
   if (!m_textService) {
     resolver->reject(DOMException::create(
@@ -33,7 +36,7 @@
     return promise;
   }
   m_textServiceRequests.insert(resolver);
-  m_textService->Detect(std::move(bitmap),
+  m_textService->Detect(std::move(sharedBufferHandle), imageWidth, imageHeight,
                         convertToBaseCallback(WTF::bind(
                             &TextDetector::onDetectText, wrapPersistent(this),
                             wrapPersistent(resolver))));
diff --git a/third_party/WebKit/Source/modules/shapedetection/TextDetector.h b/third_party/WebKit/Source/modules/shapedetection/TextDetector.h
index 800893c..64791e3 100644
--- a/third_party/WebKit/Source/modules/shapedetection/TextDetector.h
+++ b/third_party/WebKit/Source/modules/shapedetection/TextDetector.h
@@ -29,7 +29,9 @@
   ~TextDetector() override = default;
 
   ScriptPromise doDetect(ScriptPromiseResolver*,
-                         skia::mojom::blink::BitmapPtr) override;
+                         mojo::ScopedSharedBufferHandle,
+                         int imageWidth,
+                         int imageHeight) override;
   void onDetectText(
       ScriptPromiseResolver*,
       Vector<shape_detection::mojom::blink::TextDetectionResultPtr>);
diff --git a/third_party/WebKit/Source/modules/time_zone_monitor/BUILD.gn b/third_party/WebKit/Source/modules/time_zone_monitor/BUILD.gn
index efda79be..fcc3811 100644
--- a/third_party/WebKit/Source/modules/time_zone_monitor/BUILD.gn
+++ b/third_party/WebKit/Source/modules/time_zone_monitor/BUILD.gn
@@ -11,7 +11,6 @@
   ]
 
   deps = [
-    "//device/time_zone_monitor/public/interfaces:interfaces_blink",
     "//mojo/public/cpp/bindings",
     "//services/device/public/interfaces:interfaces_blink",
     "//services/service_manager/public/interfaces:interfaces_blink",
diff --git a/third_party/WebKit/Source/modules/time_zone_monitor/DEPS b/third_party/WebKit/Source/modules/time_zone_monitor/DEPS
index 212c63f5..11c33b2f 100644
--- a/third_party/WebKit/Source/modules/time_zone_monitor/DEPS
+++ b/third_party/WebKit/Source/modules/time_zone_monitor/DEPS
@@ -1,6 +1,6 @@
 include_rules = [
-    "+device/time_zone_monitor/public/interfaces/time_zone_monitor.mojom-blink.h",
     "+mojo/public/cpp/bindings/binding.h",
     "+services/device/public/interfaces/constants.mojom-blink.h",
+    "+services/device/public/interfaces/time_zone_monitor.mojom-blink.h",
     "+third_party/icu/source/i18n/unicode/timezone.h",
 ]
diff --git a/third_party/WebKit/Source/modules/time_zone_monitor/TimeZoneMonitorClient.h b/third_party/WebKit/Source/modules/time_zone_monitor/TimeZoneMonitorClient.h
index 0dced07b..5b60daa 100644
--- a/third_party/WebKit/Source/modules/time_zone_monitor/TimeZoneMonitorClient.h
+++ b/third_party/WebKit/Source/modules/time_zone_monitor/TimeZoneMonitorClient.h
@@ -5,8 +5,8 @@
 #ifndef TimeZoneMonitorClient_h
 #define TimeZoneMonitorClient_h
 
-#include "device/time_zone_monitor/public/interfaces/time_zone_monitor.mojom-blink.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "services/device/public/interfaces/time_zone_monitor.mojom-blink.h"
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/platform/graphics/paint/FloatClipRect.h b/third_party/WebKit/Source/platform/graphics/paint/FloatClipRect.h
index c135f236..bb09206 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/FloatClipRect.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/FloatClipRect.h
@@ -6,6 +6,7 @@
 #define FloatClipRect_h
 
 #include "platform/geometry/FloatRect.h"
+#include "platform/geometry/LayoutRect.h"
 #include "wtf/Allocator.h"
 
 namespace blink {
@@ -14,24 +15,52 @@
   USING_FAST_MALLOC(FloatClipRect);
 
  public:
-  FloatClipRect() : m_hasRadius(false) {}
+  FloatClipRect()
+      : m_rect(FloatRect(LayoutRect::infiniteIntRect())),
+        m_hasRadius(false),
+        m_isInfinite(true) {}
 
-  FloatClipRect(const FloatRect& rect) : m_rect(rect), m_hasRadius(false) {}
+  FloatClipRect(const FloatRect& rect)
+      : m_rect(rect), m_hasRadius(false), m_isInfinite(false) {}
 
   const FloatRect& rect() const { return m_rect; }
 
-  void intersect(const FloatRect& other) { m_rect.intersect(other); }
+  void intersect(const FloatRect& other) {
+    if (m_isInfinite) {
+      m_rect = other;
+      m_isInfinite = false;
+    } else {
+      m_rect.intersect(other);
+    }
+  }
 
   bool hasRadius() const { return m_hasRadius; }
-  void setHasRadius(bool hasRadius) { m_hasRadius = hasRadius; }
+  void setHasRadius(bool hasRadius) {
+    m_hasRadius = hasRadius;
+    m_isInfinite = false;
+  }
 
-  void setRect(const FloatRect& rect) { m_rect = rect; }
+  void setRect(const FloatRect& rect) {
+    m_rect = rect;
+    m_isInfinite = false;
+  }
+
+  bool isInfinite() const { return m_isInfinite; }
 
  private:
   FloatRect m_rect;
-  bool m_hasRadius;
+  bool m_hasRadius : 1;
+  bool m_isInfinite : 1;
 };
 
+inline bool operator==(const FloatClipRect& a, const FloatClipRect& b) {
+  if (a.isInfinite() && b.isInfinite())
+    return true;
+  if (!a.isInfinite() && !b.isInfinite())
+    return a.rect() == b.rect() && a.hasRadius() == b.hasRadius();
+  return false;
+}
+
 }  // namespace blink
 
 #endif  // FloatClipRect_h
diff --git a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.cpp b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.cpp
index 7b39445..a74c867 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.cpp
@@ -46,9 +46,11 @@
       localToAncestorVisualRectInternal(rect, sourceState, lcaState, success);
   if (!success)
     return result;
-  FloatRect final = ancestorToLocalRect(result.rect(), lcaTransform,
-                                        destinationState.transform());
-  result.setRect(final);
+  if (!result.isInfinite()) {
+    FloatRect final = ancestorToLocalRect(result.rect(), lcaTransform,
+                                          destinationState.transform());
+    result.setRect(final);
+  }
   return result;
 }
 
@@ -107,7 +109,8 @@
   FloatRect mappedRect = transformMatrix.mapRect(rect);
 
   FloatClipRect clipRect =
-      localToAncestorClipRectInternal(localState, ancestorState, success);
+      localToAncestorClipRectInternal(localState.clip(), ancestorState.clip(),
+                                      ancestorState.transform(), success);
 
   if (success) {
     clipRect.intersect(mappedRect);
@@ -208,29 +211,29 @@
   return transformMatrix.inverse().mapRect(rect);
 }
 
-PrecomputedDataForAncestor& GeometryMapper::getPrecomputedDataForAncestor(
-    const TransformPaintPropertyNode* transform) {
-  auto addResult = m_data.insert(transform, nullptr);
+GeometryMapper::TransformCache& GeometryMapper::getTransformCache(
+    const TransformPaintPropertyNode* ancestor) {
+  auto addResult = m_transformCache.insert(ancestor, nullptr);
   if (addResult.isNewEntry)
-    addResult.storedValue->value = PrecomputedDataForAncestor::create();
+    addResult.storedValue->value = WTF::wrapUnique(new TransformCache);
   return *addResult.storedValue->value;
 }
 
-TransformCache& GeometryMapper::getTransformCache(
-    const TransformPaintPropertyNode* node) {
-  return getPrecomputedDataForAncestor(node).toAncestorTransforms;
-}
+GeometryMapper::ClipCache& GeometryMapper::getClipCache(
+    const ClipPaintPropertyNode* ancestorClip,
+    const TransformPaintPropertyNode* ancestorTransform) {
+  auto addResultTransform = m_clipCache.insert(ancestorClip, nullptr);
+  if (addResultTransform.isNewEntry) {
+    addResultTransform.storedValue->value =
+        WTF::wrapUnique(new TransformToClip);
+  }
 
-ClipCache& GeometryMapper::getClipCache(
-    const TransformPaintPropertyNode* transform,
-    const ClipPaintPropertyNode* clip) {
-  PrecomputedDataForAncestor& precomputedData =
-      getPrecomputedDataForAncestor(transform);
+  auto addResultClip =
+      addResultTransform.storedValue->value->insert(ancestorTransform, nullptr);
+  if (addResultClip.isNewEntry)
+    addResultClip.storedValue->value = WTF::wrapUnique(new ClipCache);
 
-  auto addResult = precomputedData.precomputedClips.insert(clip, nullptr);
-  if (addResult.isNewEntry)
-    addResult.storedValue->value = WTF::makeUnique<ClipCache>();
-  return *addResult.storedValue->value;
+  return *addResultClip.storedValue->value;
 }
 
 FloatClipRect GeometryMapper::localToAncestorClipRect(
@@ -238,29 +241,88 @@
     const PropertyTreeState& ancestorState) {
   bool success = false;
   FloatClipRect result =
-      localToAncestorClipRectInternal(localState, ancestorState, success);
+      localToAncestorClipRectInternal(localState.clip(), ancestorState.clip(),
+                                      ancestorState.transform(), success);
+
   DCHECK(success);
+
+  return result;
+}
+
+FloatClipRect GeometryMapper::sourceToDestinationClipRect(
+    const PropertyTreeState& sourceState,
+    const PropertyTreeState& destinationState) {
+  bool success = false;
+  FloatClipRect result = sourceToDestinationClipRectInternal(
+      sourceState, destinationState, success);
+  DCHECK(success);
+
+  return result;
+}
+
+FloatClipRect GeometryMapper::sourceToDestinationClipRectInternal(
+    const PropertyTreeState& sourceState,
+    const PropertyTreeState& destinationState,
+    bool& success) {
+  FloatClipRect result = localToAncestorClipRectInternal(
+      sourceState.clip(), destinationState.clip(), destinationState.transform(),
+      success);
+  // Success if destinationState is an ancestor state.
+  if (success)
+    return result;
+
+  // Otherwise first map to the lowest common ancestor, then map to destination.
+  const TransformPaintPropertyNode* lcaTransform = lowestCommonAncestor(
+      sourceState.transform(), destinationState.transform());
+  DCHECK(lcaTransform);
+
+  // Assume that the clip of destinationState is an ancestor of the clip of
+  // sourceState and is under the space of lcaTransform. Otherwise
+  // localToAncestorClipRectInternal() will fail.
+  PropertyTreeState lcaState = destinationState;
+  lcaState.setTransform(lcaTransform);
+
+  result = localToAncestorClipRectInternal(sourceState.clip(), lcaState.clip(),
+                                           lcaState.transform(), success);
+  if (!success) {
+    if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
+      // On SPv1 we may fail when the paint invalidation container creates an
+      // overflow clip (in ancestorState) which is not in localState of an
+      // out-of-flow positioned descendant. See crbug.com/513108 and layout test
+      // compositing/overflow/handle-non-ancestor-clip-parent.html (run with
+      // --enable-prefer-compositing-to-lcd-text) for details.
+      // Ignore it for SPv1 for now.
+      success = true;
+    }
+    return result;
+  }
+  if (!result.isInfinite()) {
+    FloatRect final = ancestorToLocalRect(result.rect(), lcaTransform,
+                                          destinationState.transform());
+    result.setRect(final);
+  }
   return result;
 }
 
 FloatClipRect GeometryMapper::localToAncestorClipRectInternal(
-    const PropertyTreeState& localState,
-    const PropertyTreeState& ancestorState,
+    const ClipPaintPropertyNode* descendant,
+    const ClipPaintPropertyNode* ancestorClip,
+    const TransformPaintPropertyNode* ancestorTransform,
     bool& success) {
-  FloatClipRect clip(LayoutRect::infiniteIntRect());
-  if (localState.clip() == ancestorState.clip()) {
+  FloatClipRect clip;
+  if (descendant == ancestorClip) {
     success = true;
+    // Return an infinite clip.
     return clip;
   }
 
-  ClipCache& clipCache =
-      getClipCache(ancestorState.transform(), ancestorState.clip());
-  const ClipPaintPropertyNode* clipNode = localState.clip();
+  ClipCache& clipCache = getClipCache(ancestorClip, ancestorTransform);
+  const ClipPaintPropertyNode* clipNode = descendant;
   Vector<const ClipPaintPropertyNode*> intermediateNodes;
 
   // Iterate over the path from localState.clip to ancestorState.clip. Stop if
   // we've found a memoized (precomputed) clip for any particular node.
-  while (clipNode && clipNode != ancestorState.clip()) {
+  while (clipNode && clipNode != ancestorClip) {
     auto it = clipCache.find(clipNode);
     if (it != clipCache.end()) {
       clip = it->value;
@@ -280,7 +342,7 @@
        ++it) {
     success = false;
     const TransformationMatrix& transformMatrix = localToAncestorMatrixInternal(
-        (*it)->localTransformSpace(), ancestorState.transform(), success);
+        (*it)->localTransformSpace(), ancestorTransform, success);
     if (!success)
       return clip;
     FloatRect mappedRect = transformMatrix.mapRect((*it)->clipRect().rect());
@@ -291,7 +353,7 @@
   }
 
   success = true;
-  return clipCache.find(localState.clip())->value;
+  return clipCache.find(descendant)->value;
 }
 
 const TransformationMatrix& GeometryMapper::localToAncestorMatrix(
@@ -350,7 +412,8 @@
 }
 
 void GeometryMapper::clearCache() {
-  m_data.clear();
+  m_transformCache.clear();
+  m_clipCache.clear();
 }
 
 namespace {
diff --git a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.h b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.h
index 6ed23d31..0551407 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.h
@@ -12,34 +12,6 @@
 
 namespace blink {
 
-// Maps from a descendant clip node to its equivalent "clip visual rect" in
-// the space of the ancestor. The clip visual rect is defined as the
-// intersection of all clips between the descendant and the ancestor (*not*
-// including the ancestor) in the clip tree, individually transformed from
-// their localTransformSpace into the ancestor's localTransformSpace.
-// If one of the clip that contributes to it has a border radius, the
-// hasRadius() field is set to true.
-typedef HashMap<const ClipPaintPropertyNode*, FloatClipRect> ClipCache;
-
-// Maps from a transform node that is a descendant of the ancestor to the
-// combined transform between the descendant's and the ancestor's coordinate
-typedef HashMap<const TransformPaintPropertyNode*, TransformationMatrix>
-    TransformCache;
-
-struct PrecomputedDataForAncestor {
-  TransformCache toAncestorTransforms;
-
-  // There can be multiple clips within the same transform space. This
-  // maps from the desired destination clip within the same transform
-  // space to its corresponding ClipCache.
-  HashMap<const ClipPaintPropertyNode*, std::unique_ptr<ClipCache>>
-      precomputedClips;
-
-  static std::unique_ptr<PrecomputedDataForAncestor> create() {
-    return WTF::makeUnique<PrecomputedDataForAncestor>();
-  }
-};
-
 // GeometryMapper is a helper class for fast computations of transformed and
 // visual rects in different PropertyTreeStates. The design document has a
 // number of details on use cases, algorithmic definitions, and running times.
@@ -139,6 +111,12 @@
       const PropertyTreeState& localTransformState,
       const PropertyTreeState& ancestorState);
 
+  // Like localToAncestorClipRect, except it can handle destination transform
+  // spaces which are not direct ancestors of the source transform space.
+  FloatClipRect sourceToDestinationClipRect(
+      const PropertyTreeState& sourceState,
+      const PropertyTreeState& destinationState);
+
   // Returns the lowest common ancestor in the paint property tree.
   template <typename NodeType>
   static PLATFORM_EXPORT const NodeType* lowestCommonAncestor(const NodeType*,
@@ -179,8 +157,14 @@
       bool& success);
 
   FloatClipRect localToAncestorClipRectInternal(
-      const PropertyTreeState& localTransformState,
-      const PropertyTreeState& ancestorState,
+      const ClipPaintPropertyNode* descendant,
+      const ClipPaintPropertyNode* ancestorClip,
+      const TransformPaintPropertyNode* ancestorTransform,
+      bool& success);
+
+  FloatClipRect sourceToDestinationClipRectInternal(
+      const PropertyTreeState& sourceState,
+      const PropertyTreeState& destinationState,
       bool& success);
 
   FloatClipRect slowLocalToAncestorVisualRectWithEffects(
@@ -189,24 +173,43 @@
       const PropertyTreeState& ancestorState,
       bool& success);
 
-  // Returns the precomputed data if already set, or adds and memoizes a new
-  // PrecomputedDataForAncestor otherwise.
-  PrecomputedDataForAncestor& getPrecomputedDataForAncestor(
-      const TransformPaintPropertyNode*);
+  // Maps from a transform node that is a descendant of the implied ancestor
+  // transform node to the combined transform between the descendant's and the
+  // ancestor's coordinate space.
+  // The "implied ancestor" is the key of the m_transformCache object for which
+  // this TransformCache is a value.
+  using TransformCache =
+      HashMap<const TransformPaintPropertyNode*, TransformationMatrix>;
 
   // Returns the transform cache for the given ancestor transform node.
-  TransformCache& getTransformCache(const TransformPaintPropertyNode*);
+  TransformCache& getTransformCache(const TransformPaintPropertyNode* ancestor);
 
-  // Returns the clip cache for the given ancestor clip node.
-  ClipCache& getClipCache(const TransformPaintPropertyNode*,
-                          const ClipPaintPropertyNode*);
+  // Maps from a descendant clip node to its equivalent "clip visual rect" in
+  // the local transform space of the implied ancestor clip node. The clip
+  // visual rect is defined as the intersection of all clips between the
+  // descendant and the ancestor (*not* including the ancestor) in the clip
+  // tree, individually transformed from their localTransformSpace into the
+  // ancestor's localTransformSpace. If one of the clips that contributes to it
+  // has a border radius, the hasRadius() field is set to true.
+  // The "implied ancestor" is the key of the TransformToClip cachefor which
+  // this ClipCache is a value.
+  using ClipCache = HashMap<const ClipPaintPropertyNode*, FloatClipRect>;
+
+  using TransformToClip =
+      HashMap<const TransformPaintPropertyNode*, std::unique_ptr<ClipCache>>;
+
+  // Returns the clip cache for the given transform space relative to the
+  // given ancestor clip node.
+  ClipCache& getClipCache(const ClipPaintPropertyNode* ancestorClip,
+                          const TransformPaintPropertyNode* ancestorTransform);
 
   friend class GeometryMapperTest;
   friend class PaintLayerClipperTest;
 
-  HashMap<const TransformPaintPropertyNode*,
-          std::unique_ptr<PrecomputedDataForAncestor>>
-      m_data;
+  HashMap<const TransformPaintPropertyNode*, std::unique_ptr<TransformCache>>
+      m_transformCache;
+  HashMap<const ClipPaintPropertyNode*, std::unique_ptr<TransformToClip>>
+      m_clipCache;
 
   const TransformationMatrix m_identity;
 
diff --git a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTest.cpp b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTest.cpp
index c18e722c..f4a913e 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTest.cpp
@@ -31,14 +31,15 @@
     return state;
   }
 
-  TransformCache& getTransformCache(
+  GeometryMapper::TransformCache& getTransformCache(
       const PropertyTreeState& propertyTreeState) {
     return geometryMapper->getTransformCache(propertyTreeState.transform());
   }
 
-  ClipCache& getClipCache(const PropertyTreeState& propertyTreeState) {
-    return geometryMapper->getClipCache(propertyTreeState.transform(),
-                                        propertyTreeState.clip());
+  GeometryMapper::ClipCache& getClipCache(
+      const PropertyTreeState& propertyTreeState) {
+    return geometryMapper->getClipCache(propertyTreeState.clip(),
+                                        propertyTreeState.transform());
   }
 
   const TransformPaintPropertyNode* lowestCommonAncestor(
@@ -105,6 +106,13 @@
         << ", expected: " << expected.height();                                \
   } while (false)
 
+#define EXPECT_CLIP_RECT_EQ(expected, actual)              \
+  do {                                                     \
+    EXPECT_EQ(expected.isInfinite(), actual.isInfinite()); \
+    if (!expected.isInfinite())                            \
+      EXPECT_RECT_EQ(expected.rect(), actual.rect());      \
+  } while (false)
+
 #define CHECK_MAPPINGS(inputRect, expectedVisualRect, expectedTransformedRect, \
                        expectedTransformToAncestor,                            \
                        expectedClipInAncestorSpace, localPropertyTreeState,    \
@@ -117,7 +125,7 @@
     clipRect = geometryMapper->localToAncestorClipRect(                        \
         localPropertyTreeState, ancestorPropertyTreeState);                    \
     EXPECT_EQ(hasRadius, clipRect.hasRadius());                                \
-    EXPECT_RECT_EQ(expectedClipInAncestorSpace, clipRect.rect());              \
+    EXPECT_CLIP_RECT_EQ(expectedClipInAncestorSpace, clipRect);                \
     clipRect = geometryMapper->sourceToDestinationVisualRect(                  \
         inputRect, localPropertyTreeState, ancestorPropertyTreeState);         \
     EXPECT_EQ(hasRadius, clipRect.hasRadius());                                \
@@ -139,8 +147,12 @@
     if (ancestorPropertyTreeState.clip() != localPropertyTreeState.clip()) {   \
       EXPECT_EQ(expectedClipInAncestorSpace,                                   \
                 getClipCache(ancestorPropertyTreeState)                        \
-                    .get(localPropertyTreeState.clip())                        \
-                    .rect());                                                  \
+                    .get(localPropertyTreeState.clip()))                       \
+          << expectedClipInAncestorSpace.rect().toString() << " "              \
+          << getClipCache(ancestorPropertyTreeState)                           \
+                 .get(localPropertyTreeState.clip())                           \
+                 .rect()                                                       \
+                 .toString();                                                  \
     }                                                                          \
   } while (false)
 
@@ -149,8 +161,7 @@
 
   bool hasRadius = false;
   CHECK_MAPPINGS(input, input, input,
-                 TransformPaintPropertyNode::root()->matrix(),
-                 ClipPaintPropertyNode::root()->clipRect().rect(),
+                 TransformPaintPropertyNode::root()->matrix(), FloatClipRect(),
                  rootPropertyTreeState(), rootPropertyTreeState(), hasRadius);
 }
 
@@ -165,9 +176,8 @@
   FloatRect input(0, 0, 100, 100);
 
   bool hasRadius = false;
-  CHECK_MAPPINGS(input, input, input, transform->matrix(),
-                 ClipPaintPropertyNode::root()->clipRect().rect(), localState,
-                 rootPropertyTreeState(), hasRadius);
+  CHECK_MAPPINGS(input, input, input, transform->matrix(), FloatClipRect(),
+                 localState, rootPropertyTreeState(), hasRadius);
 }
 
 TEST_F(GeometryMapperTest, TranslationTransform) {
@@ -183,9 +193,8 @@
   FloatRect output = transformMatrix.mapRect(input);
 
   bool hasRadius = false;
-  CHECK_MAPPINGS(input, output, output, transform->matrix(),
-                 ClipPaintPropertyNode::root()->clipRect().rect(), localState,
-                 rootPropertyTreeState(), hasRadius);
+  CHECK_MAPPINGS(input, output, output, transform->matrix(), FloatClipRect(),
+                 localState, rootPropertyTreeState(), hasRadius);
 
   EXPECT_RECT_EQ(input, geometryMapper->ancestorToLocalRect(
                             output, rootPropertyTreeState().transform(),
@@ -207,9 +216,8 @@
   FloatRect output = transformMatrix.mapRect(input);
 
   bool hasRadius = false;
-  CHECK_MAPPINGS(input, output, output, transformMatrix,
-                 ClipPaintPropertyNode::root()->clipRect().rect(), localState,
-                 rootPropertyTreeState(), hasRadius);
+  CHECK_MAPPINGS(input, output, output, transformMatrix, FloatClipRect(),
+                 localState, rootPropertyTreeState(), hasRadius);
 }
 
 TEST_F(GeometryMapperTest, RotationAndScaleTransformWithTransformOrigin) {
@@ -228,9 +236,8 @@
   FloatRect output = transformMatrix.mapRect(input);
 
   bool hasRadius = false;
-  CHECK_MAPPINGS(input, output, output, transformMatrix,
-                 ClipPaintPropertyNode::root()->clipRect().rect(), localState,
-                 rootPropertyTreeState(), hasRadius);
+  CHECK_MAPPINGS(input, output, output, transformMatrix, FloatClipRect(),
+                 localState, rootPropertyTreeState(), hasRadius);
 }
 
 TEST_F(GeometryMapperTest, NestedTransforms) {
@@ -254,8 +261,7 @@
   FloatRect output = final.mapRect(input);
 
   bool hasRadius = false;
-  CHECK_MAPPINGS(input, output, output, final,
-                 ClipPaintPropertyNode::root()->clipRect().rect(), localState,
+  CHECK_MAPPINGS(input, output, output, final, FloatClipRect(), localState,
                  rootPropertyTreeState(), hasRadius);
 
   // Check the cached matrix for the intermediate transform.
@@ -286,8 +292,7 @@
   FloatRect output = final.mapRect(input);
 
   bool hasRadius = false;
-  CHECK_MAPPINGS(input, output, output, final,
-                 ClipPaintPropertyNode::root()->clipRect().rect(), localState,
+  CHECK_MAPPINGS(input, output, output, final, FloatClipRect(), localState,
                  rootPropertyTreeState(), hasRadius);
 
   // Check the cached matrix for the intermediate transform.
@@ -319,9 +324,8 @@
   FloatRect output = scaleTransform.mapRect(input);
 
   bool hasRadius = false;
-  CHECK_MAPPINGS(input, output, output, scaleTransform,
-                 ClipPaintPropertyNode::root()->clipRect().rect(), localState,
-                 intermediateState, hasRadius);
+  CHECK_MAPPINGS(input, output, output, scaleTransform, FloatClipRect(),
+                 localState, intermediateState, hasRadius);
 }
 
 TEST_F(GeometryMapperTest, SimpleClip) {
@@ -340,8 +344,9 @@
                  output,  // Visual rect
                  input,   // Transformed rect (not clipped).
                  TransformPaintPropertyNode::root()
-                     ->matrix(),           // Transform matrix to ancestor space
-                 clip->clipRect().rect(),  // Clip rect in ancestor space
+                     ->matrix(),  // Transform matrix to ancestor space
+                 FloatClipRect(clip->clipRect().rect()),  // Clip rect in
+                                                          // ancestor space
                  localState,
                  rootPropertyTreeState(), hasRadius);
 }
@@ -359,13 +364,16 @@
   FloatRect input(0, 0, 100, 100);
   FloatRect output(10, 10, 50, 50);
 
+  FloatClipRect expectedClip(clip->clipRect().rect());
+  expectedClip.setHasRadius(true);
+
   bool hasRadius = true;
   CHECK_MAPPINGS(input,   // Input
                  output,  // Visual rect
                  input,   // Transformed rect (not clipped).
                  TransformPaintPropertyNode::root()
-                     ->matrix(),           // Transform matrix to ancestor space
-                 clip->clipRect().rect(),  // Clip rect in ancestor space
+                     ->matrix(),  // Transform matrix to ancestor space
+                 expectedClip,    // Clip rect in ancestor space
                  localState,
                  rootPropertyTreeState(), hasRadius);
 }
@@ -391,26 +399,81 @@
   FloatRect input(0, 0, 100, 100);
   FloatRect output1(10, 10, 30, 40);
 
+  FloatClipRect clipRect(clip1->clipRect().rect());
+  clipRect.setHasRadius(true);
+
   bool hasRadius = true;
   CHECK_MAPPINGS(input,    // Input
                  output1,  // Visual rect
                  input,    // Transformed rect (not clipped).
                  TransformPaintPropertyNode::root()
                      ->matrix(),  // Transform matrix to ancestor space
-                 clip1->clipRect().rect(),  // Clip rect in ancestor space
+                 clipRect,        // Clip rect in ancestor space
                  localState,
                  ancestorState, hasRadius);
 
   ancestorState.setClip(clip1.get());
   FloatRect output2(10, 10, 50, 50);
 
+  clipRect.setRect(clip2->clipRect().rect());
+
   hasRadius = false;
   CHECK_MAPPINGS(input,    // Input
                  output2,  // Visual rect
                  input,    // Transformed rect (not clipped).
                  TransformPaintPropertyNode::root()
                      ->matrix(),  // Transform matrix to ancestor space
-                 clip2->clipRect().rect(),  // Clip rect in ancestor space
+                 clipRect,        // Clip rect in ancestor space
+                 localState,
+                 ancestorState, hasRadius);
+}
+
+TEST_F(GeometryMapperTest, TwoClipsTransformAbove) {
+  RefPtr<TransformPaintPropertyNode> transform =
+      TransformPaintPropertyNode::create(rootPropertyTreeState().transform(),
+                                         TransformationMatrix(),
+                                         FloatPoint3D());
+
+  FloatRoundedRect clipRect1(
+      FloatRect(10, 10, 50, 50),
+      FloatRoundedRect::Radii(FloatSize(1, 1), FloatSize(), FloatSize(),
+                              FloatSize()));
+
+  RefPtr<ClipPaintPropertyNode> clip1 = ClipPaintPropertyNode::create(
+      ClipPaintPropertyNode::root(), transform.get(), clipRect1);
+
+  RefPtr<ClipPaintPropertyNode> clip2 = ClipPaintPropertyNode::create(
+      clip1, transform.get(), FloatRoundedRect(10, 10, 30, 40));
+
+  PropertyTreeState localState = rootPropertyTreeState();
+  PropertyTreeState ancestorState = rootPropertyTreeState();
+  localState.setClip(clip2.get());
+
+  FloatRect input(0, 0, 100, 100);
+  FloatRect output1(10, 10, 30, 40);
+
+  FloatClipRect expectedClip(clip2->clipRect().rect());
+  expectedClip.setHasRadius(true);
+
+  bool hasRadius = true;
+  CHECK_MAPPINGS(input,    // Input
+                 output1,  // Visual rect
+                 input,    // Transformed rect (not clipped).
+                 TransformPaintPropertyNode::root()
+                     ->matrix(),  // Transform matrix to ancestor space
+                 expectedClip,    // Clip rect in ancestor space
+                 localState,
+                 ancestorState, hasRadius);
+
+  expectedClip.setRect(clip1->clipRect().rect());
+  localState.setClip(clip1.get());
+  FloatRect output2(10, 10, 50, 50);
+  CHECK_MAPPINGS(input,    // Input
+                 output2,  // Visual rect
+                 input,    // Transformed rect (not clipped).
+                 TransformPaintPropertyNode::root()
+                     ->matrix(),  // Transform matrix to ancestor space
+                 expectedClip,    // Clip rect in ancestor space
                  localState,
                  ancestorState, hasRadius);
 }
@@ -441,8 +504,8 @@
       output,                          // Visual rect
       rotateTransform.mapRect(input),  // Transformed rect (not clipped).
       rotateTransform,                 // Transform matrix to ancestor space
-      rotateTransform.mapRect(
-          clip->clipRect().rect()),  // Clip rect in ancestor space
+      FloatClipRect(rotateTransform.mapRect(
+          clip->clipRect().rect())),  // Clip rect in ancestor space
       localState,
       rootPropertyTreeState(), hasRadius);
 }
@@ -473,7 +536,7 @@
       output,                          // Visual rect
       rotateTransform.mapRect(input),  // Transformed rect (not clipped)
       rotateTransform,                 // Transform matrix to ancestor space
-      clip->clipRect().rect(),         // Clip rect in ancestor space
+      FloatClipRect(clip->clipRect().rect()),  // Clip rect in ancestor space
       localState, rootPropertyTreeState(), hasRadius);
 }
 
@@ -508,7 +571,7 @@
         output,                          // Visual rect
         rotateTransform.mapRect(input),  // Transformed rect (not clipped)
         rotateTransform,                 // Transform matrix to ancestor space
-        clip1->clipRect().rect(),        // Clip rect in ancestor space
+        FloatClipRect(clip1->clipRect().rect()),  // Clip rect in ancestor space
         localState, rootPropertyTreeState(), hasRadius);
   }
 
@@ -534,7 +597,7 @@
         output,                          // Visual rect
         rotateTransform.mapRect(input),  // Transformed rect (not clipped)
         rotateTransform,                 // Transform matrix to ancestor space
-        mappedClip,                      // Clip rect in ancestor space
+        FloatClipRect(mappedClip),       // Clip rect in ancestor space
         localState, rootPropertyTreeState(), hasRadius);
   }
 }
@@ -747,8 +810,8 @@
   CHECK_MAPPINGS(
       input, output, FloatRect(0, 0, 300, 300),
       transformAboveEffect->matrix() * transformBelowEffect->matrix(),
-      FloatRect(30, 30, 270, 270), localState, rootPropertyTreeState(),
-      hasRadius);
+      FloatClipRect(FloatRect(30, 30, 270, 270)), localState,
+      rootPropertyTreeState(), hasRadius);
 }
 
 TEST_F(GeometryMapperTest, ReflectionWithPaintOffset) {
@@ -768,9 +831,8 @@
   FloatRect output(50, 100, 100, 50);
 
   bool hasRadius = false;
-  CHECK_MAPPINGS(input, output, input, TransformationMatrix(),
-                 ClipPaintPropertyNode::root()->clipRect().rect(), localState,
-                 rootPropertyTreeState(), hasRadius);
+  CHECK_MAPPINGS(input, output, input, TransformationMatrix(), FloatClipRect(),
+                 localState, rootPropertyTreeState(), hasRadius);
 }
 
 }  // namespace blink
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 73311ca6..dc1ece7d 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
@@ -96,16 +96,31 @@
         self._printer.write_update("Collecting tests ...")
         running_all_tests = False
         try:
-            paths, test_names, running_all_tests = self._collect_tests(args)
+            paths, all_test_names, running_all_tests = self._collect_tests(args)
         except IOError:
             # This is raised if --test-list doesn't exist
             return test_run_results.RunDetails(exit_code=test_run_results.NO_TESTS_EXIT_STATUS)
 
+        # Create a sorted list of test files so the subset chunk,
+        # if used, contains alphabetically consecutive tests.
+        if self._options.order == 'natural':
+            all_test_names.sort(key=self._port.test_key)
+        elif self._options.order == 'random':
+            all_test_names.sort()
+            random.Random(self._options.seed).shuffle(all_test_names)
+
+        test_names, tests_in_other_chunks = self._finder.split_into_chunks(all_test_names)
+
         self._printer.write_update("Parsing expectations ...")
         self._expectations = test_expectations.TestExpectations(self._port, test_names)
 
         tests_to_run, tests_to_skip = self._prepare_lists(paths, test_names)
-        self._printer.print_found(len(test_names), len(tests_to_run), self._options.repeat_each, self._options.iterations)
+
+        self._expectations.remove_tests(tests_in_other_chunks)
+
+        self._printer.print_found(
+            len(all_test_names), len(test_names), len(tests_to_run),
+            self._options.repeat_each, self._options.iterations)
 
         # Check to make sure we're not skipping every test.
         if not tests_to_run:
@@ -242,21 +257,6 @@
         tests_to_skip = self._finder.skip_tests(paths, test_names, self._expectations, self._http_tests(test_names))
         tests_to_run = [test for test in test_names if test not in tests_to_skip]
 
-        if not tests_to_run:
-            return tests_to_run, tests_to_skip
-
-        # Create a sorted list of test files so the subset chunk,
-        # if used, contains alphabetically consecutive tests.
-        if self._options.order == 'natural':
-            tests_to_run.sort(key=self._port.test_key)
-        elif self._options.order == 'random':
-            tests_to_run.sort()
-            random.Random(self._options.seed).shuffle(tests_to_run)
-
-        tests_to_run, tests_in_other_chunks = self._finder.split_into_chunks(tests_to_run)
-        self._expectations.add_extra_skipped_tests(tests_in_other_chunks)
-        tests_to_skip.update(tests_in_other_chunks)
-
         return tests_to_run, tests_to_skip
 
     def _test_input_for_file(self, test_file):
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py
index 6d1aee2..38d7593 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py
@@ -1174,6 +1174,11 @@
             model.add_expectation_line(expectation_line)
         self._model.merge_model(model)
 
+    def remove_tests(self, tests_to_remove):
+        for test in self._expectations:
+            if test.name and test.name in tests_to_remove:
+                self.remove_expectation_line(test)
+
     def add_expectations_from_bot(self):
         # FIXME: With mode 'very-flaky' and 'maybe-flaky', this will show the expectations entry in the flakiness
         # dashboard rows for each test to be whatever the bot thinks they should be. Is this a good thing?
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing.py
index 3170b040..3f281ba 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing.py
@@ -101,11 +101,14 @@
         self._print_default('Command line: ' + ' '.join(self._port.driver_cmd_line()))
         self._print_default('')
 
-    def print_found(self, num_all_test_files, num_to_run, repeat_each, iterations):
-        found_str = 'Found %s; running %d' % (grammar.pluralize('test', num_all_test_files), num_to_run)
+    def print_found(self, num_all_test_files, num_shard_test_files, num_to_run, repeat_each, iterations):
+        found_str = 'Found %s' % grammar.pluralize('test', num_shard_test_files)
+        if num_all_test_files != num_shard_test_files:
+            found_str += ' (total %d)' % num_all_test_files
+        found_str += '; running %d' % num_to_run
         if repeat_each * iterations > 1:
             found_str += ' (%d times each: --repeat-each=%d --iterations=%d)' % (repeat_each * iterations, repeat_each, iterations)
-        found_str += ', skipping %d' % (num_all_test_files - num_to_run)
+        found_str += ', skipping %d' % (num_shard_test_files - num_to_run)
         self._print_default(found_str + '.')
 
     def print_expected(self, run_results, tests_with_result_type_callback):
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing_unittest.py
index 58a05d5..d0343cb 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing_unittest.py
@@ -232,11 +232,16 @@
     def test_print_found(self):
         printer, err = self.get_printer()
 
-        printer.print_found(100, 10, 1, 1)
+        self.reset(err)
+        printer.print_found(100, 100, 10, 1, 1)
         self.assertWritten(err, ["Found 100 tests; running 10, skipping 90.\n"])
 
         self.reset(err)
-        printer.print_found(100, 10, 2, 3)
+        printer.print_found(100, 20, 10, 1, 1)
+        self.assertWritten(err, ["Found 20 tests (total 100); running 10, skipping 10.\n"])
+
+        self.reset(err)
+        printer.print_found(100, 100, 10, 2, 3)
         self.assertWritten(err, ["Found 100 tests; running 10 (6 times each: --repeat-each=2 --iterations=3), skipping 90.\n"])
 
     def test_debug_rwt_logging_is_throttled(self):
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/chromium_commit.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/chromium_commit.py
index aba9101..efc1e57 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/chromium_commit.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/chromium_commit.py
@@ -20,6 +20,8 @@
                     'refs/heads/master@{#431915}'
         """
         self.host = host
+        self.absolute_chromium_dir = absolute_chromium_dir(host)
+        self.absolute_chromium_wpt_dir = absolute_chromium_wpt_dir(host)
 
         assert sha or position, 'requires sha or position'
         assert not (sha and position), 'cannot accept both sha and position'
@@ -34,9 +36,6 @@
         self.sha = sha
         self.position = position
 
-        self.absolute_chromium_dir = absolute_chromium_dir(host)
-        self.absolute_chromium_wpt_dir = absolute_chromium_wpt_dir(host)
-
     def num_behind_master(self):
         """Returns the number of commits this commit is behind origin/master.
         It is inclusive of this commit and of the latest commit.
@@ -48,7 +47,7 @@
     def position_to_sha(self, commit_position):
         return self.host.executive.run_command([
             'git', 'crrev-parse', commit_position
-        ], cwd=absolute_chromium_dir).strip()
+        ], cwd=self.absolute_chromium_dir).strip()
 
     def subject(self):
         return self.host.executive.run_command([
@@ -58,7 +57,7 @@
     def body(self):
         return self.host.executive.run_command([
             'git', 'show', '--format=%b', '--no-patch', self.sha
-        ], cwd=absolute_chromium_dir)
+        ], cwd=self.absolute_chromium_dir)
 
     def author(self):
         return self.host.executive.run_command([
diff --git a/tools/json_schema_compiler/test/error_generation_unittest.cc b/tools/json_schema_compiler/test/error_generation_unittest.cc
index 5c3f1b8..73fcfdc 100644
--- a/tools/json_schema_compiler/test/error_generation_unittest.cc
+++ b/tools/json_schema_compiler/test/error_generation_unittest.cc
@@ -5,6 +5,7 @@
 #include "tools/json_schema_compiler/test/error_generation.h"
 
 #include "base/json/json_writer.h"
+#include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "tools/json_schema_compiler/test/test_util.h"
@@ -39,7 +40,7 @@
     EXPECT_TRUE(EqualsUtf16("", GetPopulateError<TestType>(*value)));
   }
   {
-    std::unique_ptr<base::BinaryValue> value(new base::BinaryValue());
+    auto value = base::MakeUnique<base::Value>(base::Value::Type::BINARY);
     EXPECT_TRUE(EqualsUtf16("expected dictionary, got binary",
         GetPopulateError<TestType>(*value)));
   }
@@ -52,7 +53,7 @@
         GetPopulateError<ChoiceType::Integers>(*value)));
   }
   {
-    std::unique_ptr<base::BinaryValue> value(new base::BinaryValue());
+    auto value = base::MakeUnique<base::Value>(base::Value::Type::BINARY);
     EXPECT_TRUE(EqualsUtf16("expected integers or integer, got binary",
         GetPopulateError<ChoiceType::Integers>(*value)));
   }
@@ -178,7 +179,7 @@
 TEST(JsonSchemaCompilerErrorTest, BinaryTypeExpected) {
   {
     std::unique_ptr<base::DictionaryValue> value =
-        Dictionary("data", new base::BinaryValue());
+        Dictionary("data", new base::Value(base::Value::Type::BINARY));
     EXPECT_TRUE(EqualsUtf16("", GetPopulateError<BinaryData>(*value)));
   }
   {
@@ -244,7 +245,7 @@
 TEST(JsonSchemaCompilerErrorTest, OptionalBinaryTypeFailure) {
   {
     std::unique_ptr<base::DictionaryValue> value =
-        Dictionary("data", new base::BinaryValue());
+        Dictionary("data", new base::Value(base::Value::Type::BINARY));
     EXPECT_TRUE(EqualsUtf16("", GetPopulateError<OptionalBinaryData>(*value)));
   }
   {
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index 6e5631b..bc9223a5 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -65,7 +65,6 @@
     "//ui/display",
     "//ui/gfx",
     "//ui/gfx/geometry",
-    "//url",
   ]
 }
 
diff --git a/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java b/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java
index 97c1e48..9a9d89b 100644
--- a/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java
+++ b/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java
@@ -5,7 +5,6 @@
 package org.chromium.ui.base;
 
 import android.content.ClipData;
-import android.content.Intent;
 import android.graphics.Bitmap;
 import android.os.Build;
 import android.view.View;
@@ -14,27 +13,18 @@
 import android.widget.ImageView;
 
 import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.base.Log;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 
-import java.net.URISyntaxException;
-
 /**
  * Class to acquire, position, and remove anchor views from the implementing View.
  */
 @JNINamespace("ui")
 public abstract class ViewAndroidDelegate {
 
-    private static final String TAG = "ViewAndroidDelegate";
-
     // TODO(hush): use View#DRAG_FLAG_GLOBAL when Chromium starts to build with API 24.
     private static final int DRAG_FLAG_GLOBAL = 1 << 8;
 
-    private static final String GEO_SCHEME = "geo";
-    private static final String TEL_SCHEME = "tel";
-    private static final String MAILTO_SCHEME = "mailto";
-
     /**
      * @return An anchor view that can be used to anchor decoration views like Autofill popup.
      */
@@ -115,57 +105,6 @@
     }
 
     /**
-     * Called whenever the background color of the page changes as notified by Blink.
-     * @param color The new ARGB color of the page background.
-     */
-    @CalledByNative
-    public void onBackgroundColorChanged(int color) {}
-
-    /**
-     * Notify the client of the position of the top controls.
-     * @param topControlsOffsetY The Y offset of the top controls in physical pixels.
-     * @param topContentOffsetY The Y offset of the content in physical pixels.
-     */
-    @CalledByNative
-    public void onTopControlsChanged(float topControlsOffsetY, float topContentOffsetY) {}
-
-    /**
-     * Notify the client of the position of the bottom controls.
-     * @param bottomControlsOffsetY The Y offset of the bottom controls in physical pixels.
-     * @param bottomContentOffsetY The Y offset of the content in physical pixels.
-     */
-    @CalledByNative
-    public void onBottomControlsChanged(float bottomControlsOffsetY, float bottomContentOffsetY) {}
-
-    /**
-     * Called when a new content intent is requested to be started.
-     * Invokes {@link #startContentIntent(Intent, String, boolean)} only if the parsed
-     * intent is valid and the scheme is acceptable.
-     */
-    @CalledByNative
-    private void onStartContentIntent(String intentUrl, boolean isMainFrame) {
-        Intent intent;
-        try {
-            intent = Intent.parseUri(intentUrl, Intent.URI_INTENT_SCHEME);
-        } catch (URISyntaxException e) {
-            Log.d(TAG, "Bad URI %s", intentUrl, e);
-            return;
-        }
-        String scheme = intent.getScheme();
-        if (!(GEO_SCHEME.equals(scheme) || TEL_SCHEME.equals(scheme)
-                || MAILTO_SCHEME.equals(scheme))) {
-            Log.d(TAG, "Invalid scheme for URI %s", intentUrl);
-            return;
-        }
-        startContentIntent(intent, intentUrl, isMainFrame);
-    }
-
-    /**
-     * Start a new content intent.
-     */
-    public void startContentIntent(Intent intent, String intentUrl, boolean isMainFrame) {}
-
-    /**
      * @return container view that the anchor views are added to. May be null.
      */
     @CalledByNative
diff --git a/ui/android/view_android.cc b/ui/android/view_android.cc
index e1716c14..43f084f 100644
--- a/ui/android/view_android.cc
+++ b/ui/android/view_android.cc
@@ -7,17 +7,14 @@
 #include <algorithm>
 
 #include "base/android/jni_android.h"
-#include "base/android/jni_string.h"
 #include "cc/layers/layer.h"
 #include "jni/ViewAndroidDelegate_jni.h"
 #include "ui/android/window_android.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
-#include "url/gurl.h"
 
 namespace ui {
 
-using base::android::ConvertUTF8ToJavaString;
 using base::android::JavaRef;
 using base::android::ScopedJavaLocalRef;
 
@@ -218,9 +215,7 @@
   if (delegate.is_null())
     return false;
   JNIEnv* env = base::android::AttachCurrentThread();
-  return Java_ViewAndroidDelegate_startDragAndDrop(env,
-                                                   delegate,
-                                                   jtext,
+  return Java_ViewAndroidDelegate_startDragAndDrop(env, delegate, jtext,
                                                    jimage);
 }
 
@@ -246,52 +241,4 @@
   return false;
 }
 
-void ViewAndroid::OnBackgroundColorChanged(unsigned int color) {
-  ScopedJavaLocalRef<jobject> delegate(GetViewAndroidDelegate());
-  if (delegate.is_null())
-    return;
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_ViewAndroidDelegate_onBackgroundColorChanged(env,
-                                                    delegate,
-                                                    color);
-}
-
-void ViewAndroid::OnTopControlsChanged(float top_controls_offset,
-                                       float top_content_offset) {
-  ScopedJavaLocalRef<jobject> delegate(GetViewAndroidDelegate());
-  if (delegate.is_null())
-    return;
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_ViewAndroidDelegate_onTopControlsChanged(env,
-                                                delegate,
-                                                top_controls_offset,
-                                                top_content_offset);
-}
-
-void ViewAndroid::OnBottomControlsChanged(float bottom_controls_offset,
-                                          float bottom_content_offset) {
-  ScopedJavaLocalRef<jobject> delegate(GetViewAndroidDelegate());
-  if (delegate.is_null())
-    return;
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_ViewAndroidDelegate_onBottomControlsChanged(env,
-                                                   delegate,
-                                                   bottom_controls_offset,
-                                                   bottom_content_offset);
-}
-
-void ViewAndroid::StartContentIntent(const GURL& content_url,
-                                       bool is_main_frame) {
-  ScopedJavaLocalRef<jobject> delegate(GetViewAndroidDelegate());
-  if (delegate.is_null())
-    return;
-  JNIEnv* env = base::android::AttachCurrentThread();
-  ScopedJavaLocalRef<jstring> jcontent_url =
-      ConvertUTF8ToJavaString(env, content_url.spec());
-  Java_ViewAndroidDelegate_onStartContentIntent(env,
-                                                delegate,
-                                                jcontent_url,
-                                                is_main_frame);
-
-}
 }  // namespace ui
diff --git a/ui/android/view_android.h b/ui/android/view_android.h
index 22780e6b..e446048 100644
--- a/ui/android/view_android.h
+++ b/ui/android/view_android.h
@@ -13,8 +13,6 @@
 #include "ui/android/view_client.h"
 #include "ui/gfx/geometry/rect_f.h"
 
-class GURL;
-
 namespace cc {
 class Layer;
 }
@@ -104,14 +102,6 @@
   bool StartDragAndDrop(const base::android::JavaRef<jstring>& jtext,
                         const base::android::JavaRef<jobject>& jimage);
 
-  void OnBackgroundColorChanged(unsigned int color);
-  void OnTopControlsChanged(float top_controls_offset,
-                            float top_content_offset);
-  void OnBottomControlsChanged(float bottom_controls_offset,
-                               float bottom_content_offset);
-  void StartContentIntent(const GURL& content_url,
-                          bool is_main_frame);
-
   ScopedAnchorView AcquireAnchorView();
   void SetAnchorRect(const base::android::JavaRef<jobject>& anchor,
                      const gfx::RectF& bounds);
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index c748054..b0b8e8ae8 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -170,6 +170,8 @@
     "shadow_value.h",
     "skbitmap_operations.cc",
     "skbitmap_operations.h",
+    "skia_color_space_util.cc",
+    "skia_color_space_util.h",
     "skia_util.cc",
     "skia_util.h",
     "switches.cc",
diff --git a/ui/gfx/color_space.cc b/ui/gfx/color_space.cc
index e0eb7b94..0f5a42b 100644
--- a/ui/gfx/color_space.cc
+++ b/ui/gfx/color_space.cc
@@ -10,30 +10,11 @@
 #include "base/synchronization/lock.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "ui/gfx/icc_profile.h"
+#include "ui/gfx/skia_color_space_util.h"
 #include "ui/gfx/transform.h"
 
 namespace gfx {
 
-namespace {
-
-SkColorSpaceTransferFn InvertTransferFn(SkColorSpaceTransferFn fn) {
-  SkColorSpaceTransferFn fn_inv = {0};
-  if (fn.fA > 0 && fn.fG > 0) {
-    double a_to_the_g = pow(fn.fA, fn.fG);
-    fn_inv.fA = 1.f / a_to_the_g;
-    fn_inv.fB = -fn.fE / a_to_the_g;
-    fn_inv.fG = 1.f / fn.fG;
-  }
-  fn_inv.fD = fn.fC * fn.fD + fn.fF;
-  fn_inv.fE = -fn.fB / fn.fA;
-  if (fn.fC != 0) {
-    fn_inv.fC = 1.f / fn.fC;
-    fn_inv.fF = -fn.fF / fn.fC;
-  }
-  return fn_inv;
-}
-};
-
 ColorSpace::PrimaryID ColorSpace::PrimaryIDFromInt(int primary_id) {
   if (primary_id < 0 || primary_id > static_cast<int>(PrimaryID::LAST))
     return PrimaryID::UNKNOWN;
@@ -486,7 +467,7 @@
 bool ColorSpace::GetInverseTransferFunction(SkColorSpaceTransferFn* fn) const {
   if (!GetTransferFunction(fn))
     return false;
-  *fn = InvertTransferFn(*fn);
+  *fn = SkTransferFnInverse(*fn);
   return true;
 }
 
diff --git a/ui/gfx/color_space.h b/ui/gfx/color_space.h
index d0fe36e8..798b9305a 100644
--- a/ui/gfx/color_space.h
+++ b/ui/gfx/color_space.h
@@ -21,7 +21,6 @@
 namespace gfx {
 
 class ICCProfile;
-class ColorSpaceToColorSpaceTransform;
 
 // Used to represet a color space for the purpose of color conversion.
 // This is designed to be safe and compact enough to send over IPC
@@ -207,7 +206,8 @@
   sk_sp<SkColorSpace> icc_profile_sk_color_space_;
 
   friend class ICCProfile;
-  friend class ColorSpaceToColorSpaceTransform;
+  friend class ColorTransform;
+  friend class ColorTransformInternal;
   friend class ColorSpaceWin;
   friend struct IPC::ParamTraits<gfx::ColorSpace>;
   FRIEND_TEST_ALL_PREFIXES(SimpleColorSpace, GetColorSpace);
diff --git a/ui/gfx/color_transform.cc b/ui/gfx/color_transform.cc
index ecc8aaf2..49a3026 100644
--- a/ui/gfx/color_transform.cc
+++ b/ui/gfx/color_transform.cc
@@ -5,14 +5,17 @@
 #include "ui/gfx/color_transform.h"
 
 #include <algorithm>
-#include <vector>
+#include <cmath>
+#include <list>
+#include <memory>
 
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "third_party/qcms/src/qcms.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/icc_profile.h"
+#include "ui/gfx/skia_color_space_util.h"
 #include "ui/gfx/transform.h"
-#include "third_party/qcms/src/qcms.h"
 
 #ifndef THIS_MUST_BE_INCLUDED_AFTER_QCMS_H
 extern "C" {
@@ -20,15 +23,26 @@
 };
 #endif
 
+using std::exp;
+using std::log;
+using std::max;
+using std::min;
+using std::pow;
+using std::sqrt;
+
 namespace gfx {
 
-float EvalSkTransferFn(const SkColorSpaceTransferFn& fn, float x) {
-  if (x < 0)
-    return 0;
-  if (x < fn.fD)
-    return fn.fC * x + fn.fF;
-  return powf(fn.fA * x + fn.fB, fn.fG) + fn.fE;
-}
+namespace {
+
+// Helper for scoped QCMS profiles.
+struct QcmsProfileDeleter {
+  void operator()(qcms_profile* p) {
+    if (p) {
+      qcms_profile_release(p);
+    }
+  }
+};
+using ScopedQcmsProfile = std::unique_ptr<qcms_profile, QcmsProfileDeleter>;
 
 Transform Invert(const Transform& t) {
   Transform ret = t;
@@ -58,11 +72,11 @@
       float a = 1.099296826809442f;
       float b = 0.018053968510807f;
       if (v < -b) {
-        return -a * powf(-v, 0.45f) + (a - 1.0f);
+        return -a * pow(-v, 0.45f) + (a - 1.0f);
       } else if (v <= b) {
         return 4.5f * v;
       } else {
-        return a * powf(v, 0.45f) - (a - 1.0f);
+        return a * pow(v, 0.45f) - (a - 1.0f);
       }
     }
 
@@ -71,24 +85,24 @@
       float b = 0.018f;
       float l = 0.0045f;
       if (v < -l) {
-        return -(a * powf(-4.0f * v, 0.45f) + (a - 1.0f)) / 4.0f;
+        return -(a * pow(-4.0f * v, 0.45f) + (a - 1.0f)) / 4.0f;
       } else if (v <= b) {
         return 4.5f * v;
       } else {
-        return a * powf(v, 0.45f) - (a - 1.0f);
+        return a * pow(v, 0.45f) - (a - 1.0f);
       }
     }
 
     case ColorSpace::TransferID::SMPTEST2084: {
       // Go from scRGB levels to 0-1.
       v *= 80.0f / 10000.0f;
-      v = fmax(0.0f, v);
+      v = max(0.0f, v);
       float m1 = (2610.0f / 4096.0f) / 4.0f;
       float m2 = (2523.0f / 4096.0f) * 128.0f;
       float c1 = 3424.0f / 4096.0f;
       float c2 = (2413.0f / 4096.0f) * 32.0f;
       float c3 = (2392.0f / 4096.0f) * 32.0f;
-      return powf((c1 + c2 * powf(v, m1)) / (1.0f + c3 * powf(v, m1)), m2);
+      return pow((c1 + c2 * pow(v, m1)) / (1.0f + c3 * pow(v, m1)), m2);
     }
 
     // Spec: http://www.arib.or.jp/english/html/overview/doc/2-STD-B67v1_0.pdf
@@ -96,9 +110,9 @@
       const float a = 0.17883277f;
       const float b = 0.28466892f;
       const float c = 0.55991073f;
-      v = fmax(0.0f, v);
+      v = max(0.0f, v);
       if (v <= 1)
-        return 0.5f * sqrtf(v);
+        return 0.5f * sqrt(v);
       else
         return a * log(v - b) + c;
     }
@@ -116,22 +130,22 @@
     case ColorSpace::TransferID::LOG:
       if (v < 0.0f)
         return 0.0f;
-      return powf(10.0f, (v - 1.0f) * 2.0f);
+      return pow(10.0f, (v - 1.0f) * 2.0f);
 
     case ColorSpace::TransferID::LOG_SQRT:
       if (v < 0.0f)
         return 0.0f;
-      return powf(10.0f, (v - 1.0f) * 2.5f);
+      return pow(10.0f, (v - 1.0f) * 2.5f);
 
     case ColorSpace::TransferID::IEC61966_2_4: {
       float a = 1.099296826809442f;
       float b = 0.018053968510807f;
       if (v < FromLinear(ColorSpace::TransferID::IEC61966_2_4, -a)) {
-        return -powf((a - 1.0f - v) / a, 1.0f / 0.45f);
+        return -pow((a - 1.0f - v) / a, 1.0f / 0.45f);
       } else if (v <= FromLinear(ColorSpace::TransferID::IEC61966_2_4, b)) {
         return v / 4.5f;
       } else {
-        return powf((v + a - 1.0f) / a, 1.0f / 0.45f);
+        return pow((v + a - 1.0f) / a, 1.0f / 0.45f);
       }
     }
 
@@ -140,24 +154,23 @@
       float b = 0.018f;
       float l = 0.0045f;
       if (v < FromLinear(ColorSpace::TransferID::BT1361_ECG, -l)) {
-        return -powf((1.0f - a - v * 4.0f) / a, 1.0f / 0.45f) / 4.0f;
+        return -pow((1.0f - a - v * 4.0f) / a, 1.0f / 0.45f) / 4.0f;
       } else if (v <= FromLinear(ColorSpace::TransferID::BT1361_ECG, b)) {
         return v / 4.5f;
       } else {
-        return powf((v + a - 1.0f) / a, 1.0f / 0.45f);
+        return pow((v + a - 1.0f) / a, 1.0f / 0.45f);
       }
     }
 
     case ColorSpace::TransferID::SMPTEST2084: {
-      v = fmax(0.0f, v);
+      v = max(0.0f, v);
       float m1 = (2610.0f / 4096.0f) / 4.0f;
       float m2 = (2523.0f / 4096.0f) * 128.0f;
       float c1 = 3424.0f / 4096.0f;
       float c2 = (2413.0f / 4096.0f) * 32.0f;
       float c3 = (2392.0f / 4096.0f) * 32.0f;
-      v = powf(
-          fmax(powf(v, 1.0f / m2) - c1, 0) / (c2 - c3 * powf(v, 1.0f / m2)),
-          1.0f / m1);
+      v = pow(max(pow(v, 1.0f / m2) - c1, 0.0f) / (c2 - c3 * pow(v, 1.0f / m2)),
+              1.0f / m1);
       // This matches the scRGB definition that 1.0 means 80 nits.
       // TODO(hubbe): It would be *nice* if 1.0 meant more than that, but
       // that might be difficult to do right now.
@@ -166,12 +179,12 @@
     }
 
     case ColorSpace::TransferID::SMPTEST2084_NON_HDR:
-      v = fmax(0.0f, v);
-      return fmin(2.3f * pow(v, 2.8f), v / 5.0f + 0.8f);
+      v = max(0.0f, v);
+      return min(2.3f * pow(v, 2.8f), v / 5.0f + 0.8f);
 
     // Spec: http://www.arib.or.jp/english/html/overview/doc/2-STD-B67v1_0.pdf
     case ColorSpace::TransferID::ARIB_STD_B67: {
-      v = fmax(0.0f, v);
+      v = max(0.0f, v);
       const float a = 0.17883277f;
       const float b = 0.28466892f;
       const float c = 0.55991073f;
@@ -204,79 +217,105 @@
   return Transform(range_adjust_matrix);
 }
 
+Transform GetPrimaryTransform(const gfx::ColorSpace& color_space) {
+  SkMatrix44 primary_matrix;
+  color_space.GetPrimaryMatrix(&primary_matrix);
+  return Transform(primary_matrix);
+}
+
+}  // namespace
+
 class ColorTransformMatrix;
-class ColorTransformToLinear;
 class ColorTransformFromLinear;
 class ColorTransformToBT2020CL;
 class ColorTransformFromBT2020CL;
 class ColorTransformNull;
+class QCMSColorTransform;
 
-class ColorTransformInternal : public ColorTransform {
+class ColorTransformStep {
  public:
-  // Visitor pattern, Prepend() calls return prev->Join(this).
-  virtual bool Prepend(ColorTransformInternal* prev) = 0;
+  ColorTransformStep() {}
+  virtual ~ColorTransformStep() {}
+  virtual ColorTransformFromLinear* GetFromLinear() { return nullptr; }
+  virtual ColorTransformToBT2020CL* GetToBT2020CL() { return nullptr; }
+  virtual ColorTransformFromBT2020CL* GetFromBT2020CL() { return nullptr; }
+  virtual ColorTransformMatrix* GetMatrix() { return nullptr; }
+  virtual ColorTransformNull* GetNull() { return nullptr; }
+  virtual QCMSColorTransform* GetQCMS() { return nullptr; }
 
   // Join methods, returns true if the |next| transform was successfully
   // assimilated into |this|.
   // If Join() returns true, |next| is no longer needed and can be deleted.
-  virtual bool Join(const ColorTransformToLinear& next) { return false; }
-  virtual bool Join(const ColorTransformFromLinear& next) { return false; }
-  virtual bool Join(const ColorTransformToBT2020CL& next) { return false; }
-  virtual bool Join(const ColorTransformFromBT2020CL& next) { return false; }
-  virtual bool Join(const ColorTransformMatrix& next) { return false; }
-  virtual bool Join(const ColorTransformNull& next) { return true; }
+  virtual bool Join(ColorTransformStep* next) { return false; }
 
   // Return true if this is a null transform.
   virtual bool IsNull() { return false; }
+
+  virtual void Transform(ColorTransform::TriStim* color, size_t num) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ColorTransformStep);
 };
 
-class ColorTransformNull : public ColorTransformInternal {
+class ColorTransformInternal : public ColorTransform {
  public:
-  bool Prepend(ColorTransformInternal* prev) override {
-    return prev->Join(*this);
+  ColorTransformInternal(const ColorSpace& from,
+                         const ColorSpace& to,
+                         Intent intent);
+  ~ColorTransformInternal() override;
+
+  // Perform transformation of colors, |colors| is both input and output.
+  void Transform(TriStim* colors, size_t num) override {
+    for (const auto& step : steps_)
+      step->Transform(colors, num);
   }
+  size_t NumberOfStepsForTesting() const override { return steps_.size(); }
+
+ private:
+  void AppendColorSpaceToColorSpaceTransform(ColorSpace from,
+                                             const ColorSpace& to,
+                                             ColorTransform::Intent intent);
+  void Simplify();
+
+  std::list<std::unique_ptr<ColorTransformStep>> steps_;
+};
+
+class ColorTransformNull : public ColorTransformStep {
+ public:
+  ColorTransformNull* GetNull() override { return this; }
   bool IsNull() override { return true; }
-  void transform(ColorTransform::TriStim* color, size_t num) override {}
+  void Transform(ColorTransform::TriStim* color, size_t num) override {}
 };
 
-class ColorTransformMatrix : public ColorTransformInternal {
+class ColorTransformMatrix : public ColorTransformStep {
  public:
-  explicit ColorTransformMatrix(const Transform& matrix) : matrix_(matrix) {}
-
-  bool Prepend(ColorTransformInternal* prev) override {
-    return prev->Join(*this);
-  }
-
-  bool Join(const ColorTransformMatrix& next) override {
-    Transform tmp = next.matrix_;
+  explicit ColorTransformMatrix(const class Transform& matrix)
+      : matrix_(matrix) {}
+  ColorTransformMatrix* GetMatrix() override { return this; }
+  bool Join(ColorTransformStep* next_untyped) override {
+    ColorTransformMatrix* next = next_untyped->GetMatrix();
+    if (!next)
+      return false;
+    class Transform tmp = next->matrix_;
     tmp *= matrix_;
     matrix_ = tmp;
     return true;
   }
 
   bool IsNull() override {
-    // Returns true if we're very close to an identity matrix.
-    for (int i = 0; i < 4; i++) {
-      for (int j = 0; j < 4; j++) {
-        float expected = i == j ? 1.0f : 0.0f;
-        if (fabs(matrix_.matrix().get(i, j) - expected) > 0.00001f) {
-          return false;
-        }
-      }
-    }
-    return true;
+    return SkMatrixIsApproximatelyIdentity(matrix_.matrix());
   }
 
-  void transform(ColorTransform::TriStim* colors, size_t num) override {
+  void Transform(ColorTransform::TriStim* colors, size_t num) override {
     for (size_t i = 0; i < num; i++)
       matrix_.TransformPoint(colors + i);
   }
 
  private:
-  Transform matrix_;
+  class Transform matrix_;
 };
 
-class ColorTransformFromLinear : public ColorTransformInternal {
+class ColorTransformFromLinear : public ColorTransformStep {
  public:
   explicit ColorTransformFromLinear(ColorSpace::TransferID transfer,
                                     const SkColorSpaceTransferFn& fn,
@@ -285,13 +324,9 @@
     if (transfer_ == ColorSpace::TransferID::LINEAR_HDR)
       transfer_ = ColorSpace::TransferID::LINEAR;
   }
-  bool Prepend(ColorTransformInternal* prev) override {
-    return prev->Join(*this);
-  }
-
+  ColorTransformFromLinear* GetFromLinear() override { return this; }
   bool IsNull() override { return transfer_ == ColorSpace::TransferID::LINEAR; }
-
-  void transform(ColorTransform::TriStim* colors, size_t num) override {
+  void Transform(ColorTransform::TriStim* colors, size_t num) override {
     if (fn_valid_) {
       for (size_t i = 0; i < num; i++) {
         colors[i].set_x(EvalSkTransferFn(fn_, colors[i].x()));
@@ -314,7 +349,7 @@
   bool fn_valid_ = false;
 };
 
-class ColorTransformToLinear : public ColorTransformInternal {
+class ColorTransformToLinear : public ColorTransformStep {
  public:
   explicit ColorTransformToLinear(ColorSpace::TransferID transfer,
                                   const SkColorSpaceTransferFn& fn,
@@ -324,10 +359,6 @@
       transfer_ = ColorSpace::TransferID::LINEAR;
   }
 
-  bool Prepend(ColorTransformInternal* prev) override {
-    return prev->Join(*this);
-  }
-
   static bool IsGamma22(ColorSpace::TransferID transfer) {
     switch (transfer) {
       // We don't need to check BT709 here because it's been translated into
@@ -341,9 +372,15 @@
     }
   }
 
-  bool Join(const ColorTransformFromLinear& next) override {
-    if (transfer_ == next.transfer_ ||
-        (IsGamma22(transfer_) && IsGamma22(next.transfer_))) {
+  bool Join(ColorTransformStep* next_untyped) override {
+    ColorTransformFromLinear* next = next_untyped->GetFromLinear();
+    if (!next)
+      return false;
+    // TODO(ccameron): Use SkTransferFnsApproximatelyCancel and
+    // SkTransferFnIsApproximatelyIdentity to merge parametric transfer
+    // functions.
+    if (transfer_ == next->transfer_ ||
+        (IsGamma22(transfer_) && IsGamma22(next->transfer_))) {
       transfer_ = ColorSpace::TransferID::LINEAR;
       return true;
     }
@@ -358,7 +395,7 @@
   }
 
   ColorTransform::TriStim ClipToWhite(ColorTransform::TriStim& c) {
-    float maximum = std::max(std::max(c.x(), c.y()), c.z());
+    float maximum = max(max(c.x(), c.y()), c.z());
     if (maximum > 1.0f) {
       float l = Luma(c);
       c.Scale(1.0f / maximum);
@@ -370,7 +407,7 @@
     return c;
   }
 
-  void transform(ColorTransform::TriStim* colors, size_t num) override {
+  void Transform(ColorTransform::TriStim* colors, size_t num) override {
     if (fn_valid_) {
       for (size_t i = 0; i < num; i++) {
         colors[i].set_x(EvalSkTransferFn(fn_, colors[i].x()));
@@ -420,13 +457,12 @@
 // Then we run the transfer function like normal, and finally
 // this class is inserted as an extra step which takes calculates
 // the U and V values.
-class ColorTransformToBT2020CL : public ColorTransformInternal {
+class ColorTransformToBT2020CL : public ColorTransformStep {
  public:
-  bool Prepend(ColorTransformInternal* prev) override {
-    return prev->Join(*this);
-  }
-
-  bool Join(const ColorTransformFromBT2020CL& next) override {
+  bool Join(ColorTransformStep* next_untyped) override {
+    ColorTransformFromBT2020CL* next = next_untyped->GetFromBT2020CL();
+    if (!next)
+      return false;
     if (null_)
       return false;
     null_ = true;
@@ -435,7 +471,7 @@
 
   bool IsNull() override { return null_; }
 
-  void transform(ColorTransform::TriStim* RYB, size_t num) override {
+  void Transform(ColorTransform::TriStim* RYB, size_t num) override {
     for (size_t i = 0; i < num; i++) {
       float U, V;
       float B_Y = RYB[i].z() - RYB[i].y();
@@ -459,13 +495,12 @@
 };
 
 // Inverse of ColorTransformToBT2020CL, see comment above for more info.
-class ColorTransformFromBT2020CL : public ColorTransformInternal {
+class ColorTransformFromBT2020CL : public ColorTransformStep {
  public:
-  bool Prepend(ColorTransformInternal* prev) override {
-    return prev->Join(*this);
-  }
-
-  bool Join(const ColorTransformToBT2020CL& next) override {
+  bool Join(ColorTransformStep* next_untyped) override {
+    ColorTransformToBT2020CL* next = next_untyped->GetToBT2020CL();
+    if (!next)
+      return false;
     if (null_)
       return false;
     null_ = true;
@@ -474,7 +509,7 @@
 
   bool IsNull() override { return null_; }
 
-  void transform(ColorTransform::TriStim* YUV, size_t num) override {
+  void Transform(ColorTransform::TriStim* YUV, size_t num) override {
     if (null_)
       return;
     for (size_t i = 0; i < num; i++) {
@@ -501,188 +536,133 @@
   bool null_ = false;
 };
 
-class ChainColorTransform : public ColorTransform {
- public:
-  ChainColorTransform(std::unique_ptr<ColorTransform> a,
-                      std::unique_ptr<ColorTransform> b)
-      : a_(std::move(a)), b_(std::move(b)) {}
-
- private:
-  void transform(TriStim* colors, size_t num) override {
-    a_->transform(colors, num);
-    b_->transform(colors, num);
-  }
-  std::unique_ptr<ColorTransform> a_;
-  std::unique_ptr<ColorTransform> b_;
-};
-
-class TransformBuilder {
- public:
-  void Append(std::unique_ptr<ColorTransformInternal> transform) {
-    if (!disable_optimizations_ && transform->IsNull())
-      return;  // Null transform
-    transforms_.push_back(std::move(transform));
-    if (disable_optimizations_)
-      return;
-    while (transforms_.size() >= 2 &&
-           transforms_.back()->Prepend(
-               transforms_[transforms_.size() - 2].get())) {
-      transforms_.pop_back();
-      if (transforms_.back()->IsNull()) {
-        transforms_.pop_back();
+void ColorTransformInternal::AppendColorSpaceToColorSpaceTransform(
+    ColorSpace from,
+    const ColorSpace& to,
+    ColorTransform::Intent intent) {
+  if (intent == ColorTransform::Intent::INTENT_PERCEPTUAL) {
+    switch (from.transfer_) {
+      case ColorSpace::TransferID::UNSPECIFIED:
+      case ColorSpace::TransferID::BT709:
+      case ColorSpace::TransferID::SMPTE170M:
+        // SMPTE 1886 suggests that we should be using gamma 2.4 for BT709
+        // content. However, most displays actually use a gamma of 2.2, and
+        // user studies shows that users don't really care. Using the same
+        // gamma as the display will let us optimize a lot more, so lets stick
+        // with using the SRGB transfer function.
+        from.transfer_ = ColorSpace::TransferID::IEC61966_2_1;
         break;
-      }
+
+      case ColorSpace::TransferID::SMPTEST2084:
+        if (!to.IsHDR()) {
+          // We don't have an HDR display, so replace SMPTE 2084 with
+          // something that returns ranges more or less suitable for a normal
+          // display.
+          from.transfer_ = ColorSpace::TransferID::SMPTEST2084_NON_HDR;
+        }
+        break;
+
+      case ColorSpace::TransferID::ARIB_STD_B67:
+        if (!to.IsHDR()) {
+          // Interpreting HLG using a gamma 2.4 works reasonably well for SDR
+          // displays.
+          from.transfer_ = ColorSpace::TransferID::GAMMA24;
+        }
+        break;
+
+      default:  // Do nothing
+        break;
     }
+
+    // TODO(hubbe): shrink gamuts here (never stretch gamuts)
   }
 
-  std::unique_ptr<ColorTransform> GetTransform() {
-    if (transforms_.empty())
-      return base::MakeUnique<ColorTransformNull>();
-    std::unique_ptr<ColorTransform> ret(std::move(transforms_.back()));
-    transforms_.pop_back();
+  steps_.push_back(
+      base::MakeUnique<ColorTransformMatrix>(GetRangeAdjustMatrix(from)));
 
-    while (!transforms_.empty()) {
-      ret = std::unique_ptr<ColorTransform>(new ChainColorTransform(
-          std::move(transforms_.back()), std::move(ret)));
-      transforms_.pop_back();
-    }
+  steps_.push_back(
+      base::MakeUnique<ColorTransformMatrix>(Invert(GetTransferMatrix(from))));
 
-    return ret;
+  SkColorSpaceTransferFn to_linear_fn;
+  bool to_linear_fn_valid = from.GetTransferFunction(&to_linear_fn);
+  steps_.push_back(base::MakeUnique<ColorTransformToLinear>(
+      from.transfer_, to_linear_fn, to_linear_fn_valid));
+
+  if (from.matrix_ == ColorSpace::MatrixID::BT2020_CL) {
+    // BT2020 CL is a special case.
+    steps_.push_back(base::MakeUnique<ColorTransformFromBT2020CL>());
+  }
+  steps_.push_back(
+      base::MakeUnique<ColorTransformMatrix>(GetPrimaryTransform(from)));
+
+  steps_.push_back(
+      base::MakeUnique<ColorTransformMatrix>(Invert(GetPrimaryTransform(to))));
+  if (to.matrix_ == ColorSpace::MatrixID::BT2020_CL) {
+    // BT2020 CL is a special case.
+    steps_.push_back(base::MakeUnique<ColorTransformToBT2020CL>());
   }
 
-  void disable_optimizations() { disable_optimizations_ = true; }
+  SkColorSpaceTransferFn from_linear_fn;
+  bool from_linear_fn_valid = to.GetInverseTransferFunction(&from_linear_fn);
+  steps_.push_back(base::MakeUnique<ColorTransformFromLinear>(
+      to.transfer_, from_linear_fn, from_linear_fn_valid));
 
- private:
-  bool disable_optimizations_ = false;
-  std::vector<std::unique_ptr<ColorTransformInternal>> transforms_;
-};
+  steps_.push_back(
+      base::MakeUnique<ColorTransformMatrix>(GetTransferMatrix(to)));
 
-class ColorSpaceToColorSpaceTransform {
- public:
-  static Transform GetPrimaryTransform(const ColorSpace& c) {
-    SkMatrix44 sk_matrix;
-    c.GetPrimaryMatrix(&sk_matrix);
-    return Transform(sk_matrix);
-  }
+  steps_.push_back(
+      base::MakeUnique<ColorTransformMatrix>(Invert(GetRangeAdjustMatrix(to))));
+}
 
-  static void ColorSpaceToColorSpace(ColorSpace from,
-                                     ColorSpace to,
-                                     ColorTransform::Intent intent,
-                                     TransformBuilder* builder) {
-    if (intent == ColorTransform::Intent::INTENT_PERCEPTUAL) {
-      switch (from.transfer_) {
-        case ColorSpace::TransferID::UNSPECIFIED:
-        case ColorSpace::TransferID::BT709:
-        case ColorSpace::TransferID::SMPTE170M:
-          // SMPTE 1886 suggests that we should be using gamma 2.4 for BT709
-          // content. However, most displays actually use a gamma of 2.2, and
-          // user studies shows that users don't really care. Using the same
-          // gamma as the display will let us optimize a lot more, so lets stick
-          // with using the SRGB transfer function.
-          from.transfer_ = ColorSpace::TransferID::IEC61966_2_1;
-          break;
-
-        case ColorSpace::TransferID::SMPTEST2084:
-          if (!to.IsHDR()) {
-            // We don't have an HDR display, so replace SMPTE 2084 with
-            // something that returns ranges more or less suitable for a normal
-            // display.
-            from.transfer_ = ColorSpace::TransferID::SMPTEST2084_NON_HDR;
-          }
-          break;
-
-        case ColorSpace::TransferID::ARIB_STD_B67:
-          if (!to.IsHDR()) {
-            // Interpreting HLG using a gamma 2.4 works reasonably well for SDR
-            // displays.
-            from.transfer_ = ColorSpace::TransferID::GAMMA24;
-          }
-          break;
-
-        default:  // Do nothing
-          break;
-      }
-
-      // TODO(hubbe): shrink gamuts here (never stretch gamuts)
-    }
-
-    builder->Append(base::MakeUnique<ColorTransformMatrix>(
-        GetRangeAdjustMatrix(from)));
-
-    builder->Append(base::MakeUnique<ColorTransformMatrix>(
-        Invert(GetTransferMatrix(from))));
-
-    SkColorSpaceTransferFn to_linear_fn;
-    bool to_linear_fn_valid = from.GetTransferFunction(&to_linear_fn);
-    builder->Append(base::MakeUnique<ColorTransformToLinear>(
-        from.transfer_, to_linear_fn, to_linear_fn_valid));
-
-    if (from.matrix_ == ColorSpace::MatrixID::BT2020_CL) {
-      // BT2020 CL is a special case.
-      builder->Append(base::MakeUnique<ColorTransformFromBT2020CL>());
-    }
-    builder->Append(
-        base::MakeUnique<ColorTransformMatrix>(GetPrimaryTransform(from)));
-
-    builder->Append(base::MakeUnique<ColorTransformMatrix>(
-        Invert(GetPrimaryTransform(to))));
-    if (to.matrix_ == ColorSpace::MatrixID::BT2020_CL) {
-      // BT2020 CL is a special case.
-      builder->Append(base::MakeUnique<ColorTransformToBT2020CL>());
-    }
-
-    SkColorSpaceTransferFn from_linear_fn;
-    bool from_linear_fn_valid = to.GetInverseTransferFunction(&from_linear_fn);
-    builder->Append(base::MakeUnique<ColorTransformFromLinear>(
-        to.transfer_, from_linear_fn, from_linear_fn_valid));
-
-    builder->Append(
-        base::MakeUnique<ColorTransformMatrix>(GetTransferMatrix(to)));
-
-    builder->Append(base::MakeUnique<ColorTransformMatrix>(
-        Invert(GetRangeAdjustMatrix(to))));
-  }
-};
-
-class QCMSColorTransform : public ColorTransformInternal {
+class QCMSColorTransform : public ColorTransformStep {
  public:
   // Takes ownership of the profiles
-  QCMSColorTransform(qcms_profile* from, qcms_profile* to)
-      : from_(from), to_(to) {}
-  ~QCMSColorTransform() override {
-    qcms_profile_release(from_);
-    qcms_profile_release(to_);
-  }
-  bool Prepend(ColorTransformInternal* prev) override {
-    // Not currently optimizable.
+  QCMSColorTransform(ScopedQcmsProfile from, ScopedQcmsProfile to)
+      : from_(std::move(from)), to_(std::move(to)) {}
+  ~QCMSColorTransform() override {}
+  QCMSColorTransform* GetQCMS() override { return this; }
+  bool Join(ColorTransformStep* next_untyped) override {
+    QCMSColorTransform* next = next_untyped->GetQCMS();
+    if (!next)
+      return false;
+    if (qcms_profile_match(to_.get(), next->from_.get())) {
+      to_ = std::move(next->to_);
+      return true;
+    }
     return false;
   }
-  bool IsNull() override { return from_ == to_; }
-  void transform(TriStim* colors, size_t num) override {
-    CHECK(sizeof(TriStim) == sizeof(float[3]));
+  bool IsNull() override {
+    if (qcms_profile_match(from_.get(), to_.get()))
+      return true;
+    return false;
+  }
+  void Transform(ColorTransform::TriStim* colors, size_t num) override {
+    CHECK(sizeof(ColorTransform::TriStim) == sizeof(float[3]));
     // QCMS doesn't like numbers outside 0..1
     for (size_t i = 0; i < num; i++) {
-      colors[i].set_x(fmin(1.0f, fmax(0.0f, colors[i].x())));
-      colors[i].set_y(fmin(1.0f, fmax(0.0f, colors[i].y())));
-      colors[i].set_z(fmin(1.0f, fmax(0.0f, colors[i].z())));
+      colors[i].set_x(min(1.0f, max(0.0f, colors[i].x())));
+      colors[i].set_y(min(1.0f, max(0.0f, colors[i].y())));
+      colors[i].set_z(min(1.0f, max(0.0f, colors[i].z())));
     }
-    qcms_chain_transform(from_, to_, reinterpret_cast<float*>(colors),
+    qcms_chain_transform(from_.get(), to_.get(),
+                         reinterpret_cast<float*>(colors),
                          reinterpret_cast<float*>(colors), num * 3);
   }
 
  private:
-  qcms_profile *from_, *to_;
+  ScopedQcmsProfile from_;
+  ScopedQcmsProfile to_;
 };
 
-qcms_profile* GetQCMSProfileIfAvailable(const ColorSpace& color_space) {
+ScopedQcmsProfile GetQCMSProfileIfAvailable(const ColorSpace& color_space) {
   ICCProfile icc_profile = ICCProfile::FromColorSpace(color_space);
   if (icc_profile.GetData().empty())
     return nullptr;
-  return qcms_profile_from_memory(icc_profile.GetData().data(),
-                                  icc_profile.GetData().size());
+  return ScopedQcmsProfile(qcms_profile_from_memory(
+      icc_profile.GetData().data(), icc_profile.GetData().size()));
 }
 
-qcms_profile* GetXYZD50Profile() {
+ScopedQcmsProfile GetXYZD50Profile() {
   // QCMS is trixy, it has a datatype called qcms_CIE_xyY, but what it expects
   // is in fact not xyY color coordinates, it just wants the x/y values of the
   // primaries with Y equal to 1.0.
@@ -700,67 +680,78 @@
   w.x = 0.34567f;
   w.y = 0.35850f;
   w.Y = 1.0f;
-  return qcms_profile_create_rgb_with_gamma(w, xyz, 1.0f);
+  return ScopedQcmsProfile(qcms_profile_create_rgb_with_gamma(w, xyz, 1.0f));
 }
 
+ColorTransformInternal::ColorTransformInternal(const ColorSpace& from,
+                                               const ColorSpace& to,
+                                               Intent intent) {
+  ScopedQcmsProfile from_profile = GetQCMSProfileIfAvailable(from);
+  ScopedQcmsProfile to_profile = GetQCMSProfileIfAvailable(to);
+  bool has_from_profile = !!from_profile;
+  bool has_to_profile = !!to_profile;
+
+  if (from_profile) {
+    steps_.push_back(base::MakeUnique<QCMSColorTransform>(
+        std::move(from_profile), GetXYZD50Profile()));
+  }
+
+  AppendColorSpaceToColorSpaceTransform(
+      has_from_profile ? ColorSpace::CreateXYZD50() : from,
+      has_to_profile ? ColorSpace::CreateXYZD50() : to, intent);
+
+  if (to_profile) {
+    steps_.push_back(base::MakeUnique<QCMSColorTransform>(
+        GetXYZD50Profile(), std::move(to_profile)));
+  }
+
+  if (intent != Intent::TEST_NO_OPT)
+    Simplify();
+}
+
+ColorTransformInternal::~ColorTransformInternal() {}
+
+void ColorTransformInternal::Simplify() {
+  for (auto iter = steps_.begin(); iter != steps_.end();) {
+    std::unique_ptr<ColorTransformStep>& this_step = *iter;
+
+    // Try to Join |next_step| into |this_step|. If successful, re-visit the
+    // step before |this_step|.
+    auto iter_next = iter;
+    iter_next++;
+    if (iter_next != steps_.end()) {
+      std::unique_ptr<ColorTransformStep>& next_step = *iter_next;
+      if (this_step->Join(next_step.get())) {
+        steps_.erase(iter_next);
+        if (iter != steps_.begin())
+          --iter;
+        continue;
+      }
+    }
+
+    // If |this_step| step is a no-op, remove it, and re-visit the step before
+    // |this_step|.
+    if (this_step->IsNull()) {
+      iter = steps_.erase(iter);
+      if (iter != steps_.begin())
+        --iter;
+      continue;
+    }
+
+    ++iter;
+  }
+}
+
+// static
 std::unique_ptr<ColorTransform> ColorTransform::NewColorTransform(
     const ColorSpace& from,
     const ColorSpace& to,
     Intent intent) {
-  TransformBuilder builder;
-  if (intent == Intent::TEST_NO_OPT) {
-    builder.disable_optimizations();
-  }
-
-  qcms_profile* from_profile = GetQCMSProfileIfAvailable(from);
-  qcms_profile* to_profile = GetQCMSProfileIfAvailable(to);
-
-  if (from_profile && to_profile) {
-    return std::unique_ptr<ColorTransform>(
-        new QCMSColorTransform(from_profile, to_profile));
-  }
-  if (from_profile) {
-    builder.Append(std::unique_ptr<ColorTransformInternal>(
-        new QCMSColorTransform(from_profile, GetXYZD50Profile())));
-  }
-  ColorSpaceToColorSpaceTransform::ColorSpaceToColorSpace(
-      from_profile ? ColorSpace::CreateXYZD50() : from,
-      to_profile ? ColorSpace::CreateXYZD50() : to, intent, &builder);
-  if (to_profile) {
-    builder.Append(std::unique_ptr<ColorTransformInternal>(
-        new QCMSColorTransform(GetXYZD50Profile(), to_profile)));
-  }
-
-  return builder.GetTransform();
+  return std::unique_ptr<ColorTransform>(
+      new ColorTransformInternal(from, to, intent));
 }
 
-// static
-float ColorTransform::ToLinearForTesting(ColorSpace::TransferID transfer,
-                                         float v) {
-  ColorSpace space(ColorSpace::PrimaryID::BT709, transfer,
-                   ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL);
-  SkColorSpaceTransferFn to_linear_fn;
-  bool to_linear_fn_valid = space.GetTransferFunction(&to_linear_fn);
-  ColorTransformToLinear to_linear_transform(transfer, to_linear_fn,
-                                             to_linear_fn_valid);
-  TriStim color(v, v, v);
-  to_linear_transform.transform(&color, 1);
-  return color.x();
-}
-
-// static
-float ColorTransform::FromLinearForTesting(ColorSpace::TransferID transfer,
-                                           float v) {
-  ColorSpace space(ColorSpace::PrimaryID::BT709, transfer,
-                   ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL);
-  SkColorSpaceTransferFn from_linear_fn;
-  bool from_linear_fn_valid = space.GetInverseTransferFunction(&from_linear_fn);
-
-  ColorTransformFromLinear from_linear_transform(transfer, from_linear_fn,
-                                                 from_linear_fn_valid);
-  TriStim color(v, v, v);
-  from_linear_transform.transform(&color, 1);
-  return color.x();
-}
+ColorTransform::ColorTransform() {}
+ColorTransform::~ColorTransform() {}
 
 }  // namespace gfx
diff --git a/ui/gfx/color_transform.h b/ui/gfx/color_transform.h
index 1d99617..5368b7a 100644
--- a/ui/gfx/color_transform.h
+++ b/ui/gfx/color_transform.h
@@ -5,10 +5,7 @@
 #ifndef UI_GFX_COLOR_TRANSFORM_H_
 #define UI_GFX_COLOR_TRANSFORM_H_
 
-#include <memory>
-#include <stdint.h>
-
-#include "build/build_config.h"
+#include "base/macros.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/point3_f.h"
 #include "ui/gfx/gfx_export.h"
@@ -23,19 +20,23 @@
   // Channel order is XYZ, RGB or YUV.
   typedef Point3F TriStim;
 
-  virtual ~ColorTransform() {}
+  ColorTransform();
+  virtual ~ColorTransform();
 
   // Perform transformation of colors, |colors| is both input and output.
-  virtual void transform(TriStim* colors, size_t num) = 0;
+  virtual void Transform(TriStim* colors, size_t num) = 0;
+
+  virtual size_t NumberOfStepsForTesting() const = 0;
 
   static std::unique_ptr<ColorTransform> NewColorTransform(
       const ColorSpace& from,
       const ColorSpace& to,
       Intent intent);
 
-  static float ToLinearForTesting(ColorSpace::TransferID id, float v);
-  static float FromLinearForTesting(ColorSpace::TransferID id, float v);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ColorTransform);
 };
+
 }  // namespace gfx
 
 #endif  // UI_GFX_COLOR_TRANSFORM_H_
diff --git a/ui/gfx/color_transform_fuzzer.cc b/ui/gfx/color_transform_fuzzer.cc
index bc226149..8a769fd 100644
--- a/ui/gfx/color_transform_fuzzer.cc
+++ b/ui/gfx/color_transform_fuzzer.cc
@@ -19,6 +19,6 @@
       bt709, icc.GetColorSpace(),
       gfx::ColorTransform::Intent::INTENT_ABSOLUTE));
   gfx::ColorTransform::TriStim tmp(16.0f / 255.0f, 0.5f, 0.5f);
-  t->transform(&tmp, 1);
+  t->Transform(&tmp, 1);
   return 0;
 }
diff --git a/ui/gfx/color_transform_unittest.cc b/ui/gfx/color_transform_unittest.cc
index 3fc43c2..5a888267 100644
--- a/ui/gfx/color_transform_unittest.cc
+++ b/ui/gfx/color_transform_unittest.cc
@@ -66,24 +66,71 @@
       bt709, sRGB, ColorTransform::Intent::INTENT_ABSOLUTE));
 
   ColorTransform::TriStim tmp(16.0f / 255.0f, 0.5f, 0.5f);
-  t->transform(&tmp, 1);
+  t->Transform(&tmp, 1);
   EXPECT_NEAR(tmp.x(), 0.0f, 0.001f);
   EXPECT_NEAR(tmp.y(), 0.0f, 0.001f);
   EXPECT_NEAR(tmp.z(), 0.0f, 0.001f);
 
   tmp = ColorTransform::TriStim(235.0f / 255.0f, 0.5f, 0.5f);
-  t->transform(&tmp, 1);
+  t->Transform(&tmp, 1);
   EXPECT_NEAR(tmp.x(), 1.0f, 0.001f);
   EXPECT_NEAR(tmp.y(), 1.0f, 0.001f);
   EXPECT_NEAR(tmp.z(), 1.0f, 0.001f);
 
   // Test a blue color
   tmp = ColorTransform::TriStim(128.0f / 255.0f, 240.0f / 255.0f, 0.5f);
-  t->transform(&tmp, 1);
+  t->Transform(&tmp, 1);
   EXPECT_GT(tmp.z(), tmp.x());
   EXPECT_GT(tmp.z(), tmp.y());
 }
 
+TEST(SimpleColorSpace, SRGBFromICCAndNotICC) {
+  float kEpsilon = 0.001f;
+  ColorTransform::TriStim value_fromicc;
+  ColorTransform::TriStim value_default;
+
+  ICCProfile srgb_icc_profile = ICCProfileForTestingSRGB();
+  ColorSpace srgb_fromicc = srgb_icc_profile.GetColorSpace();
+  ColorSpace srgb_default = gfx::ColorSpace::CreateSRGB();
+  ColorSpace xyzd50 = gfx::ColorSpace::CreateXYZD50();
+
+  value_fromicc = value_default = ColorTransform::TriStim(0.1f, 0.5f, 0.9f);
+
+  std::unique_ptr<ColorTransform> toxyzd50_fromicc(
+      ColorTransform::NewColorTransform(
+          srgb_fromicc, xyzd50, ColorTransform::Intent::INTENT_ABSOLUTE));
+  // This will have 1 step, namely, the QCMS transform.
+  EXPECT_EQ(toxyzd50_fromicc->NumberOfStepsForTesting(), 1u);
+  toxyzd50_fromicc->Transform(&value_fromicc, 1);
+
+  std::unique_ptr<ColorTransform> toxyzd50_default(
+      ColorTransform::NewColorTransform(
+          srgb_default, xyzd50, ColorTransform::Intent::INTENT_ABSOLUTE));
+  // This will have a transfer function and then linear transform.
+  EXPECT_EQ(toxyzd50_default->NumberOfStepsForTesting(), 2u);
+  toxyzd50_default->Transform(&value_default, 1);
+
+  EXPECT_NEAR(value_fromicc.x(), value_default.x(), kEpsilon);
+  EXPECT_NEAR(value_fromicc.y(), value_default.y(), kEpsilon);
+  EXPECT_NEAR(value_fromicc.z(), value_default.z(), kEpsilon);
+
+  value_fromicc = value_default = ColorTransform::TriStim(0.1f, 0.5f, 0.9f);
+
+  std::unique_ptr<ColorTransform> fromxyzd50_fromicc(
+      ColorTransform::NewColorTransform(
+          xyzd50, srgb_fromicc, ColorTransform::Intent::INTENT_ABSOLUTE));
+  fromxyzd50_fromicc->Transform(&value_fromicc, 1);
+
+  std::unique_ptr<ColorTransform> fromxyzd50_default(
+      ColorTransform::NewColorTransform(
+          xyzd50, srgb_default, ColorTransform::Intent::INTENT_ABSOLUTE));
+  fromxyzd50_default->Transform(&value_default, 1);
+
+  EXPECT_NEAR(value_fromicc.x(), value_default.x(), kEpsilon);
+  EXPECT_NEAR(value_fromicc.y(), value_default.y(), kEpsilon);
+  EXPECT_NEAR(value_fromicc.z(), value_default.z(), kEpsilon);
+}
+
 TEST(SimpleColorSpace, BT709toSRGBICC) {
   ICCProfile srgb_icc = ICCProfileForTestingSRGB();
   ColorSpace bt709 = ColorSpace::CreateREC709();
@@ -92,20 +139,20 @@
       bt709, sRGB, ColorTransform::Intent::INTENT_ABSOLUTE));
 
   ColorTransform::TriStim tmp(16.0f / 255.0f, 0.5f, 0.5f);
-  t->transform(&tmp, 1);
+  t->Transform(&tmp, 1);
   EXPECT_NEAR(tmp.x(), 0.0f, 0.001f);
   EXPECT_NEAR(tmp.y(), 0.0f, 0.001f);
   EXPECT_NEAR(tmp.z(), 0.0f, 0.001f);
 
   tmp = ColorTransform::TriStim(235.0f / 255.0f, 0.5f, 0.5f);
-  t->transform(&tmp, 1);
+  t->Transform(&tmp, 1);
   EXPECT_NEAR(tmp.x(), 1.0f, 0.001f);
   EXPECT_NEAR(tmp.y(), 1.0f, 0.001f);
   EXPECT_NEAR(tmp.z(), 1.0f, 0.001f);
 
   // Test a blue color
   tmp = ColorTransform::TriStim(128.0f / 255.0f, 240.0f / 255.0f, 0.5f);
-  t->transform(&tmp, 1);
+  t->Transform(&tmp, 1);
   EXPECT_GT(tmp.z(), tmp.x());
   EXPECT_GT(tmp.z(), tmp.y());
 }
@@ -123,25 +170,25 @@
       sRGB, sRGB2, ColorTransform::Intent::INTENT_ABSOLUTE));
 
   ColorTransform::TriStim tmp(1.0f, 1.0f, 1.0f);
-  t->transform(&tmp, 1);
+  t->Transform(&tmp, 1);
   EXPECT_NEAR(tmp.x(), 1.0f, kEpsilon);
   EXPECT_NEAR(tmp.y(), 1.0f, kEpsilon);
   EXPECT_NEAR(tmp.z(), 1.0f, kEpsilon);
 
   tmp = ColorTransform::TriStim(1.0f, 0.0f, 0.0f);
-  t->transform(&tmp, 1);
+  t->Transform(&tmp, 1);
   EXPECT_NEAR(tmp.x(), 1.0f, kEpsilon);
   EXPECT_NEAR(tmp.y(), 0.0f, kEpsilon);
   EXPECT_NEAR(tmp.z(), 0.0f, kEpsilon);
 
   tmp = ColorTransform::TriStim(0.0f, 1.0f, 0.0f);
-  t->transform(&tmp, 1);
+  t->Transform(&tmp, 1);
   EXPECT_NEAR(tmp.x(), 0.0f, kEpsilon);
   EXPECT_NEAR(tmp.y(), 1.0f, kEpsilon);
   EXPECT_NEAR(tmp.z(), 0.0f, kEpsilon);
 
   tmp = ColorTransform::TriStim(0.0f, 0.0f, 1.0f);
-  t->transform(&tmp, 1);
+  t->Transform(&tmp, 1);
   EXPECT_NEAR(tmp.x(), 0.0f, kEpsilon);
   EXPECT_NEAR(tmp.y(), 0.0f, kEpsilon);
   EXPECT_NEAR(tmp.z(), 1.0f, kEpsilon);
@@ -154,20 +201,20 @@
       unknown, sRGB, ColorTransform::Intent::INTENT_PERCEPTUAL));
 
   ColorTransform::TriStim tmp(16.0f / 255.0f, 0.5f, 0.5f);
-  t->transform(&tmp, 1);
+  t->Transform(&tmp, 1);
   EXPECT_NEAR(tmp.x(), 0.0f, 0.001f);
   EXPECT_NEAR(tmp.y(), 0.0f, 0.001f);
   EXPECT_NEAR(tmp.z(), 0.0f, 0.001f);
 
   tmp = ColorTransform::TriStim(235.0f / 255.0f, 0.5f, 0.5f);
-  t->transform(&tmp, 1);
+  t->Transform(&tmp, 1);
   EXPECT_NEAR(tmp.x(), 1.0f, 0.001f);
   EXPECT_NEAR(tmp.y(), 1.0f, 0.001f);
   EXPECT_NEAR(tmp.z(), 1.0f, 0.001f);
 
   // Test a blue color
   tmp = ColorTransform::TriStim(128.0f / 255.0f, 240.0f / 255.0f, 0.5f);
-  t->transform(&tmp, 1);
+  t->Transform(&tmp, 1);
   EXPECT_GT(tmp.z(), tmp.x());
   EXPECT_GT(tmp.z(), tmp.y());
 }
@@ -175,10 +222,33 @@
 class TransferTest : public testing::TestWithParam<ColorSpace::TransferID> {};
 
 TEST_P(TransferTest, basicTest) {
+  gfx::ColorSpace space_with_transfer(ColorSpace::PrimaryID::BT709, GetParam(),
+                                      ColorSpace::MatrixID::RGB,
+                                      ColorSpace::RangeID::FULL);
+  gfx::ColorSpace space_linear(
+      ColorSpace::PrimaryID::BT709, ColorSpace::TransferID::LINEAR,
+      ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL);
+
+  std::unique_ptr<ColorTransform> to_linear(ColorTransform::NewColorTransform(
+      space_with_transfer, space_linear,
+      ColorTransform::Intent::INTENT_ABSOLUTE));
+
+  std::unique_ptr<ColorTransform> from_linear(ColorTransform::NewColorTransform(
+      space_linear, space_with_transfer,
+      ColorTransform::Intent::INTENT_ABSOLUTE));
+
+  // The transforms will ahve 1 or 0 steps (0 for linear).
+  size_t expected_steps = 1u;
+  if (GetParam() == ColorSpace::TransferID::LINEAR)
+    expected_steps = 0u;
+  EXPECT_EQ(to_linear->NumberOfStepsForTesting(), expected_steps);
+  EXPECT_EQ(from_linear->NumberOfStepsForTesting(), expected_steps);
+
   for (float x = 0.0f; x <= 1.0f; x += 1.0f / 128.0f) {
-    float linear = ColorTransform::ToLinearForTesting(GetParam(), x);
-    float x2 = ColorTransform::FromLinearForTesting(GetParam(), linear);
-    EXPECT_NEAR(x, x2, 0.001f);
+    ColorTransform::TriStim tristim(x, x, x);
+    to_linear->Transform(&tristim, 1);
+    from_linear->Transform(&tristim, 1);
+    EXPECT_NEAR(x, tristim.x(), 0.001f);
   }
 }
 
@@ -211,7 +281,7 @@
   std::unique_ptr<ColorTransform> t(
       ColorTransform::NewColorTransform(color_space_, color_space_, intent_));
   ColorTransform::TriStim tristim(0.4f, 0.5f, 0.6f);
-  t->transform(&tristim, 1);
+  t->Transform(&tristim, 1);
   EXPECT_NEAR(tristim.x(), 0.4f, 0.001f);
   EXPECT_NEAR(tristim.y(), 0.5f, 0.001f);
   EXPECT_NEAR(tristim.z(), 0.6f, 0.001f);
@@ -223,8 +293,8 @@
   std::unique_ptr<ColorTransform> t2(ColorTransform::NewColorTransform(
       ColorSpace::CreateXYZD50(), color_space_, intent_));
   ColorTransform::TriStim tristim(0.4f, 0.5f, 0.6f);
-  t1->transform(&tristim, 1);
-  t2->transform(&tristim, 1);
+  t1->Transform(&tristim, 1);
+  t2->Transform(&tristim, 1);
   EXPECT_NEAR(tristim.x(), 0.4f, 0.001f);
   EXPECT_NEAR(tristim.y(), 0.5f, 0.001f);
   EXPECT_NEAR(tristim.z(), 0.6f, 0.001f);
diff --git a/ui/gfx/skia_color_space_util.cc b/ui/gfx/skia_color_space_util.cc
new file mode 100644
index 0000000..60617e76
--- /dev/null
+++ b/ui/gfx/skia_color_space_util.cc
@@ -0,0 +1,54 @@
+// 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/gfx/skia_color_space_util.h"
+
+#include <algorithm>
+#include <cmath>
+
+namespace gfx {
+
+namespace {
+
+const float kEpsilon = 1.f / 256.f;
+}
+
+float EvalSkTransferFn(const SkColorSpaceTransferFn& fn, float x) {
+  if (x < 0.f)
+    return 0.f;
+  if (x < fn.fD)
+    return fn.fC * x + fn.fF;
+  return std::pow(fn.fA * x + fn.fB, fn.fG) + fn.fE;
+}
+
+SkColorSpaceTransferFn SkTransferFnInverse(const SkColorSpaceTransferFn& fn) {
+  SkColorSpaceTransferFn fn_inv = {0};
+  if (fn.fA > 0 && fn.fG > 0) {
+    double a_to_the_g = std::pow(fn.fA, fn.fG);
+    fn_inv.fA = 1.f / a_to_the_g;
+    fn_inv.fB = -fn.fE / a_to_the_g;
+    fn_inv.fG = 1.f / fn.fG;
+  }
+  fn_inv.fD = fn.fC * fn.fD + fn.fF;
+  fn_inv.fE = -fn.fB / fn.fA;
+  if (fn.fC != 0) {
+    fn_inv.fC = 1.f / fn.fC;
+    fn_inv.fF = -fn.fF / fn.fC;
+  }
+  return fn_inv;
+}
+
+bool SkMatrixIsApproximatelyIdentity(const SkMatrix44& m) {
+  for (int i = 0; i < 4; ++i) {
+    for (int j = 0; j < 4; ++j) {
+      float identity_value = i == j ? 1 : 0;
+      float value = m.get(i, j);
+      if (std::abs(identity_value - value) > kEpsilon)
+        return false;
+    }
+  }
+  return true;
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/skia_color_space_util.h b/ui/gfx/skia_color_space_util.h
new file mode 100644
index 0000000..29dbb00b
--- /dev/null
+++ b/ui/gfx/skia_color_space_util.h
@@ -0,0 +1,22 @@
+// 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 UI_GFX_SKIA_COLOR_SPACE_UTIL_H_
+#define UI_GFX_SKIA_COLOR_SPACE_UTIL_H_
+
+#include "third_party/skia/include/core/SkColorSpace.h"
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+
+float GFX_EXPORT EvalSkTransferFn(const SkColorSpaceTransferFn& fn, float x);
+
+SkColorSpaceTransferFn GFX_EXPORT
+SkTransferFnInverse(const SkColorSpaceTransferFn& fn);
+
+bool GFX_EXPORT SkMatrixIsApproximatelyIdentity(const SkMatrix44& m);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_SKIA_COLOR_SPACE_UTIL_H_