diff --git a/DEPS b/DEPS
index c6f930a..1296d791 100644
--- a/DEPS
+++ b/DEPS
@@ -43,7 +43,7 @@
   # 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': '7dc32c84c10ce2592e3e346a6deb312810cc8751',
+  'v8_revision': '7135a7399d0cc195b02b2ea8c4315a5727a8c849',
   # 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.
@@ -131,7 +131,7 @@
    Var('chromium_git') + '/external/colorama.git' + '@' + '799604a1041e9b3bc5d2789ecbd7e8db2e18e6b8',
 
   'src/third_party/icu':
-   Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '8f91ea3a7e0413df3312204058da856058a8099b',
+   Var('chromium_git') + '/chromium/deps/icu.git' + '@' + 'e466f6ac8f60bb9697af4a91c6911c6fc4aec95f',
 
   'src/third_party/libexif/sources':
    Var('chromium_git') + '/chromium/deps/libexif/sources.git' + '@' + '045b7fb9aa6d9b7f1954db248caf5eefe917476d',
@@ -275,7 +275,7 @@
 
   'src/third_party/catapult':
     Var('chromium_git') + '/external/github.com/catapult-project/catapult.git' + '@' +
-    'cb78598b63fc3b6b4767a6b81f68ef7d05a27c3d',
+    'dd5b026ac253edbcdb3be08a12eef1d03b796b35',
 
   'src/third_party/openh264/src':
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + 'b37cda248234162033e3e11b0335f3131cdfe488',
diff --git a/chrome/browser/safe_browsing/local_database_manager.cc b/chrome/browser/safe_browsing/local_database_manager.cc
index 38395ff..6a02287 100644
--- a/chrome/browser/safe_browsing/local_database_manager.cc
+++ b/chrome/browser/safe_browsing/local_database_manager.cc
@@ -34,6 +34,7 @@
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing_db/util.h"
+#include "components/safe_browsing_db/v4_get_hash_protocol_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
 #include "url/url_constants.h"
@@ -270,8 +271,16 @@
 }
 
 LocalSafeBrowsingDatabaseManager::LocalSafeBrowsingDatabaseManager(
-    const scoped_refptr<SafeBrowsingService>& service)
-    : sb_service_(service),
+    const scoped_refptr<SafeBrowsingService>& service) :
+    LocalSafeBrowsingDatabaseManager(service, NULL, V4GetHashProtocolConfig()) {
+}
+
+LocalSafeBrowsingDatabaseManager::LocalSafeBrowsingDatabaseManager(
+    const scoped_refptr<SafeBrowsingService>& service,
+    net::URLRequestContextGetter* request_context_getter,
+    const V4GetHashProtocolConfig& config)
+    : SafeBrowsingDatabaseManager(request_context_getter, config),
+      sb_service_(service),
       database_(NULL),
       enabled_(false),
       enable_download_protection_(false),
diff --git a/chrome/browser/safe_browsing/local_database_manager.h b/chrome/browser/safe_browsing/local_database_manager.h
index c1635034..6c543ad 100644
--- a/chrome/browser/safe_browsing/local_database_manager.h
+++ b/chrome/browser/safe_browsing/local_database_manager.h
@@ -41,6 +41,7 @@
 class SafeBrowsingDatabase;
 class ClientSideDetectionService;
 class DownloadProtectionService;
+struct V4GetHashProtocolConfig;
 
 // Implemetation that manages a local database on disk.
 //
@@ -100,10 +101,16 @@
     DISALLOW_COPY_AND_ASSIGN(SafeBrowsingCheck);
   };
 
-  // Creates the safe browsing service.  Need to initialize before using.
+  // Use this constructor for testing only.
   explicit LocalSafeBrowsingDatabaseManager(
       const scoped_refptr<SafeBrowsingService>& service);
 
+  // Creates the safe browsing service.  Need to initialize before using.
+  LocalSafeBrowsingDatabaseManager(
+      const scoped_refptr<SafeBrowsingService>& service,
+      net::URLRequestContextGetter* request_context_getter,
+      const V4GetHashProtocolConfig& config);
+
   //
   // SafeBrowsingDatabaseManager overrides
   //
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc
index 67f3f2fe..18b315a 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service.cc
@@ -31,6 +31,7 @@
 #include "chrome/browser/safe_browsing/download_protection_service.h"
 #include "chrome/browser/safe_browsing/ping_manager.h"
 #include "chrome/browser/safe_browsing/protocol_manager.h"
+#include "chrome/browser/safe_browsing/protocol_manager_helper.h"
 #include "chrome/browser/safe_browsing/ui_manager.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_paths.h"
@@ -40,10 +41,12 @@
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing_db/database_manager.h"
+#include "components/safe_browsing_db/v4_get_hash_protocol_manager.h"
 #include "components/user_prefs/tracked/tracked_preference_validation_delegate.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/cookie_store_factory.h"
 #include "content/public/browser/notification_service.h"
+#include "google_apis/google_api_keys.h"
 #include "net/cookies/cookie_store.h"
 #include "net/extras/sqlite/cookie_crypto_delegate.h"
 #include "net/url_request/url_request_context.h"
@@ -399,10 +402,11 @@
 }
 
 SafeBrowsingDatabaseManager* SafeBrowsingService::CreateDatabaseManager() {
+  V4GetHashProtocolConfig config = GetV4GetHashProtocolConfig();
 #if defined(SAFE_BROWSING_DB_LOCAL)
-  return new LocalSafeBrowsingDatabaseManager(this);
+  return new LocalSafeBrowsingDatabaseManager(this, NULL, config);
 #elif defined(SAFE_BROWSING_DB_REMOTE)
-  return new RemoteSafeBrowsingDatabaseManager();
+  return new RemoteSafeBrowsingDatabaseManager(NULL, config);
 #else
   return NULL;
 #endif
@@ -428,27 +432,8 @@
 
 SafeBrowsingProtocolConfig SafeBrowsingService::GetProtocolConfig() const {
   SafeBrowsingProtocolConfig config;
-  // On Windows, get the safe browsing client name from the browser
-  // distribution classes in installer util. These classes don't yet have
-  // an analog on non-Windows builds so just keep the name specified here.
-#if defined(OS_WIN)
-  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
-  config.client_name = dist->GetSafeBrowsingName();
-#else
-#if defined(GOOGLE_CHROME_BUILD)
-  config.client_name = "googlechrome";
-#else
-  config.client_name = "chromium";
-#endif
+  config.client_name = GetProtocolConfigClientName();
 
-  // Mark client string to allow server to differentiate mobile.
-#if defined(OS_ANDROID)
-  config.client_name.append("-a");
-#elif defined(OS_IOS)
-  config.client_name.append("-i");
-#endif
-
-#endif  // defined(OS_WIN)
   base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
   config.disable_auto_update =
       cmdline->HasSwitch(switches::kSbDisableAutoUpdate) ||
@@ -461,6 +446,43 @@
   return config;
 }
 
+V4GetHashProtocolConfig
+SafeBrowsingService::GetV4GetHashProtocolConfig() const {
+  V4GetHashProtocolConfig config;
+  config.client_name = GetProtocolConfigClientName();
+  config.version = SafeBrowsingProtocolManagerHelper::Version();
+  config.key_param = google_apis::GetAPIKey();;
+
+  return config;
+}
+
+std::string SafeBrowsingService::GetProtocolConfigClientName() const {
+  std::string client_name;
+  // On Windows, get the safe browsing client name from the browser
+  // distribution classes in installer util. These classes don't yet have
+  // an analog on non-Windows builds so just keep the name specified here.
+#if defined(OS_WIN)
+  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
+  client_name = dist->GetSafeBrowsingName();
+#else
+#if defined(GOOGLE_CHROME_BUILD)
+  client_name = "googlechrome";
+#else
+  client_name = "chromium";
+#endif
+
+  // Mark client string to allow server to differentiate mobile.
+#if defined(OS_ANDROID)
+  client_name.append("-a");
+#elif defined(OS_IOS)
+  client_name.append("-i");
+#endif
+
+#endif  // defined(OS_WIN)
+
+  return client_name;
+}
+
 // Any tests that create a DatabaseManager that isn't derived from
 // LocalSafeBrowsingDatabaseManager should override this to return NULL.
 SafeBrowsingProtocolManagerDelegate*
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.h b/chrome/browser/safe_browsing/safe_browsing_service.h
index ec8d59c..3c3435d 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.h
+++ b/chrome/browser/safe_browsing/safe_browsing_service.h
@@ -58,6 +58,7 @@
 class SafeBrowsingServiceFactory;
 class SafeBrowsingUIManager;
 class SafeBrowsingURLRequestContextGetter;
+struct V4GetHashProtocolConfig;
 
 #if defined(FULL_SAFE_BROWSING)
 class IncidentReportingService;
@@ -103,6 +104,12 @@
   // Create a protocol config struct.
   virtual SafeBrowsingProtocolConfig GetProtocolConfig() const;
 
+  // Create a v4 protocol config struct.
+  virtual V4GetHashProtocolConfig GetV4GetHashProtocolConfig() const;
+
+  // Returns the client_name field for both V3 and V4 protocol manager configs.
+  std::string GetProtocolConfigClientName() const;
+
   // Get current enabled status. Must be called on IO thread.
   bool enabled() const {
     DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
diff --git a/components/exo/surface.cc b/components/exo/surface.cc
index 5b209b30..6de8be6 100644
--- a/components/exo/surface.cc
+++ b/components/exo/surface.cc
@@ -162,7 +162,8 @@
 }
 
 void Surface::Attach(Buffer* buffer) {
-  TRACE_EVENT1("exo", "Surface::Attach", "buffer", buffer->AsTracedValue());
+  TRACE_EVENT1("exo", "Surface::Attach", "buffer",
+               buffer ? buffer->GetSize().ToString() : "null");
 
   has_pending_contents_ = true;
   pending_buffer_ = buffer ? buffer->AsWeakPtr() : base::WeakPtr<Buffer>();
diff --git a/components/safe_browsing_db/BUILD.gn b/components/safe_browsing_db/BUILD.gn
index ad32884..5a58fe2 100644
--- a/components/safe_browsing_db/BUILD.gn
+++ b/components/safe_browsing_db/BUILD.gn
@@ -49,9 +49,12 @@
   ]
   deps = [
     ":hit_report",
+    ":proto",
     ":util",
+    ":v4_get_hash_protocol_manager",
     "//base:base",
     "//content/public/common",
+    "//net",
     "//url:url",
   ]
 }
@@ -100,7 +103,9 @@
   ]
   deps = [
     ":database_manager",
+    ":proto",
     ":safe_browsing_api_handler",
+    ":v4_get_hash_protocol_manager",
     "//base:base",
     "//components/variations",
     "//content/public/browser",
diff --git a/components/safe_browsing_db/database_manager.cc b/components/safe_browsing_db/database_manager.cc
index b818722dd..6412cf0a 100644
--- a/components/safe_browsing_db/database_manager.cc
+++ b/components/safe_browsing_db/database_manager.cc
@@ -4,10 +4,29 @@
 
 #include "components/safe_browsing_db/database_manager.h"
 
+#include "components/safe_browsing_db/v4_get_hash_protocol_manager.h"
+#include "net/url_request/url_request_context_getter.h"
 #include "url/gurl.h"
 
 namespace safe_browsing {
 
+SafeBrowsingDatabaseManager::SafeBrowsingDatabaseManager()
+    : SafeBrowsingDatabaseManager(NULL, V4GetHashProtocolConfig()) {
+}
+
+SafeBrowsingDatabaseManager::SafeBrowsingDatabaseManager(
+    net::URLRequestContextGetter* request_context_getter,
+    const V4GetHashProtocolConfig& config) {
+  // Instantiate a V4GetHashProtocolManager.
+  if (request_context_getter) {
+    v4_get_hash_protocol_manager_.reset(V4GetHashProtocolManager::Create(
+        request_context_getter, config));
+  }
+}
+
+SafeBrowsingDatabaseManager::~SafeBrowsingDatabaseManager() {
+}
+
 void SafeBrowsingDatabaseManager::CheckApiBlacklistUrl(const GURL& url,
                                                        Client* client) {
   // TODO(kcarattini): Implement this.
diff --git a/components/safe_browsing_db/database_manager.h b/components/safe_browsing_db/database_manager.h
index fd8a276f..6ee8cbd 100644
--- a/components/safe_browsing_db/database_manager.h
+++ b/components/safe_browsing_db/database_manager.h
@@ -20,8 +20,15 @@
 #include "content/public/common/resource_type.h"
 #include "url/gurl.h"
 
+namespace net {
+class URLRequestContextGetter;
+}  // namespace net
+
 namespace safe_browsing {
 
+struct V4GetHashProtocolConfig;
+class V4GetHashProtocolManager;
+
 // Base class to either the locally-managed or a remotely-managed database.
 class SafeBrowsingDatabaseManager
     : public base::RefCountedThreadSafe<SafeBrowsingDatabaseManager> {
@@ -162,9 +169,19 @@
   virtual void StopOnIOThread(bool shutdown) = 0;
 
  protected:
-  virtual ~SafeBrowsingDatabaseManager() {}
+  // Use this constructor for testing only.
+  SafeBrowsingDatabaseManager();
+
+  // Constructs the database manager.
+  SafeBrowsingDatabaseManager(
+      net::URLRequestContextGetter* request_context_getter,
+      const V4GetHashProtocolConfig& config);
+
+  virtual ~SafeBrowsingDatabaseManager();
 
   friend class base::RefCountedThreadSafe<SafeBrowsingDatabaseManager>;
+
+  std::unique_ptr<V4GetHashProtocolManager> v4_get_hash_protocol_manager_;
 };  // class SafeBrowsingDatabaseManager
 
 }  // namespace safe_browsing
diff --git a/components/safe_browsing_db/remote_database_manager.cc b/components/safe_browsing_db/remote_database_manager.cc
index e699b305..adcb0be 100644
--- a/components/safe_browsing_db/remote_database_manager.cc
+++ b/components/safe_browsing_db/remote_database_manager.cc
@@ -11,6 +11,7 @@
 #include "base/strings/string_split.h"
 #include "base/timer/elapsed_timer.h"
 #include "components/safe_browsing_db/safe_browsing_api_handler.h"
+#include "components/safe_browsing_db/v4_get_hash_protocol_manager.h"
 #include "components/variations/variations_associated_data.h"
 #include "content/public/browser/browser_thread.h"
 
@@ -93,7 +94,14 @@
 
 // TODO(nparker): Add more tests for this class
 RemoteSafeBrowsingDatabaseManager::RemoteSafeBrowsingDatabaseManager()
-    : enabled_(false) {
+    : RemoteSafeBrowsingDatabaseManager(NULL, V4GetHashProtocolConfig()) {
+}
+
+RemoteSafeBrowsingDatabaseManager::RemoteSafeBrowsingDatabaseManager(
+      net::URLRequestContextGetter* request_context_getter,
+      const V4GetHashProtocolConfig& config)
+    : SafeBrowsingDatabaseManager(request_context_getter, config),
+      enabled_(false) {
   // Decide which resource types to check. These two are the minimum.
   resource_types_to_check_.insert(content::RESOURCE_TYPE_MAIN_FRAME);
   resource_types_to_check_.insert(content::RESOURCE_TYPE_SUB_FRAME);
diff --git a/components/safe_browsing_db/remote_database_manager.h b/components/safe_browsing_db/remote_database_manager.h
index 6b2c764..143902c 100644
--- a/components/safe_browsing_db/remote_database_manager.h
+++ b/components/safe_browsing_db/remote_database_manager.h
@@ -18,15 +18,26 @@
 #include "components/safe_browsing_db/database_manager.h"
 #include "url/gurl.h"
 
+namespace net {
+class URLRequestContextGetter;
+}
+
 namespace safe_browsing {
 
+struct V4GetHashProtocolConfig;
+
 // An implementation that proxies requests to a service outside of Chromium.
 // Does not manage a local database.
 class RemoteSafeBrowsingDatabaseManager : public SafeBrowsingDatabaseManager {
  public:
+  // Use this constructor for testing only.
+  RemoteSafeBrowsingDatabaseManager();
+
   // Construct RemoteSafeBrowsingDatabaseManager.
   // Must be initialized by calling StartOnIOThread() before using.
-  RemoteSafeBrowsingDatabaseManager();
+  RemoteSafeBrowsingDatabaseManager(
+      net::URLRequestContextGetter* request_context_getter,
+      const V4GetHashProtocolConfig& config);
 
   //
   // SafeBrowsingDatabaseManager implementation
diff --git a/content/renderer/mojo/service_registry_js_wrapper.cc b/content/renderer/mojo/service_registry_js_wrapper.cc
index e45decc3..14f4dc3 100644
--- a/content/renderer/mojo/service_registry_js_wrapper.cc
+++ b/content/renderer/mojo/service_registry_js_wrapper.cc
@@ -10,33 +10,10 @@
 #include "content/common/mojo/service_registry_impl.h"
 #include "content/public/common/service_registry.h"
 #include "mojo/edk/js/handle.h"
-#include "v8/include/v8.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 
 namespace content {
 
-namespace {
-
-struct JsFactoryDeleter {
-  inline void operator()(v8::Persistent<v8::Function>* ptr) const {
-    ptr->Reset();
-    delete ptr;
-  }
-};
-
-using ScopedJsFactory =
-    scoped_ptr<v8::Persistent<v8::Function>, JsFactoryDeleter>;
-
-void CallJsFactory(ScopedJsFactory factory,
-                   mojo::ScopedMessagePipeHandle pipe) {
-  v8::Isolate* isolate = v8::Isolate::GetCurrent();
-  v8::Local<v8::Value> argv[] = {
-    gin::ConvertToV8(isolate, mojo::Handle(pipe.release().value()))
-  };
-  factory->Get(isolate)->Call(v8::Undefined(isolate), 1, argv);
-}
-
-}  // namespace
-
 gin::WrapperInfo ServiceRegistryJsWrapper::kWrapperInfo = {
     gin::kEmbedderNativeGin};
 const char ServiceRegistryJsWrapper::kModuleName[] =
@@ -48,10 +25,12 @@
 // static
 gin::Handle<ServiceRegistryJsWrapper> ServiceRegistryJsWrapper::Create(
     v8::Isolate* isolate,
+    v8::Handle<v8::Context> context,
     ServiceRegistry* service_registry) {
   return gin::CreateHandle(
       isolate,
       new ServiceRegistryJsWrapper(
+          isolate, context,
           static_cast<ServiceRegistryImpl*>(service_registry)->GetWeakPtr()));
 }
 
@@ -80,16 +59,45 @@
       static_cast<ServiceRegistryImpl*>(service_registry_.get());
   if (!registry)
     return;
-  ScopedJsFactory factory(
-      new v8::Persistent<v8::Function>(v8::Isolate::GetCurrent(),
-                                       service_factory));
+  ScopedJsFactory factory(v8::Isolate::GetCurrent(), service_factory);
   registry->AddServiceOverrideForTesting(
-      service_name, base::Bind(&CallJsFactory, base::Passed(&factory)));
+      service_name, base::Bind(&ServiceRegistryJsWrapper::CallJsFactory,
+                               weak_factory_.GetWeakPtr(), factory));
 }
 
 ServiceRegistryJsWrapper::ServiceRegistryJsWrapper(
+    v8::Isolate* isolate,
+    v8::Handle<v8::Context> context,
     base::WeakPtr<ServiceRegistry> service_registry)
-    : service_registry_(service_registry) {
+    : isolate_(isolate),
+      context_(isolate, context),
+      service_registry_(service_registry),
+      weak_factory_(this) {
+  context_.SetWeak(this, &ServiceRegistryJsWrapper::ClearContext,
+                   v8::WeakCallbackType::kParameter);
+}
+
+void ServiceRegistryJsWrapper::CallJsFactory(
+    const ScopedJsFactory& factory,
+    mojo::ScopedMessagePipeHandle pipe) {
+  if (context_.IsEmpty())
+    return;
+
+  v8::HandleScope handle_scope(isolate_);
+  v8::Handle<v8::Context> context = context_.Get(isolate_);
+  v8::Context::Scope context_scope(context);
+  v8::Local<v8::Value> argv[] = {
+      gin::ConvertToV8(isolate_, mojo::Handle(pipe.release().value()))};
+  blink::WebLocalFrame::frameForContext(context)
+      ->callFunctionEvenIfScriptDisabled(factory.Get(isolate_),
+                                         v8::Undefined(isolate_), 1, argv);
+}
+
+// static
+void ServiceRegistryJsWrapper::ClearContext(
+    const v8::WeakCallbackInfo<ServiceRegistryJsWrapper>& data) {
+  ServiceRegistryJsWrapper* service_registry = data.GetParameter();
+  service_registry->context_.Reset();
 }
 
 }  // namespace content
diff --git a/content/renderer/mojo/service_registry_js_wrapper.h b/content/renderer/mojo/service_registry_js_wrapper.h
index 543d214..8fe8a3eb 100644
--- a/content/renderer/mojo/service_registry_js_wrapper.h
+++ b/content/renderer/mojo/service_registry_js_wrapper.h
@@ -12,11 +12,8 @@
 #include "gin/handle.h"
 #include "gin/object_template_builder.h"
 #include "gin/wrappable.h"
-#include "mojo/public/cpp/system/handle.h"
-
-namespace v8 {
-class Isolate;
-}
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "v8/include/v8.h"
 
 namespace content {
 
@@ -30,6 +27,7 @@
   ~ServiceRegistryJsWrapper() override;
   static gin::Handle<ServiceRegistryJsWrapper> Create(
       v8::Isolate* isolate,
+      v8::Handle<v8::Context> context,
       ServiceRegistry* service_registry);
 
   // gin::Wrappable<ServiceRegistryJsWrapper> overrides.
@@ -45,11 +43,25 @@
   static const char kModuleName[];
 
  private:
-  explicit ServiceRegistryJsWrapper(
-      base::WeakPtr<ServiceRegistry> service_registry);
+  using ScopedJsFactory =
+      v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function>>;
 
+  ServiceRegistryJsWrapper(v8::Isolate* isolate,
+                           v8::Handle<v8::Context> context,
+                           base::WeakPtr<ServiceRegistry> service_registry);
+
+  void CallJsFactory(const ScopedJsFactory& factory,
+                     mojo::ScopedMessagePipeHandle pipe);
+
+  static void ClearContext(
+      const v8::WeakCallbackInfo<ServiceRegistryJsWrapper>& data);
+
+  v8::Isolate* isolate_;
+  v8::Global<v8::Context> context_;
   base::WeakPtr<ServiceRegistry> service_registry_;
 
+  base::WeakPtrFactory<ServiceRegistryJsWrapper> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(ServiceRegistryJsWrapper);
 };
 
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 1ef1619..a906c38 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2382,9 +2382,9 @@
   registry->AddBuiltinModule(isolate, mojo::edk::js::Support::kModuleName,
                              mojo::edk::js::Support::GetModule(isolate));
   registry->AddBuiltinModule(
-      isolate,
-      ServiceRegistryJsWrapper::kModuleName,
-      ServiceRegistryJsWrapper::Create(isolate, &service_registry_).ToV8());
+      isolate, ServiceRegistryJsWrapper::kModuleName,
+      ServiceRegistryJsWrapper::Create(isolate, context, &service_registry_)
+          .ToV8());
 }
 
 void RenderFrameImpl::AddMessageToConsole(ConsoleMessageLevel level,
diff --git a/ipc/unix_domain_socket_util.cc b/ipc/unix_domain_socket_util.cc
index 86367998..6e06fe7 100644
--- a/ipc/unix_domain_socket_util.cc
+++ b/ipc/unix_domain_socket_util.cc
@@ -24,37 +24,24 @@
 
 namespace {
 
-// Returns fd (>= 0) on success, -1 on failure. If successful, fills in
+// Returns true on success, false otherwise. If successful, fills in
 // |unix_addr| with the appropriate data for the socket, and sets
 // |unix_addr_len| to the length of the data therein.
-int MakeUnixAddrForPath(const std::string& socket_name,
-                        struct sockaddr_un* unix_addr,
-                        size_t* unix_addr_len) {
+bool MakeUnixAddrForPath(const std::string& socket_name,
+                         struct sockaddr_un* unix_addr,
+                         size_t* unix_addr_len) {
   DCHECK(unix_addr);
   DCHECK(unix_addr_len);
 
   if (socket_name.length() == 0) {
     LOG(ERROR) << "Empty socket name provided for unix socket address.";
-    return -1;
+    return false;
   }
   // We reject socket_name.length() == kMaxSocketNameLength to make room for
   // the NUL terminator at the end of the string.
   if (socket_name.length() >= kMaxSocketNameLength) {
     LOG(ERROR) << "Socket name too long: " << socket_name;
-    return -1;
-  }
-
-  // Create socket.
-  base::ScopedFD fd(socket(AF_UNIX, SOCK_STREAM, 0));
-  if (!fd.is_valid()) {
-    PLOG(ERROR) << "socket";
-    return -1;
-  }
-
-  // Make socket non-blocking
-  if (!base::SetNonBlocking(fd.get())) {
-    PLOG(ERROR) << "base::SetNonBlocking() failed " << fd.get();
-    return -1;
+    return false;
   }
 
   // Create unix_addr structure.
@@ -63,7 +50,25 @@
   strncpy(unix_addr->sun_path, socket_name.c_str(), kMaxSocketNameLength);
   *unix_addr_len =
       offsetof(struct sockaddr_un, sun_path) + socket_name.length();
-  return fd.release();
+  return true;
+}
+
+// Returns a valid socket on success.
+base::ScopedFD CreateUnixDomainSocket() {
+  // Create socket.
+  base::ScopedFD fd(socket(AF_UNIX, SOCK_STREAM, 0));
+  if (!fd.is_valid()) {
+    PLOG(ERROR) << "Failed to create AF_UNIX socket.";
+    return base::ScopedFD();
+  }
+
+  // Make socket non-blocking.
+  if (!base::SetNonBlocking(fd.get())) {
+    PLOG(ERROR) << "base::SetNonBlocking() failed " << fd.get();
+    return base::ScopedFD();
+  }
+
+  return fd;
 }
 
 bool IsRecoverableError() {
@@ -80,8 +85,9 @@
   const std::string socket_name = socket_path.value();
   struct sockaddr_un unix_addr;
   size_t unix_addr_len;
-  base::ScopedFD fd(
-      MakeUnixAddrForPath(socket_name, &unix_addr, &unix_addr_len));
+  if (!MakeUnixAddrForPath(socket_name, &unix_addr, &unix_addr_len))
+    return false;
+  base::ScopedFD fd(CreateUnixDomainSocket());
   if (!fd.is_valid())
     return false;
 
@@ -122,8 +128,9 @@
 
   struct sockaddr_un unix_addr;
   size_t unix_addr_len;
-  base::ScopedFD fd(
-      MakeUnixAddrForPath(socket_path.value(), &unix_addr, &unix_addr_len));
+  if (!MakeUnixAddrForPath(socket_path.value(), &unix_addr, &unix_addr_len))
+    return false;
+  base::ScopedFD fd(CreateUnixDomainSocket());
   if (!fd.is_valid())
     return false;
 
diff --git a/mojo/edk/embedder/embedder.cc b/mojo/edk/embedder/embedder.cc
index 1feadda..62702af 100644
--- a/mojo/edk/embedder/embedder.cc
+++ b/mojo/edk/embedder/embedder.cc
@@ -87,6 +87,15 @@
       shared_memory_handle, num_bytes, read_only, mojo_wrapper_handle);
 }
 
+MojoResult PassSharedMemoryHandle(
+    MojoHandle mojo_handle,
+    base::SharedMemoryHandle* shared_memory_handle,
+    size_t* num_bytes,
+    bool* read_only) {
+  return internal::g_core->PassSharedMemoryHandle(
+      mojo_handle, shared_memory_handle, num_bytes, read_only);
+}
+
 void InitIPCSupport(ProcessDelegate* process_delegate,
                     scoped_refptr<base::TaskRunner> io_thread_task_runner) {
   CHECK(internal::g_core);
diff --git a/mojo/edk/embedder/embedder.h b/mojo/edk/embedder/embedder.h
index e34ef5c..95b3b21 100644
--- a/mojo/edk/embedder/embedder.h
+++ b/mojo/edk/embedder/embedder.h
@@ -94,6 +94,21 @@
                           bool read_only,
                           MojoHandle* mojo_wrapper_handle);
 
+// Retrieves the underlying |SharedMemoryHandle| from a shared buffer
+// |MojoHandle| and closes the handle. If successful, |num_bytes| will contain
+// the size of the shared memory buffer and |read_only| will contain whether the
+// buffer handle is read-only. Both |num_bytes| and |read_only| may be null.
+// Note: The value of |shared_memory_handle| may be
+// base::SharedMemory::NULLHandle(), even if this function returns success.
+// Callers should perform appropriate checks.
+// TODO(crbug.com/556587): Support read-only handles. Currently, |read_only|
+// will always return |false|.
+MOJO_SYSTEM_IMPL_EXPORT MojoResult
+PassSharedMemoryHandle(MojoHandle mojo_handle,
+                       base::SharedMemoryHandle* shared_memory_handle,
+                       size_t* num_bytes,
+                       bool* read_only);
+
 // Initialialization/shutdown for interprocess communication (IPC) -------------
 
 // |InitIPCSupport()| sets up the subsystem for interprocess communication,
diff --git a/mojo/edk/embedder/embedder_unittest.cc b/mojo/edk/embedder/embedder_unittest.cc
index 1a36378..9b96c1f5 100644
--- a/mojo/edk/embedder/embedder_unittest.cc
+++ b/mojo/edk/embedder/embedder_unittest.cc
@@ -362,8 +362,19 @@
   memcpy(buffer, kByeWorld, sizeof(kByeWorld));
   WriteMessage(client_mp, "bye");
 
-  // 5. Close |sb1|.
-  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(sb1));
+  // 5. Extract the shared memory handle and ensure we can map it and read the
+  // contents.
+  base::SharedMemoryHandle shm_handle;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            PassSharedMemoryHandle(sb1, &shm_handle, nullptr, nullptr));
+  base::SharedMemory shared_memory(shm_handle, false);
+  ASSERT_TRUE(shared_memory.Map(123));
+  EXPECT_NE(buffer, shared_memory.memory());
+  EXPECT_EQ(kByeWorld, std::string(static_cast<char*>(shared_memory.memory())));
+
+  // 6. Close |sb1|. Should fail because |PassSharedMemoryHandle()| should have
+  // closed the handle.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(sb1));
 }
 
 // TODO(vtl): Test immediate write & close.
diff --git a/mojo/edk/embedder/platform_shared_buffer.cc b/mojo/edk/embedder/platform_shared_buffer.cc
index 9dce8d5..bbcf15d4 100644
--- a/mojo/edk/embedder/platform_shared_buffer.cc
+++ b/mojo/edk/embedder/platform_shared_buffer.cc
@@ -152,6 +152,13 @@
   return handle;
 }
 
+base::SharedMemoryHandle PlatformSharedBuffer::DuplicateSharedMemoryHandle() {
+  DCHECK(shared_memory_);
+
+  base::AutoLock locker(lock_);
+  return base::SharedMemory::DuplicateHandle(shared_memory_->handle());
+}
+
 PlatformSharedBuffer::PlatformSharedBuffer(size_t num_bytes)
     : num_bytes_(num_bytes) {}
 
diff --git a/mojo/edk/embedder/platform_shared_buffer.h b/mojo/edk/embedder/platform_shared_buffer.h
index 623fb9e3..5494122 100644
--- a/mojo/edk/embedder/platform_shared_buffer.h
+++ b/mojo/edk/embedder/platform_shared_buffer.h
@@ -74,6 +74,9 @@
   // TODO(vtl): On POSIX, we'll need two FDs to support sharing read-only.
   ScopedPlatformHandle DuplicatePlatformHandle();
 
+  // Duplicates the underlying shared memory handle and passes it to the caller.
+  base::SharedMemoryHandle DuplicateSharedMemoryHandle();
+
   // Passes the underlying platform handle to the caller. This should only be
   // called if there's a unique reference to this object (owned by the caller).
   // After calling this, this object should no longer be used, but should only
diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc
index 11892be2..69c918c 100644
--- a/mojo/edk/system/core.cc
+++ b/mojo/edk/system/core.cc
@@ -163,6 +163,48 @@
   return MOJO_RESULT_OK;
 }
 
+MojoResult Core::PassSharedMemoryHandle(
+    MojoHandle mojo_handle,
+    base::SharedMemoryHandle* shared_memory_handle,
+    size_t* num_bytes,
+    bool* read_only) {
+  if (!shared_memory_handle)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  scoped_refptr<Dispatcher> dispatcher;
+  MojoResult result = MOJO_RESULT_OK;
+  {
+    base::AutoLock lock(handles_lock_);
+    // Get the dispatcher and check it before removing it from the handle table
+    // to ensure that the dispatcher is of the correct type. This ensures we
+    // don't close and remove the wrong type of dispatcher.
+    dispatcher = handles_.GetDispatcher(mojo_handle);
+    if (!dispatcher || dispatcher->GetType() != Dispatcher::Type::SHARED_BUFFER)
+      return MOJO_RESULT_INVALID_ARGUMENT;
+
+    result = handles_.GetAndRemoveDispatcher(mojo_handle, &dispatcher);
+    if (result != MOJO_RESULT_OK)
+      return result;
+  }
+
+  SharedBufferDispatcher* shm_dispatcher =
+      static_cast<SharedBufferDispatcher*>(dispatcher.get());
+  scoped_refptr<PlatformSharedBuffer> platform_shared_buffer =
+      shm_dispatcher->PassPlatformSharedBuffer();
+
+  if (!platform_shared_buffer)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (num_bytes)
+    *num_bytes = platform_shared_buffer->GetNumBytes();
+  if (read_only)
+    *read_only = false;
+  *shared_memory_handle = platform_shared_buffer->DuplicateSharedMemoryHandle();
+
+  shm_dispatcher->Close();
+  return result;
+}
+
 void Core::RequestShutdown(const base::Closure& callback) {
   base::Closure on_shutdown;
   if (base::ThreadTaskRunnerHandle::IsSet()) {
diff --git a/mojo/edk/system/core.h b/mojo/edk/system/core.h
index 938726a..e2a22b0 100644
--- a/mojo/edk/system/core.h
+++ b/mojo/edk/system/core.h
@@ -88,6 +88,12 @@
       bool read_only,
       MojoHandle* mojo_wrapper_handle);
 
+  MojoResult PassSharedMemoryHandle(
+      MojoHandle mojo_handle,
+      base::SharedMemoryHandle* shared_memory_handle,
+      size_t* num_bytes,
+      bool* read_only);
+
   // Requests that the EDK tear itself down. |callback| will be called once
   // the shutdown process is complete. Note that |callback| is always called
   // asynchronously on the calling thread if said thread is running a message
diff --git a/mojo/edk/system/shared_buffer_dispatcher.cc b/mojo/edk/system/shared_buffer_dispatcher.cc
index 1484166..1805a92 100644
--- a/mojo/edk/system/shared_buffer_dispatcher.cc
+++ b/mojo/edk/system/shared_buffer_dispatcher.cc
@@ -139,13 +139,24 @@
   return CreateInternal(std::move(shared_buffer));
 }
 
+scoped_refptr<PlatformSharedBuffer>
+SharedBufferDispatcher::PassPlatformSharedBuffer() {
+  base::AutoLock lock(lock_);
+  if (!shared_buffer_ || in_transit_)
+    return nullptr;
+
+  scoped_refptr<PlatformSharedBuffer> retval = shared_buffer_;
+  shared_buffer_ = nullptr;
+  return retval;
+}
+
 Dispatcher::Type SharedBufferDispatcher::GetType() const {
   return Type::SHARED_BUFFER;
 }
 
 MojoResult SharedBufferDispatcher::Close() {
   base::AutoLock lock(lock_);
-  if (!shared_buffer_ || in_transit_)
+  if (in_transit_)
     return MOJO_RESULT_INVALID_ARGUMENT;
 
   shared_buffer_ = nullptr;
diff --git a/mojo/edk/system/shared_buffer_dispatcher.h b/mojo/edk/system/shared_buffer_dispatcher.h
index 011a43a..a8d5bcda 100644
--- a/mojo/edk/system/shared_buffer_dispatcher.h
+++ b/mojo/edk/system/shared_buffer_dispatcher.h
@@ -61,6 +61,10 @@
       PlatformHandle* platform_handles,
       size_t num_platform_handles);
 
+  // Passes the underlying platform shared buffer. This dispatcher must be
+  // closed after calling this function.
+  scoped_refptr<PlatformSharedBuffer> PassPlatformSharedBuffer();
+
   // Dispatcher:
   Type GetType() const override;
   MojoResult Close() override;
diff --git a/third_party/WebKit/LayoutTests/fast/gradients/css3-gradient-parsing-expected.txt b/third_party/WebKit/LayoutTests/fast/gradients/css3-gradient-parsing-expected.txt
index fd9b1cf..82406f37 100644
--- a/third_party/WebKit/LayoutTests/fast/gradients/css3-gradient-parsing-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/gradients/css3-gradient-parsing-expected.txt
@@ -27,18 +27,18 @@
 
 
 PASS testGradient("background-image: -webkit-radial-gradient(white, black)", "background-image") is "-webkit-radial-gradient(center, white, black)"
-PASS testGradient("background-image: -webkit-radial-gradient(bottom right, white, black)", "background-image") is "-webkit-radial-gradient(100% 100%, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(bottom right, white, black)", "background-image") is "-webkit-radial-gradient(right bottom, white, black)"
 PASS testGradient("background-image: -webkit-radial-gradient(cover, white, black)", "background-image") is "-webkit-radial-gradient(center, ellipse cover, white, black)"
 PASS testGradient("background-image: -webkit-radial-gradient(circle, white, black)", "background-image") is "-webkit-radial-gradient(center, circle cover, white, black)"
 PASS testGradient("background-image: -webkit-radial-gradient(circle contain, white, black)", "background-image") is "-webkit-radial-gradient(center, circle contain, white, black)"
-PASS testGradient("background-image: -webkit-radial-gradient(top, circle contain, white, black)", "background-image") is "-webkit-radial-gradient(50% 0%, circle contain, white, black)"
-PASS testGradient("background-image: -webkit-radial-gradient(top left, circle contain, white, black)", "background-image") is "-webkit-radial-gradient(0% 0%, circle contain, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(top, circle contain, white, black)", "background-image") is "-webkit-radial-gradient(center top, circle contain, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(top left, circle contain, white, black)", "background-image") is "-webkit-radial-gradient(left top, circle contain, white, black)"
 PASS testGradient("background-image: -webkit-radial-gradient(10px 20%, circle contain, white, black)", "background-image") is "-webkit-radial-gradient(10px 20%, circle contain, white, black)"
 PASS testGradient("background-image: -webkit-radial-gradient(10px, 20%, circle contain, white, black)", "background-image") is "none"
 PASS testGradient("background-image: -webkit-radial-gradient(circle 10px 20%, circle contain, white, black)", "background-image") is "none"
 PASS testGradient("background-image: -webkit-radial-gradient(circle 10px, circle contain, white, black)", "background-image") is "none"
 PASS testGradient("background-image: -webkit-radial-gradient(center, 10px, white, black)", "background-image") is "none"
-PASS testGradient("background-image: -webkit-radial-gradient(center, 10px 10px, white, black)", "background-image") is "-webkit-radial-gradient(50% 50%, 10px 10px, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(center, 10px 10px, white, black)", "background-image") is "-webkit-radial-gradient(center center, 10px 10px, white, black)"
 PASS testGradient("background-image: -webkit-radial-gradient(ellipse farthest-corner, white, black)", "background-image") is "-webkit-radial-gradient(center, ellipse farthest-corner, white, black)"
 PASS testGradient("background-image: -webkit-radial-gradient(circle closest-side, white, black)", "background-image") is "-webkit-radial-gradient(center, circle closest-side, white, black)"
 -webkit-repeating-radial-gradient
diff --git a/third_party/WebKit/LayoutTests/fast/gradients/css3-gradient-parsing.html b/third_party/WebKit/LayoutTests/fast/gradients/css3-gradient-parsing.html
index d89fa56..d13c8ca 100644
--- a/third_party/WebKit/LayoutTests/fast/gradients/css3-gradient-parsing.html
+++ b/third_party/WebKit/LayoutTests/fast/gradients/css3-gradient-parsing.html
@@ -53,18 +53,18 @@
 debug('<p>-webkit-radial-gradient</p>');
 
 shouldBe('testGradient("background-image: -webkit-radial-gradient(white, black)", "background-image")', '"-webkit-radial-gradient(center, white, black)"');
-shouldBe('testGradient("background-image: -webkit-radial-gradient(bottom right, white, black)", "background-image")', '"-webkit-radial-gradient(100% 100%, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(bottom right, white, black)", "background-image")', '"-webkit-radial-gradient(right bottom, white, black)"');
 shouldBe('testGradient("background-image: -webkit-radial-gradient(cover, white, black)", "background-image")', '"-webkit-radial-gradient(center, ellipse cover, white, black)"');
 shouldBe('testGradient("background-image: -webkit-radial-gradient(circle, white, black)", "background-image")', '"-webkit-radial-gradient(center, circle cover, white, black)"');
 shouldBe('testGradient("background-image: -webkit-radial-gradient(circle contain, white, black)", "background-image")', '"-webkit-radial-gradient(center, circle contain, white, black)"');
-shouldBe('testGradient("background-image: -webkit-radial-gradient(top, circle contain, white, black)", "background-image")', '"-webkit-radial-gradient(50% 0%, circle contain, white, black)"');
-shouldBe('testGradient("background-image: -webkit-radial-gradient(top left, circle contain, white, black)", "background-image")', '"-webkit-radial-gradient(0% 0%, circle contain, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(top, circle contain, white, black)", "background-image")', '"-webkit-radial-gradient(center top, circle contain, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(top left, circle contain, white, black)", "background-image")', '"-webkit-radial-gradient(left top, circle contain, white, black)"');
 shouldBe('testGradient("background-image: -webkit-radial-gradient(10px 20%, circle contain, white, black)", "background-image")', '"-webkit-radial-gradient(10px 20%, circle contain, white, black)"');
 shouldBe('testGradient("background-image: -webkit-radial-gradient(10px, 20%, circle contain, white, black)", "background-image")', '"none"');
 shouldBe('testGradient("background-image: -webkit-radial-gradient(circle 10px 20%, circle contain, white, black)", "background-image")', '"none"');
 shouldBe('testGradient("background-image: -webkit-radial-gradient(circle 10px, circle contain, white, black)", "background-image")', '"none"');
 shouldBe('testGradient("background-image: -webkit-radial-gradient(center, 10px, white, black)", "background-image")', '"none"');
-shouldBe('testGradient("background-image: -webkit-radial-gradient(center, 10px 10px, white, black)", "background-image")', '"-webkit-radial-gradient(50% 50%, 10px 10px, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(center, 10px 10px, white, black)", "background-image")', '"-webkit-radial-gradient(center center, 10px 10px, white, black)"');
 shouldBe('testGradient("background-image: -webkit-radial-gradient(ellipse farthest-corner, white, black)", "background-image")', '"-webkit-radial-gradient(center, ellipse farthest-corner, white, black)"');
 shouldBe('testGradient("background-image: -webkit-radial-gradient(circle closest-side, white, black)", "background-image")', '"-webkit-radial-gradient(center, circle closest-side, white, black)"');
 
diff --git a/third_party/WebKit/LayoutTests/fast/gradients/unprefixed-gradient-parsing-expected.txt b/third_party/WebKit/LayoutTests/fast/gradients/unprefixed-gradient-parsing-expected.txt
index 565771d..fe964bc 100644
--- a/third_party/WebKit/LayoutTests/fast/gradients/unprefixed-gradient-parsing-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/gradients/unprefixed-gradient-parsing-expected.txt
@@ -41,7 +41,7 @@
 
 
 PASS testGradient("background-image: radial-gradient(white, black)") is "radial-gradient(white, black)"
-PASS testGradient("background-image: radial-gradient(at bottom right, white, black)") is "radial-gradient(at 100% 100%, white, black)"
+PASS testGradient("background-image: radial-gradient(at bottom right, white, black)") is "radial-gradient(at right bottom, white, black)"
 PASS testGradient("background-image: radial-gradient(farthest-corner, white, black)") is "radial-gradient(white, black)"
 PASS testGradient("background-image: radial-gradient(farthest-corner, white, 35%, black)") is "radial-gradient(white, 35%, black)"
 PASS testGradient("background-image: radial-gradient(closest-side, white, black)") is "radial-gradient(closest-side, white, black)"
@@ -51,8 +51,8 @@
 PASS testGradient("background-image: radial-gradient(circle, white, black)") is "radial-gradient(circle, white, black)"
 PASS testGradient("background-image: radial-gradient(circle farthest-corner, white, black)") is "radial-gradient(circle, white, black)"
 PASS testGradient("background-image: radial-gradient(circle closest-side, white, black)") is "radial-gradient(circle closest-side, white, black)"
-PASS testGradient("background-image: radial-gradient(circle closest-side at top, white, black)") is "radial-gradient(circle closest-side at 50% 0%, white, black)"
-PASS testGradient("background-image: radial-gradient(circle closest-side at top left, white, black)") is "radial-gradient(circle closest-side at 0% 0%, white, black)"
+PASS testGradient("background-image: radial-gradient(circle closest-side at top, white, black)") is "radial-gradient(circle closest-side at center top, white, black)"
+PASS testGradient("background-image: radial-gradient(circle closest-side at top left, white, black)") is "radial-gradient(circle closest-side at left top, white, black)"
 PASS testGradient("background-image: radial-gradient(circle closest-side at 10px 20%, white, black)") is "radial-gradient(circle closest-side at 10px 20%, white, black)"
 PASS testGradient("background-image: radial-gradient(at 10px 20% circle closest-side, white, black)") is "none"
 PASS testGradient("background-image: radial-gradient(circle at 10px 20% circle, white, black)") is "none"
@@ -69,10 +69,10 @@
 PASS testGradient("background-image: radial-gradient(ellipse 10px, white, 25%, 75%, black)") is "none"
 PASS testGradient("background-image: radial-gradient(ellipse 10px 20px, white, black)") is "radial-gradient(10px 20px, white, black)"
 PASS testGradient("background-image: radial-gradient(circle 10px, white, black)") is "radial-gradient(10px, white, black)"
-PASS testGradient("background-image: radial-gradient(10px 10px at center, white, black)") is "radial-gradient(10px 10px at 50% 50%, white, black)"
-PASS testGradient("background-image: radial-gradient(10px 10px at center, white, 50%, black)") is "radial-gradient(10px 10px at 50% 50%, white, 50%, black)"
-PASS testGradient("background-image: radial-gradient(10px 10px at center, white, 0%, black)") is "radial-gradient(10px 10px at 50% 50%, white, 0%, black)"
-PASS testGradient("background-image: radial-gradient(10px 10px at center, white, 100%, black)") is "radial-gradient(10px 10px at 50% 50%, white, 100%, black)"
+PASS testGradient("background-image: radial-gradient(10px 10px at center, white, black)") is "radial-gradient(10px 10px at center center, white, black)"
+PASS testGradient("background-image: radial-gradient(10px 10px at center, white, 50%, black)") is "radial-gradient(10px 10px at center center, white, 50%, black)"
+PASS testGradient("background-image: radial-gradient(10px 10px at center, white, 0%, black)") is "radial-gradient(10px 10px at center center, white, 0%, black)"
+PASS testGradient("background-image: radial-gradient(10px 10px at center, white, 100%, black)") is "radial-gradient(10px 10px at center center, white, 100%, black)"
 repeating-radial-gradient
 
 
diff --git a/third_party/WebKit/LayoutTests/fast/gradients/unprefixed-gradient-parsing.html b/third_party/WebKit/LayoutTests/fast/gradients/unprefixed-gradient-parsing.html
index 1793fcf..bb5f971 100644
--- a/third_party/WebKit/LayoutTests/fast/gradients/unprefixed-gradient-parsing.html
+++ b/third_party/WebKit/LayoutTests/fast/gradients/unprefixed-gradient-parsing.html
@@ -60,7 +60,7 @@
 debug('<p>radial-gradient</p>');
 
 shouldBe('testGradient("background-image: radial-gradient(white, black)")', '"radial-gradient(white, black)"');
-shouldBe('testGradient("background-image: radial-gradient(at bottom right, white, black)")', '"radial-gradient(at 100% 100%, white, black)"');
+shouldBe('testGradient("background-image: radial-gradient(at bottom right, white, black)")', '"radial-gradient(at right bottom, white, black)"');
 shouldBe('testGradient("background-image: radial-gradient(farthest-corner, white, black)")', '"radial-gradient(white, black)"');
 shouldBe('testGradient("background-image: radial-gradient(farthest-corner, white, 35%, black)")', '"radial-gradient(white, 35%, black)"');
 shouldBe('testGradient("background-image: radial-gradient(closest-side, white, black)")', '"radial-gradient(closest-side, white, black)"');
@@ -70,8 +70,8 @@
 shouldBe('testGradient("background-image: radial-gradient(circle, white, black)")', '"radial-gradient(circle, white, black)"');
 shouldBe('testGradient("background-image: radial-gradient(circle farthest-corner, white, black)")', '"radial-gradient(circle, white, black)"');
 shouldBe('testGradient("background-image: radial-gradient(circle closest-side, white, black)")', '"radial-gradient(circle closest-side, white, black)"');
-shouldBe('testGradient("background-image: radial-gradient(circle closest-side at top, white, black)")', '"radial-gradient(circle closest-side at 50% 0%, white, black)"');
-shouldBe('testGradient("background-image: radial-gradient(circle closest-side at top left, white, black)")', '"radial-gradient(circle closest-side at 0% 0%, white, black)"');
+shouldBe('testGradient("background-image: radial-gradient(circle closest-side at top, white, black)")', '"radial-gradient(circle closest-side at center top, white, black)"');
+shouldBe('testGradient("background-image: radial-gradient(circle closest-side at top left, white, black)")', '"radial-gradient(circle closest-side at left top, white, black)"');
 shouldBe('testGradient("background-image: radial-gradient(circle closest-side at 10px 20%, white, black)")', '"radial-gradient(circle closest-side at 10px 20%, white, black)"');
 shouldBe('testGradient("background-image: radial-gradient(at 10px 20% circle closest-side, white, black)")', '"none"');
 shouldBe('testGradient("background-image: radial-gradient(circle at 10px 20% circle, white, black)")', '"none"');
@@ -88,10 +88,10 @@
 shouldBe('testGradient("background-image: radial-gradient(ellipse 10px, white, 25%, 75%, black)")', '"none"');
 shouldBe('testGradient("background-image: radial-gradient(ellipse 10px 20px, white, black)")', '"radial-gradient(10px 20px, white, black)"');
 shouldBe('testGradient("background-image: radial-gradient(circle 10px, white, black)")', '"radial-gradient(10px, white, black)"');
-shouldBe('testGradient("background-image: radial-gradient(10px 10px at center, white, black)")', '"radial-gradient(10px 10px at 50% 50%, white, black)"');
-shouldBe('testGradient("background-image: radial-gradient(10px 10px at center, white, 50%, black)")', '"radial-gradient(10px 10px at 50% 50%, white, 50%, black)"');
-shouldBe('testGradient("background-image: radial-gradient(10px 10px at center, white, 0%, black)")', '"radial-gradient(10px 10px at 50% 50%, white, 0%, black)"');
-shouldBe('testGradient("background-image: radial-gradient(10px 10px at center, white, 100%, black)")', '"radial-gradient(10px 10px at 50% 50%, white, 100%, black)"');
+shouldBe('testGradient("background-image: radial-gradient(10px 10px at center, white, black)")', '"radial-gradient(10px 10px at center center, white, black)"');
+shouldBe('testGradient("background-image: radial-gradient(10px 10px at center, white, 50%, black)")', '"radial-gradient(10px 10px at center center, white, 50%, black)"');
+shouldBe('testGradient("background-image: radial-gradient(10px 10px at center, white, 0%, black)")', '"radial-gradient(10px 10px at center center, white, 0%, black)"');
+shouldBe('testGradient("background-image: radial-gradient(10px 10px at center, white, 100%, black)")', '"radial-gradient(10px 10px at center center, white, 100%, black)"');
 
 debug('<p>repeating-radial-gradient</p>');
 shouldBe('testGradient("background-image: repeating-radial-gradient(white, black)")', '"repeating-radial-gradient(white, black)"');
diff --git a/third_party/WebKit/LayoutTests/harness-tests/mojo-helpers.html b/third_party/WebKit/LayoutTests/harness-tests/mojo-helpers.html
index 79cbcec0..005b8cdc 100644
--- a/third_party/WebKit/LayoutTests/harness-tests/mojo-helpers.html
+++ b/third_party/WebKit/LayoutTests/harness-tests/mojo-helpers.html
@@ -16,12 +16,17 @@
 
 mojo_test(mojo => {
   return new Promise(resolve => {
-    // Complete the test as soon as a request comes in for a Frobinator service.
-    mojo.serviceRegistry.addServiceOverrideForTesting('Frobinator', resolve);
+    let calls = 0;
+    // Complete the test as soon as two requests come in for a Frobinator service.
+    mojo.serviceRegistry.addServiceOverrideForTesting('Frobinator', () => {
+      if (++calls == 2)
+        resolve();
+    });
 
     // Try to connect to the browser's Frobinator service. This should be
     // intercepted by the above override.
     mojo.serviceRegistry.connectToService('Frobinator');
+    mojo.serviceRegistry.connectToService('Frobinator');
   });
 }, 'Service registry overrides should be properly intercepted.');
 
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/css-snap-size-1/snap-height-dynamic-001-expected.txt b/third_party/WebKit/LayoutTests/imported/csswg-test/css-snap-size-1/snap-height-dynamic-001-expected.txt
deleted file mode 100644
index 53950e1..0000000
--- a/third_party/WebKit/LayoutTests/imported/csswg-test/css-snap-size-1/snap-height-dynamic-001-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL CSS Snap Size: snap-height dynamic change assert_not_equals: Height must change when snap-height changes got disallowed value 28
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/web-animations-api/animation-state-changes-negative-playback-rate.html b/third_party/WebKit/LayoutTests/web-animations-api/animation-state-changes-negative-playback-rate.html
new file mode 100644
index 0000000..f47492d0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/web-animations-api/animation-state-changes-negative-playback-rate.html
@@ -0,0 +1,424 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+
+<script>
+var duration = 100000;
+
+function assert_unresolved(value) {
+  assert_equals(value, null);
+}
+
+function idleAnimation() {
+  var animation = document.documentElement.animate([], duration);
+  animation.playbackRate = -1;
+  animation.cancel();
+  return animation;
+}
+
+function runningAnimation() {
+  var animation = idleAnimation();
+  animation.play();
+  animation.startTime = document.timeline.currentTime + duration;
+  return animation;
+}
+
+function pendingStartTimeAnimation() {
+  var animation = idleAnimation();
+  animation.play();
+  return animation;
+}
+
+function pendingStartTimeAndCurrentTimeAnimation() {
+  var animation = idleAnimation();
+  animation.play();
+  animation.pause();
+  animation.play();
+  return animation;
+}
+
+function pausedAnimation() {
+  var animation = idleAnimation();
+  animation.pause();
+  return animation;
+}
+
+function finishedAnimation() {
+  var animation = idleAnimation();
+  animation.play();
+  animation.finish();
+  return animation;
+}
+
+test(function() {
+  var animation = idleAnimation();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'idle');
+}, "idle");
+
+test(function() {
+  var animation = pendingStartTimeAnimation();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'pending');
+}, "pending startTime");
+
+test(function() {
+  var animation = pendingStartTimeAndCurrentTimeAnimation();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'pending');
+}, "pending startTime and currentTime");
+
+test(function() {
+  var animation = runningAnimation();
+  assert_equals(animation.startTime, document.timeline.currentTime - (animation.playbackRate * animation.currentTime));
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'running');
+}, "running");
+
+test(function() {
+  var animation = pausedAnimation();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'paused');
+}, "paused");
+
+test(function() {
+  var animation = finishedAnimation();
+  assert_equals(animation.startTime, document.timeline.currentTime - (animation.playbackRate * animation.currentTime));
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'finished');
+}, "finished");
+
+test(function() {
+  var animation = idleAnimation();
+  animation.play();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'pending');
+}, "idle -> play()");
+
+test(function() {
+  var animation = idleAnimation();
+  animation.pause();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'paused');
+}, "idle -> pause()");
+
+test(function() {
+  var animation = idleAnimation();
+  animation.cancel();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'idle');
+}, "idle -> cancel()");
+
+test(function() {
+  var animation = idleAnimation();
+  animation.finish();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'idle');
+}, "idle -> finish()");
+
+test(function() {
+  var animation = idleAnimation();
+  animation.reverse();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'pending');
+}, "idle -> reverse()");
+
+test(function() {
+  var animation = idleAnimation();
+  animation.currentTime = 1000;
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'idle');
+}, "idle -> set currentTime");
+
+test(function() {
+  var animation = idleAnimation();
+  animation.startTime = 1000;
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'idle');
+}, "idle -> set startTime");
+
+test(function() {
+  var animation = pendingStartTimeAnimation();
+  animation.play();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'pending');
+}, "pending startTime -> play()");
+
+test(function() {
+  var animation = pendingStartTimeAnimation();
+  animation.pause();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'pending');
+}, "pending startTime -> pause()");
+
+test(function() {
+  var animation = pendingStartTimeAnimation();
+  animation.cancel();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'idle');
+}, "pending startTime -> cancel()");
+
+test(function() {
+  var animation = pendingStartTimeAnimation();
+  animation.finish();
+  assert_equals(animation.startTime, document.timeline.currentTime - (animation.playbackRate * animation.currentTime));
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'finished');
+}, "pending startTime -> finish()");
+
+test(function() {
+  var animation = pendingStartTimeAnimation();
+  animation.reverse();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'pending');
+}, "pending startTime -> reverse()");
+
+test(function() {
+  var animation = pendingStartTimeAnimation();
+  animation.currentTime = 1000;
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 1000);
+  assert_equals(animation.playState, 'pending');
+}, "pending startTime -> set currentTime");
+
+test(function() {
+  var animation = pendingStartTimeAnimation();
+  animation.startTime = document.timeline.currentTime + 1000;
+  assert_equals(animation.startTime, document.timeline.currentTime + 1000);
+  assert_equals(animation.currentTime, 1000);
+  assert_equals(animation.playState, 'running');
+}, "pending startTime -> set startTime");
+
+test(function() {
+  var animation = pendingStartTimeAndCurrentTimeAnimation();
+  animation.play();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'pending');
+}, "pending startTime & currentTime -> play()");
+
+test(function() {
+  var animation = pendingStartTimeAndCurrentTimeAnimation();
+  animation.pause();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'pending');
+}, "pending startTime & currentTime -> pause()");
+
+test(function() {
+  var animation = pendingStartTimeAndCurrentTimeAnimation();
+  animation.cancel();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'idle');
+}, "pending startTime & currentTime -> cancel()");
+
+test(function() {
+  var animation = pendingStartTimeAndCurrentTimeAnimation();
+  animation.finish();
+  assert_equals(animation.startTime, document.timeline.currentTime - (animation.playbackRate * animation.currentTime));
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'finished');
+}, "pending startTime & currentTime -> finish()");
+
+test(function() {
+  var animation = pendingStartTimeAndCurrentTimeAnimation();
+  animation.reverse();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'pending');
+}, "pending startTime & currentTime -> reverse()");
+
+test(function() {
+  var animation = pendingStartTimeAndCurrentTimeAnimation();
+  animation.currentTime = 1000;
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 1000);
+  assert_equals(animation.playState, 'pending');
+}, "pending startTime & currentTime -> set currentTime");
+
+test(function() {
+  var animation = pendingStartTimeAndCurrentTimeAnimation();
+  animation.startTime = document.timeline.currentTime + duration;
+  assert_equals(animation.startTime, document.timeline.currentTime + duration);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'running');
+}, "pending startTime & currentTime -> set startTime");
+
+test(function() {
+  var animation = runningAnimation();
+  var startTime = animation.startTime;
+  var currentTime = animation.currentTime;
+  animation.play();
+  assert_equals(animation.startTime, startTime);
+  assert_equals(animation.currentTime, currentTime);
+  assert_equals(animation.playState, 'running');
+}, "running -> play()");
+
+test(function() {
+  var animation = runningAnimation();
+  animation.pause();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'pending');
+}, "running -> pause()");
+
+test(function() {
+  var animation = runningAnimation();
+  animation.cancel();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'idle');
+}, "running -> cancel()");
+
+test(function() {
+  var animation = runningAnimation();
+  animation.finish();
+  assert_equals(animation.startTime, document.timeline.currentTime - (animation.playbackRate * animation.currentTime));
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'finished');
+}, "running -> finish()");
+
+test(function() {
+  var animation = runningAnimation();
+  animation.reverse();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'pending');
+}, "running -> reverse()");
+
+test(function() {
+  var animation = runningAnimation();
+  animation.currentTime = 1000;
+  assert_equals(animation.startTime, document.timeline.currentTime - (animation.playbackRate * animation.currentTime));
+  assert_equals(animation.currentTime, 1000);
+  assert_equals(animation.playState, 'running');
+}, "running -> set currentTime");
+
+test(function() {
+  var animation = runningAnimation();
+  animation.startTime = document.timeline.currentTime + 1000;
+  assert_equals(animation.startTime, document.timeline.currentTime + 1000);
+  assert_equals(animation.currentTime, 1000);
+  assert_equals(animation.playState, 'running');
+}, "running -> set startTime");
+
+test(function() {
+  var animation = pausedAnimation();
+  animation.play();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'pending');
+}, "paused -> play()");
+
+test(function() {
+  var animation = pausedAnimation();
+  animation.pause();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'paused');
+}, "paused -> pause()");
+
+test(function() {
+  var animation = pausedAnimation();
+  animation.cancel();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'idle');
+}, "paused -> cancel()");
+
+test(function() {
+  var animation = pausedAnimation();
+  animation.finish();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'paused');
+}, "paused -> finish()");
+
+test(function() {
+  var animation = pausedAnimation();
+  animation.reverse();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'pending');
+}, "paused -> reverse()");
+
+test(function() {
+  var animation = pausedAnimation();
+  animation.currentTime = 1000;
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 1000);
+  assert_equals(animation.playState, 'paused');
+}, "paused -> set currentTime");
+
+test(function() {
+  var animation = pausedAnimation();
+  animation.startTime = 1000;
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'paused');
+}, "paused -> set startTime");
+
+test(function() {
+  var animation = finishedAnimation();
+  animation.play();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'pending');
+}, "finished -> play()");
+
+test(function() {
+  var animation = finishedAnimation();
+  animation.pause();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'paused');
+}, "finished -> pause()");
+
+test(function() {
+  var animation = finishedAnimation();
+  animation.cancel();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'idle');
+}, "finished -> cancel()");
+
+test(function() {
+  var animation = finishedAnimation();
+  animation.finish();
+  assert_equals(animation.startTime, document.timeline.currentTime - (animation.playbackRate * animation.currentTime));
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'finished');
+}, "finished -> finish()");
+
+test(function() {
+  var animation = finishedAnimation();
+  animation.reverse();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'pending');
+}, "finished -> reverse()");
+
+test(function() {
+  var animation = finishedAnimation();
+  animation.currentTime = 1000;
+  assert_equals(animation.startTime, document.timeline.currentTime - (animation.playbackRate * animation.currentTime));
+  assert_equals(animation.currentTime, 1000);
+  assert_equals(animation.playState, 'running');
+}, "finished -> set currentTime");
+</script>
diff --git a/third_party/WebKit/LayoutTests/web-animations-api/animation-state-changes.html b/third_party/WebKit/LayoutTests/web-animations-api/animation-state-changes.html
new file mode 100644
index 0000000..667dd1ba
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/web-animations-api/animation-state-changes.html
@@ -0,0 +1,423 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+
+<script>
+var duration = 100000;
+
+function assert_unresolved(value) {
+  assert_equals(value, null);
+}
+
+function idleAnimation() {
+  var animation = document.documentElement.animate([], duration);
+  animation.cancel();
+  return animation;
+}
+
+function runningAnimation() {
+  var animation = idleAnimation();
+  animation.play();
+  animation.startTime = document.timeline.currentTime;
+  return animation;
+}
+
+function pendingStartTimeAnimation() {
+  var animation = idleAnimation();
+  animation.play();
+  return animation;
+}
+
+function pendingStartTimeAndCurrentTimeAnimation() {
+  var animation = idleAnimation();
+  animation.play();
+  animation.pause();
+  animation.play();
+  return animation;
+}
+
+function pausedAnimation() {
+  var animation = idleAnimation();
+  animation.pause();
+  return animation;
+}
+
+function finishedAnimation() {
+  var animation = idleAnimation();
+  animation.play();
+  animation.finish();
+  return animation;
+}
+
+test(function() {
+  var animation = idleAnimation();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'idle');
+}, "idle");
+
+test(function() {
+  var animation = pendingStartTimeAnimation();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'pending');
+}, "pending startTime");
+
+test(function() {
+  var animation = pendingStartTimeAndCurrentTimeAnimation();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'pending');
+}, "pending startTime and currentTime");
+
+test(function() {
+  var animation = runningAnimation();
+  assert_equals(animation.startTime, document.timeline.currentTime - animation.currentTime);
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'running');
+}, "running");
+
+test(function() {
+  var animation = pausedAnimation();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'paused');
+}, "paused");
+
+test(function() {
+  var animation = finishedAnimation();
+  assert_equals(animation.startTime, document.timeline.currentTime - animation.currentTime);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'finished');
+}, "finished");
+
+test(function() {
+  var animation = idleAnimation();
+  animation.play();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'pending');
+}, "idle -> play()");
+
+test(function() {
+  var animation = idleAnimation();
+  animation.pause();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'paused');
+}, "idle -> pause()");
+
+test(function() {
+  var animation = idleAnimation();
+  animation.cancel();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'idle');
+}, "idle -> cancel()");
+
+test(function() {
+  var animation = idleAnimation();
+  animation.finish();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'idle');
+}, "idle -> finish()");
+
+test(function() {
+  var animation = idleAnimation();
+  animation.reverse();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'pending');
+}, "idle -> reverse()");
+
+test(function() {
+  var animation = idleAnimation();
+  animation.currentTime = 1000;
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'idle');
+}, "idle -> set currentTime");
+
+test(function() {
+  var animation = idleAnimation();
+  animation.startTime = 1000;
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'idle');
+}, "idle -> set startTime");
+
+test(function() {
+  var animation = pendingStartTimeAnimation();
+  animation.play();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'pending');
+}, "pending startTime -> play()");
+
+test(function() {
+  var animation = pendingStartTimeAnimation();
+  animation.pause();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'pending');
+}, "pending startTime -> pause()");
+
+test(function() {
+  var animation = pendingStartTimeAnimation();
+  animation.cancel();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'idle');
+}, "pending startTime -> cancel()");
+
+test(function() {
+  var animation = pendingStartTimeAnimation();
+  animation.finish();
+  assert_equals(animation.startTime, document.timeline.currentTime - animation.currentTime);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'finished');
+}, "pending startTime -> finish()");
+
+test(function() {
+  var animation = pendingStartTimeAnimation();
+  animation.reverse();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'pending');
+}, "pending startTime -> reverse()");
+
+test(function() {
+  var animation = pendingStartTimeAnimation();
+  animation.currentTime = 1000;
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 1000);
+  assert_equals(animation.playState, 'pending');
+}, "pending startTime -> set currentTime");
+
+test(function() {
+  var animation = pendingStartTimeAnimation();
+  animation.startTime = document.timeline.currentTime - 1000;
+  assert_equals(animation.startTime, document.timeline.currentTime - 1000);
+  assert_equals(animation.currentTime, 1000);
+  assert_equals(animation.playState, 'running');
+}, "pending startTime -> set startTime");
+
+test(function() {
+  var animation = pendingStartTimeAndCurrentTimeAnimation();
+  animation.play();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'pending');
+}, "pending startTime & currentTime -> play()");
+
+test(function() {
+  var animation = pendingStartTimeAndCurrentTimeAnimation();
+  animation.pause();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'pending');
+}, "pending startTime & currentTime -> pause()");
+
+test(function() {
+  var animation = pendingStartTimeAndCurrentTimeAnimation();
+  animation.cancel();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'idle');
+}, "pending startTime & currentTime -> cancel()");
+
+test(function() {
+  var animation = pendingStartTimeAndCurrentTimeAnimation();
+  animation.finish();
+  assert_equals(animation.startTime, document.timeline.currentTime - animation.currentTime);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'finished');
+}, "pending startTime & currentTime -> finish()");
+
+test(function() {
+  var animation = pendingStartTimeAndCurrentTimeAnimation();
+  animation.reverse();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'pending');
+}, "pending startTime & currentTime -> reverse()");
+
+test(function() {
+  var animation = pendingStartTimeAndCurrentTimeAnimation();
+  animation.currentTime = 1000;
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 1000);
+  assert_equals(animation.playState, 'pending');
+}, "pending startTime & currentTime -> set currentTime");
+
+test(function() {
+  var animation = pendingStartTimeAndCurrentTimeAnimation();
+  animation.startTime = document.timeline.currentTime;
+  assert_equals(animation.startTime, document.timeline.currentTime);
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'running');
+}, "pending startTime & currentTime -> set startTime");
+
+test(function() {
+  var animation = runningAnimation();
+  var startTime = animation.startTime;
+  var currentTime = animation.currentTime;
+  animation.play();
+  assert_equals(animation.startTime, startTime);
+  assert_equals(animation.currentTime, currentTime);
+  assert_equals(animation.playState, 'running');
+}, "running -> play()");
+
+test(function() {
+  var animation = runningAnimation();
+  animation.pause();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'pending');
+}, "running -> pause()");
+
+test(function() {
+  var animation = runningAnimation();
+  animation.cancel();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'idle');
+}, "running -> cancel()");
+
+test(function() {
+  var animation = runningAnimation();
+  animation.finish();
+  assert_equals(animation.startTime, document.timeline.currentTime - animation.currentTime);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'finished');
+}, "running -> finish()");
+
+test(function() {
+  var animation = runningAnimation();
+  animation.reverse();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'pending');
+}, "running -> reverse()");
+
+test(function() {
+  var animation = runningAnimation();
+  animation.currentTime = 1000;
+  assert_equals(animation.startTime, document.timeline.currentTime - animation.currentTime);
+  assert_equals(animation.currentTime, 1000);
+  assert_equals(animation.playState, 'running');
+}, "running -> set currentTime");
+
+test(function() {
+  var animation = runningAnimation();
+  animation.startTime = document.timeline.currentTime - 1000;
+  assert_equals(animation.startTime, document.timeline.currentTime - 1000);
+  assert_equals(animation.currentTime, 1000);
+  assert_equals(animation.playState, 'running');
+}, "running -> set startTime");
+
+test(function() {
+  var animation = pausedAnimation();
+  animation.play();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'pending');
+}, "paused -> play()");
+
+test(function() {
+  var animation = pausedAnimation();
+  animation.pause();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'paused');
+}, "paused -> pause()");
+
+test(function() {
+  var animation = pausedAnimation();
+  animation.cancel();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'idle');
+}, "paused -> cancel()");
+
+test(function() {
+  var animation = pausedAnimation();
+  animation.finish();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'paused');
+}, "paused -> finish()");
+
+test(function() {
+  var animation = pausedAnimation();
+  animation.reverse();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'pending');
+}, "paused -> reverse()");
+
+test(function() {
+  var animation = pausedAnimation();
+  animation.currentTime = 1000;
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 1000);
+  assert_equals(animation.playState, 'paused');
+}, "paused -> set currentTime");
+
+test(function() {
+  var animation = pausedAnimation();
+  animation.startTime = 1000;
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'paused');
+}, "paused -> set startTime");
+
+test(function() {
+  var animation = finishedAnimation();
+  animation.play();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, 0);
+  assert_equals(animation.playState, 'pending');
+}, "finished -> play()");
+
+test(function() {
+  var animation = finishedAnimation();
+  animation.pause();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'paused');
+}, "finished -> pause()");
+
+test(function() {
+  var animation = finishedAnimation();
+  animation.cancel();
+  assert_unresolved(animation.startTime);
+  assert_unresolved(animation.currentTime);
+  assert_equals(animation.playState, 'idle');
+}, "finished -> cancel()");
+
+test(function() {
+  var animation = finishedAnimation();
+  animation.finish();
+  assert_equals(animation.startTime, document.timeline.currentTime - animation.currentTime);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'finished');
+}, "finished -> finish()");
+
+test(function() {
+  var animation = finishedAnimation();
+  animation.reverse();
+  assert_unresolved(animation.startTime);
+  assert_equals(animation.currentTime, duration);
+  assert_equals(animation.playState, 'pending');
+}, "finished -> reverse()");
+
+test(function() {
+  var animation = finishedAnimation();
+  animation.currentTime = 1000;
+  assert_equals(animation.startTime, document.timeline.currentTime - animation.currentTime);
+  assert_equals(animation.currentTime, 1000);
+  assert_equals(animation.playState, 'running');
+}, "finished -> set currentTime");
+</script>
diff --git a/third_party/WebKit/LayoutTests/web-animations-api/player-state-changes.html b/third_party/WebKit/LayoutTests/web-animations-api/player-state-changes.html
deleted file mode 100644
index 0b95f24..0000000
--- a/third_party/WebKit/LayoutTests/web-animations-api/player-state-changes.html
+++ /dev/null
@@ -1,418 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-
-<script>
-function assert_unresolved(value) {
-  assert_equals(value, null);
-}
-
-function idlePlayer() {
-  var player = document.documentElement.animate([], 100000);
-  player.cancel();
-  return player;
-}
-
-function runningPlayer() {
-  var player = document.documentElement.animate([], 100000);
-  player.startTime = document.timeline.currentTime;
-  return player;
-}
-
-function pendingStartTimePlayer() {
-  var player = document.documentElement.animate([], 100000);
-  return player;
-}
-
-function pendingStartTimeAndCurrentTimePlayer() {
-  var player = document.documentElement.animate([], 100000);
-  player.pause();
-  player.play();
-  return player;
-}
-
-function pausedPlayer() {
-  var player = document.documentElement.animate([], 100000);
-  player.pause();
-  player.currentTime = 0;
-  return player;
-}
-
-function finishedPlayer() {
-  var player = document.documentElement.animate([], 100000);
-  player.finish();
-  return player;
-}
-
-test(function() {
-  var player = idlePlayer();
-  assert_unresolved(player.startTime);
-  assert_unresolved(player.currentTime);
-  assert_equals(player.playState, 'idle');
-}, "idle");
-
-test(function() {
-  var player = pendingStartTimePlayer();
-  assert_unresolved(player.startTime);
-  assert_equals(player.currentTime, 0);
-  assert_equals(player.playState, 'pending');
-}, "pending startTime");
-
-test(function() {
-  var player = pendingStartTimeAndCurrentTimePlayer();
-  assert_unresolved(player.startTime);
-  assert_unresolved(player.currentTime);
-  assert_equals(player.playState, 'pending');
-}, "pending startTime and currentTime");
-
-test(function() {
-  var player = runningPlayer();
-  assert_equals(player.startTime, document.timeline.currentTime - player.currentTime);
-  assert_equals(player.currentTime, 0);
-  assert_equals(player.playState, 'running');
-}, "running");
-
-test(function() {
-  var player = pausedPlayer();
-  assert_unresolved(player.startTime);
-  assert_equals(player.currentTime, 0);
-  assert_equals(player.playState, 'paused');
-}, "paused");
-
-test(function() {
-  var player = finishedPlayer();
-  assert_equals(player.startTime, document.timeline.currentTime - player.currentTime);
-  assert_equals(player.currentTime, 100000);
-  assert_equals(player.playState, 'finished');
-}, "finished");
-
-test(function() {
-  var player = idlePlayer();
-  player.play();
-  assert_unresolved(player.startTime);
-  assert_equals(player.currentTime, 0);
-  assert_equals(player.playState, 'pending');
-}, "idle -> play()");
-
-test(function() {
-  var player = idlePlayer();
-  player.pause();
-  assert_unresolved(player.startTime);
-  assert_unresolved(player.currentTime);
-  assert_equals(player.playState, 'idle');
-}, "idle -> pause()");
-
-test(function() {
-  var player = idlePlayer();
-  player.cancel();
-  assert_unresolved(player.startTime);
-  assert_unresolved(player.currentTime);
-  assert_equals(player.playState, 'idle');
-}, "idle -> cancel()");
-
-test(function() {
-  var player = idlePlayer();
-  player.finish();
-  assert_unresolved(player.startTime);
-  assert_unresolved(player.currentTime);
-  assert_equals(player.playState, 'idle');
-}, "idle -> finish()");
-
-test(function() {
-  var player = idlePlayer();
-  player.reverse();
-  assert_unresolved(player.startTime);
-  assert_equals(player.currentTime, 100000);
-  assert_equals(player.playState, 'pending');
-}, "idle -> reverse()");
-
-test(function() {
-  var player = idlePlayer();
-  player.currentTime = 1000;
-  assert_unresolved(player.startTime);
-  assert_unresolved(player.currentTime);
-  assert_equals(player.playState, 'idle');
-}, "idle -> set currentTime");
-
-test(function() {
-  var player = idlePlayer();
-  player.startTime = 1000;
-  assert_unresolved(player.startTime);
-  assert_unresolved(player.currentTime);
-  assert_equals(player.playState, 'idle');
-}, "idle -> set startTime");
-
-test(function() {
-  var player = pendingStartTimePlayer();
-  player.play();
-  assert_unresolved(player.startTime);
-  assert_equals(player.currentTime, 0);
-  assert_equals(player.playState, 'pending');
-}, "pending startTime -> play()");
-
-test(function() {
-  var player = pendingStartTimePlayer();
-  player.pause();
-  assert_unresolved(player.startTime);
-  assert_unresolved(player.currentTime);
-  assert_equals(player.playState, 'pending');
-}, "pending startTime -> pause()");
-
-test(function() {
-  var player = pendingStartTimePlayer();
-  player.cancel();
-  assert_unresolved(player.startTime);
-  assert_unresolved(player.currentTime);
-  assert_equals(player.playState, 'idle');
-}, "pending startTime -> cancel()");
-
-test(function() {
-  var player = pendingStartTimePlayer();
-  player.finish();
-  assert_equals(player.startTime, document.timeline.currentTime - player.currentTime);
-  assert_equals(player.currentTime, 100000);
-  assert_equals(player.playState, 'finished');
-}, "pending startTime -> finish()");
-
-test(function() {
-  var player = pendingStartTimePlayer();
-  player.reverse();
-  assert_unresolved(player.startTime);
-  assert_equals(player.currentTime, 100000);
-  assert_equals(player.playState, 'pending');
-}, "pending startTime -> reverse()");
-
-test(function() {
-  var player = pendingStartTimePlayer();
-  player.currentTime = 1000;
-  assert_unresolved(player.startTime);
-  assert_equals(player.currentTime, 1000);
-  assert_equals(player.playState, 'pending');
-}, "pending startTime -> set currentTime");
-
-test(function() {
-  var player = pendingStartTimePlayer();
-  player.startTime = document.timeline.currentTime - 1000;
-  assert_equals(player.startTime, document.timeline.currentTime - 1000);
-  assert_equals(player.currentTime, 1000);
-  assert_equals(player.playState, 'running');
-}, "pending startTime -> set startTime");
-
-test(function() {
-  var player = pendingStartTimeAndCurrentTimePlayer();
-  player.play();
-  assert_unresolved(player.startTime);
-  assert_unresolved(player.currentTime);
-  assert_equals(player.playState, 'pending');
-}, "pending startTime & currentTime -> play()");
-
-test(function() {
-  var player = pendingStartTimeAndCurrentTimePlayer();
-  player.pause();
-  assert_unresolved(player.startTime);
-  assert_unresolved(player.currentTime);
-  assert_equals(player.playState, 'pending');
-}, "pending startTime & currentTime -> pause()");
-
-test(function() {
-  var player = pendingStartTimeAndCurrentTimePlayer();
-  player.cancel();
-  assert_unresolved(player.startTime);
-  assert_unresolved(player.currentTime);
-  assert_equals(player.playState, 'idle');
-}, "pending startTime & currentTime -> cancel()");
-
-test(function() {
-  var player = pendingStartTimeAndCurrentTimePlayer();
-  player.finish();
-  assert_equals(player.startTime, document.timeline.currentTime - player.currentTime);
-  assert_equals(player.currentTime, 100000);
-  assert_equals(player.playState, 'finished');
-}, "pending startTime & currentTime -> finish()");
-
-test(function() {
-  var player = pendingStartTimeAndCurrentTimePlayer();
-  player.reverse();
-  assert_unresolved(player.startTime);
-  assert_unresolved(player.currentTime);
-  assert_equals(player.playState, 'pending');
-}, "pending startTime & currentTime -> reverse()");
-
-test(function() {
-  var player = pendingStartTimeAndCurrentTimePlayer();
-  player.currentTime = 1000;
-  assert_unresolved(player.startTime);
-  assert_equals(player.currentTime, 1000);
-  assert_equals(player.playState, 'pending');
-}, "pending startTime & currentTime -> set currentTime");
-
-test(function() {
-  var player = pendingStartTimeAndCurrentTimePlayer();
-  player.startTime = document.timeline.currentTime;
-  assert_equals(player.startTime, document.timeline.currentTime);
-  assert_equals(player.currentTime, 0);
-  assert_equals(player.playState, 'running');
-}, "pending startTime & currentTime -> set startTime");
-
-test(function() {
-  var player = runningPlayer();
-  var startTime = player.startTime;
-  var currentTime = player.currentTime;
-  player.play();
-  assert_equals(player.startTime, startTime);
-  assert_equals(player.currentTime, currentTime);
-  assert_equals(player.playState, 'running');
-}, "running -> play()");
-
-test(function() {
-  var player = runningPlayer();
-  player.pause();
-  assert_unresolved(player.startTime);
-  assert_unresolved(player.currentTime);
-  assert_equals(player.playState, 'pending');
-}, "running -> pause()");
-
-test(function() {
-  var player = runningPlayer();
-  player.cancel();
-  assert_unresolved(player.startTime);
-  assert_unresolved(player.currentTime);
-  assert_equals(player.playState, 'idle');
-}, "running -> cancel()");
-
-test(function() {
-  var player = runningPlayer();
-  player.finish();
-  assert_equals(player.startTime, document.timeline.currentTime - player.currentTime);
-  assert_equals(player.currentTime, 100000);
-  assert_equals(player.playState, 'finished');
-}, "running -> finish()");
-
-test(function() {
-  var player = runningPlayer();
-  player.reverse();
-  assert_unresolved(player.startTime);
-  assert_unresolved(player.currentTime);
-  assert_equals(player.playState, 'pending');
-}, "running -> reverse()");
-
-test(function() {
-  var player = runningPlayer();
-  player.currentTime = 1000;
-  assert_equals(player.startTime, document.timeline.currentTime - player.currentTime);
-  assert_equals(player.currentTime, 1000);
-  assert_equals(player.playState, 'running');
-}, "running -> set currentTime");
-
-test(function() {
-  var player = runningPlayer();
-  player.startTime = document.timeline.currentTime - 1000;
-  assert_equals(player.startTime, document.timeline.currentTime - 1000);
-  assert_equals(player.currentTime, 1000);
-  assert_equals(player.playState, 'running');
-}, "running -> set startTime");
-
-test(function() {
-  var player = pausedPlayer();
-  player.play();
-  assert_unresolved(player.startTime);
-  assert_equals(player.currentTime, 0);
-  assert_equals(player.playState, 'pending');
-}, "paused -> play()");
-
-test(function() {
-  var player = pausedPlayer();
-  player.pause();
-  assert_unresolved(player.startTime);
-  assert_equals(player.currentTime, 0);
-  assert_equals(player.playState, 'paused');
-}, "paused -> pause()");
-
-test(function() {
-  var player = pausedPlayer();
-  player.cancel();
-  assert_unresolved(player.startTime);
-  assert_unresolved(player.currentTime);
-  assert_equals(player.playState, 'idle');
-}, "paused -> cancel()");
-
-test(function() {
-  var player = pausedPlayer();
-  player.finish();
-  assert_unresolved(player.startTime);
-  assert_equals(player.currentTime, 100000);
-  assert_equals(player.playState, 'paused');
-}, "paused -> finish()");
-
-test(function() {
-  var player = pausedPlayer();
-  player.reverse();
-  assert_unresolved(player.startTime);
-  assert_equals(player.currentTime, 100000);
-  assert_equals(player.playState, 'pending');
-}, "paused -> reverse()");
-
-test(function() {
-  var player = pausedPlayer();
-  player.currentTime = 1000;
-  assert_unresolved(player.startTime);
-  assert_equals(player.currentTime, 1000);
-  assert_equals(player.playState, 'paused');
-}, "paused -> set currentTime");
-
-test(function() {
-  var player = pausedPlayer();
-  player.startTime = 1000;
-  assert_unresolved(player.startTime);
-  assert_equals(player.currentTime, 0);
-  assert_equals(player.playState, 'paused');
-}, "paused -> set startTime");
-
-test(function() {
-  var player = finishedPlayer();
-  player.play();
-  assert_unresolved(player.startTime);
-  assert_equals(player.currentTime, 0);
-  assert_equals(player.playState, 'pending');
-}, "finished -> play()");
-
-test(function() {
-  var player = finishedPlayer();
-  player.pause();
-  assert_unresolved(player.startTime);
-  assert_equals(player.currentTime, 100000);
-  assert_equals(player.playState, 'paused');
-}, "finished -> pause()");
-
-test(function() {
-  var player = finishedPlayer();
-  player.cancel();
-  assert_unresolved(player.startTime);
-  assert_unresolved(player.currentTime);
-  assert_equals(player.playState, 'idle');
-}, "finished -> cancel()");
-
-test(function() {
-  var player = finishedPlayer();
-  player.finish();
-  assert_equals(player.startTime, document.timeline.currentTime - player.currentTime);
-  assert_equals(player.currentTime, 100000);
-  assert_equals(player.playState, 'finished');
-}, "finished -> finish()");
-
-test(function() {
-  var player = finishedPlayer();
-  player.reverse();
-  assert_unresolved(player.startTime);
-  assert_equals(player.currentTime, 100000);
-  assert_equals(player.playState, 'pending');
-}, "finished -> reverse()");
-
-test(function() {
-  var player = finishedPlayer();
-  player.currentTime = 1000;
-  assert_equals(player.startTime, document.timeline.currentTime - player.currentTime);
-  assert_equals(player.currentTime, 1000);
-  assert_equals(player.playState, 'running');
-}, "finished -> set currentTime");
-</script>
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptStreamer.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptStreamer.cpp
index 1cf24ad..e465472 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptStreamer.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptStreamer.cpp
@@ -231,7 +231,7 @@
 
         // Inform main thread to re-queue the data.
         m_loadingTaskRunner->postTask(
-            BLINK_FROM_HERE, bind(&SourceStream::fetchDataFromResourceBuffer, this, 0));
+            BLINK_FROM_HERE, threadSafeBind(&SourceStream::fetchDataFromResourceBuffer, AllowCrossThreadAccess(this), 0));
     }
 
     void didFinishLoading()
diff --git a/third_party/WebKit/Source/core/animation/Animation.cpp b/third_party/WebKit/Source/core/animation/Animation.cpp
index f99282a..f1a90d5 100644
--- a/third_party/WebKit/Source/core/animation/Animation.cpp
+++ b/third_party/WebKit/Source/core/animation/Animation.cpp
@@ -483,12 +483,12 @@
 
 Animation::AnimationPlayState Animation::calculatePlayState()
 {
+    if (m_paused && !m_currentTimePending)
+        return Paused;
     if (m_playState == Idle)
         return Idle;
-    if (m_currentTimePending || (isNull(m_startTime) && !m_paused && m_playbackRate != 0))
+    if (m_currentTimePending || (isNull(m_startTime) && m_playbackRate != 0))
         return Pending;
-    if (m_paused)
-        return Paused;
     if (limited())
         return Finished;
     return Running;
@@ -501,11 +501,17 @@
 
     PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand);
 
+    double newCurrentTime = currentTimeInternal();
+    if (calculatePlayState() == Idle) {
+        newCurrentTime = m_playbackRate < 0 ? effectEnd() : 0;
+    }
+
     if (playing()) {
         m_currentTimePending = true;
     }
+
     m_paused = true;
-    setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand);
+    setCurrentTimeInternal(newCurrentTime, TimingUpdateOnDemand);
 }
 
 void Animation::unpause()
@@ -861,7 +867,7 @@
 
     m_holdTime = currentTimeInternal();
     m_held = true;
-    // TODO
+    m_paused = false;
     m_playState = Idle;
     m_startTime = nullValue();
     m_currentTimePending = false;
diff --git a/third_party/WebKit/Source/core/animation/AnimationTest.cpp b/third_party/WebKit/Source/core/animation/AnimationTest.cpp
index 7860930..e5cc4df5 100644
--- a/third_party/WebKit/Source/core/animation/AnimationTest.cpp
+++ b/third_party/WebKit/Source/core/animation/AnimationTest.cpp
@@ -825,8 +825,8 @@
     EXPECT_TRUE(std::isnan(animation->currentTime()));
     EXPECT_TRUE(std::isnan(animation->startTime()));
     animation->pause();
-    EXPECT_EQ(Animation::Idle, animation->playStateInternal());
-    EXPECT_TRUE(std::isnan(animation->currentTime()));
+    EXPECT_EQ(Animation::Paused, animation->playStateInternal());
+    EXPECT_EQ(0, animation->currentTime());
     EXPECT_TRUE(std::isnan(animation->startTime()));
 }
 
diff --git a/third_party/WebKit/Source/core/css/CSSGradientValue.cpp b/third_party/WebKit/Source/core/css/CSSGradientValue.cpp
index 0a763778..2f8c60f 100644
--- a/third_party/WebKit/Source/core/css/CSSGradientValue.cpp
+++ b/third_party/WebKit/Source/core/css/CSSGradientValue.cpp
@@ -524,6 +524,8 @@
     case CSSValueRight:
         ASSERT(isHorizontal);
         return size.width();
+    case CSSValueCenter:
+        return origin + sign * .5f * edgeDistance;
     default:
         break;
     }
diff --git a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
index c1b512d..f453d18 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
@@ -3392,6 +3392,138 @@
     return nullptr;
 }
 
+static PassRefPtrWillBeRawPtr<CSSValue> consumeBackgroundBlendMode(CSSParserTokenRange& range)
+{
+    CSSValueID id = range.peek().id();
+    if (id == CSSValueNormal || id == CSSValueOverlay || (id >= CSSValueMultiply && id <= CSSValueLuminosity))
+        return consumeIdent(range);
+    return nullptr;
+}
+
+static PassRefPtrWillBeRawPtr<CSSValue> consumeBackgroundAttachment(CSSParserTokenRange& range)
+{
+    return consumeIdent<CSSValueScroll, CSSValueFixed, CSSValueLocal>(range);
+}
+
+static PassRefPtrWillBeRawPtr<CSSValue> consumeBackgroundBox(CSSParserTokenRange& range)
+{
+    return consumeIdent<CSSValueBorderBox, CSSValuePaddingBox, CSSValueContentBox>(range);
+}
+
+static PassRefPtrWillBeRawPtr<CSSValue> consumeBackgroundComposite(CSSParserTokenRange& range)
+{
+    return consumeIdentRange(range, CSSValueClear, CSSValuePlusLighter);
+}
+
+static PassRefPtrWillBeRawPtr<CSSValue> consumeMaskSourceType(CSSParserTokenRange& range)
+{
+    ASSERT(RuntimeEnabledFeatures::cssMaskSourceTypeEnabled());
+    return consumeIdent<CSSValueAuto, CSSValueAlpha, CSSValueLuminance>(range);
+}
+
+static PassRefPtrWillBeRawPtr<CSSValue> consumePrefixedBackgroundBox(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
+{
+    // The values 'border', 'padding' and 'content' are deprecated and do not apply to the version of the property that has the -webkit- prefix removed.
+    if (RefPtrWillBeRawPtr<CSSValue> value = consumeIdentRange(range, CSSValueBorder, CSSValuePaddingBox))
+        return value.release();
+    if ((property == CSSPropertyWebkitBackgroundClip || property == CSSPropertyWebkitMaskClip) && range.peek().id() == CSSValueText)
+        return consumeIdent(range);
+    return nullptr;
+}
+
+static PassRefPtrWillBeRawPtr<CSSValue> consumeBackgroundSize(CSSPropertyID unresolvedProperty, CSSParserTokenRange& range, CSSParserMode mode)
+{
+    if (identMatches<CSSValueContain, CSSValueCover>(range.peek().id()))
+        return consumeIdent(range);
+
+    RefPtrWillBeRawPtr<CSSPrimitiveValue> horizontal = consumeIdent<CSSValueAuto>(range);
+    if (!horizontal)
+        horizontal = consumeLengthOrPercent(range, mode, ValueRangeAll, UnitlessQuirk::Forbid);
+
+    RefPtrWillBeRawPtr<CSSPrimitiveValue> vertical = nullptr;
+    if (!range.atEnd()) {
+        if (range.peek().id() == CSSValueAuto) // `auto' is the default
+            range.consumeIncludingWhitespace();
+        else
+            vertical = consumeLengthOrPercent(range, mode, ValueRangeAll, UnitlessQuirk::Forbid);
+    } else if (unresolvedProperty == CSSPropertyAliasWebkitBackgroundSize) {
+        // Legacy syntax: "-webkit-background-size: 10px" is equivalent to "background-size: 10px 10px".
+        vertical = horizontal;
+    }
+    if (!vertical)
+        return horizontal;
+    return CSSValuePair::create(horizontal.release(), vertical.release(), CSSValuePair::KeepIdenticalValues);
+}
+
+static PassRefPtrWillBeRawPtr<CSSValue> consumeBackgroundComponent(CSSPropertyID unresolvedProperty, CSSParserTokenRange& range, const CSSParserContext& context)
+{
+    switch (unresolvedProperty) {
+    case CSSPropertyBackgroundClip:
+        return consumeBackgroundBox(range);
+    case CSSPropertyBackgroundBlendMode:
+        return consumeBackgroundBlendMode(range);
+    case CSSPropertyBackgroundAttachment:
+        return consumeBackgroundAttachment(range);
+    case CSSPropertyBackgroundOrigin:
+        return consumeBackgroundBox(range);
+    case CSSPropertyWebkitBackgroundComposite:
+    case CSSPropertyWebkitMaskComposite:
+        return consumeBackgroundComposite(range);
+    case CSSPropertyMaskSourceType:
+        return consumeMaskSourceType(range);
+    case CSSPropertyWebkitBackgroundClip:
+    case CSSPropertyWebkitBackgroundOrigin:
+    case CSSPropertyWebkitMaskClip:
+    case CSSPropertyWebkitMaskOrigin:
+        return consumePrefixedBackgroundBox(unresolvedProperty, range, context);
+    case CSSPropertyBackgroundImage:
+    case CSSPropertyWebkitMaskImage:
+        return consumeImage(range, context);
+    case CSSPropertyBackgroundPositionX:
+    case CSSPropertyWebkitMaskPositionX:
+        return consumePositionX(range, context.mode());
+    case CSSPropertyBackgroundPositionY:
+    case CSSPropertyWebkitMaskPositionY:
+        return consumePositionY(range, context.mode());
+    case CSSPropertyBackgroundSize:
+    case CSSPropertyAliasWebkitBackgroundSize:
+    case CSSPropertyWebkitMaskSize:
+        return consumeBackgroundSize(unresolvedProperty, range, context.mode());
+    case CSSPropertyBackgroundColor:
+        return consumeColor(range, context.mode());
+    default:
+        break;
+    };
+    return nullptr;
+}
+
+static void addBackgroundValue(RefPtrWillBeRawPtr<CSSValue>& list, PassRefPtrWillBeRawPtr<CSSValue> value)
+{
+    if (list) {
+        if (!list->isBaseValueList()) {
+            RefPtrWillBeRawPtr<CSSValue> firstValue = list.release();
+            list = CSSValueList::createCommaSeparated();
+            toCSSValueList(list.get())->append(firstValue.release());
+        }
+        toCSSValueList(list.get())->append(value);
+    } else {
+        // To conserve memory we don't actually wrap a single value in a list.
+        list = value;
+    }
+}
+
+static PassRefPtrWillBeRawPtr<CSSValue> consumeCommaSeparatedBackgroundComponent(CSSPropertyID unresolvedProperty, CSSParserTokenRange& range, const CSSParserContext& context)
+{
+    RefPtrWillBeRawPtr<CSSValue> result = nullptr;
+    do {
+        RefPtrWillBeRawPtr<CSSValue> value = consumeBackgroundComponent(unresolvedProperty, range, context);
+        if (!value)
+            return nullptr;
+        addBackgroundValue(result, value);
+    } while (consumeCommaIncludingWhitespace(range));
+    return result.release();
+}
+
 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSPropertyID unresolvedProperty)
 {
     CSSPropertyID property = resolveCSSPropertyID(unresolvedProperty);
@@ -3555,6 +3687,7 @@
     case CSSPropertyColumnRuleColor:
         return consumeColor(m_range, m_context.mode());
     case CSSPropertyColor:
+    case CSSPropertyBackgroundColor:
         return consumeColor(m_range, m_context.mode(), inQuirksMode());
     case CSSPropertyWebkitBorderStartWidth:
     case CSSPropertyWebkitBorderEndWidth:
@@ -3724,6 +3857,29 @@
     case CSSPropertyImageOrientation:
         ASSERT(RuntimeEnabledFeatures::imageOrientationEnabled());
         return consumeImageOrientation(m_range, m_context.mode());
+    case CSSPropertyBackgroundAttachment:
+    case CSSPropertyBackgroundBlendMode:
+    case CSSPropertyBackgroundClip:
+    case CSSPropertyBackgroundImage:
+    case CSSPropertyBackgroundOrigin:
+    case CSSPropertyBackgroundPositionX:
+    case CSSPropertyBackgroundPositionY:
+    case CSSPropertyBackgroundSize:
+    case CSSPropertyMaskSourceType:
+    case CSSPropertyWebkitBackgroundComposite:
+    case CSSPropertyWebkitBackgroundClip:
+    case CSSPropertyWebkitBackgroundOrigin:
+    case CSSPropertyWebkitMaskClip:
+    case CSSPropertyWebkitMaskComposite:
+    case CSSPropertyWebkitMaskImage:
+    case CSSPropertyWebkitMaskOrigin:
+    case CSSPropertyWebkitMaskPositionX:
+    case CSSPropertyWebkitMaskPositionY:
+    case CSSPropertyWebkitMaskSize:
+        return consumeCommaSeparatedBackgroundComponent(unresolvedProperty, m_range, m_context);
+    case CSSPropertyWebkitMaskRepeatX:
+    case CSSPropertyWebkitMaskRepeatY:
+        return nullptr;
     default:
         CSSParserValueList valueList(m_range);
         if (valueList.size()) {
diff --git a/third_party/WebKit/Source/core/css/parser/LegacyCSSPropertyParser.cpp b/third_party/WebKit/Source/core/css/parser/LegacyCSSPropertyParser.cpp
index 647911c..c47bdf3 100644
--- a/third_party/WebKit/Source/core/css/parser/LegacyCSSPropertyParser.cpp
+++ b/third_party/WebKit/Source/core/css/parser/LegacyCSSPropertyParser.cpp
@@ -271,41 +271,6 @@
     RefPtrWillBeRawPtr<CSSValue> parsedValue = nullptr;
 
     switch (propId) {
-    case CSSPropertyBackgroundColor: // <color> | inherit
-        parsedValue = parseColor(m_valueList->current(), inQuirksMode() && !inShorthand());
-        if (parsedValue)
-            m_valueList->next();
-        break;
-
-    case CSSPropertyBackgroundBlendMode:
-    case CSSPropertyBackgroundAttachment:
-    case CSSPropertyBackgroundClip:
-    case CSSPropertyWebkitBackgroundClip:
-    case CSSPropertyWebkitBackgroundComposite:
-    case CSSPropertyBackgroundImage:
-    case CSSPropertyBackgroundOrigin:
-    case CSSPropertyMaskSourceType:
-    case CSSPropertyWebkitBackgroundOrigin:
-    case CSSPropertyBackgroundPositionX:
-    case CSSPropertyBackgroundPositionY:
-    case CSSPropertyBackgroundSize:
-    case CSSPropertyWebkitMaskClip:
-    case CSSPropertyWebkitMaskComposite:
-    case CSSPropertyWebkitMaskImage:
-    case CSSPropertyWebkitMaskOrigin:
-    case CSSPropertyWebkitMaskPositionX:
-    case CSSPropertyWebkitMaskPositionY:
-    case CSSPropertyWebkitMaskSize:
-    case CSSPropertyWebkitMaskRepeatX:
-    case CSSPropertyWebkitMaskRepeatY:
-    {
-        RefPtrWillBeRawPtr<CSSValue> dummyValue = nullptr;
-        CSSPropertyID propId1, propId2;
-        if (!parseFillProperty(unresolvedProperty, propId1, propId2, parsedValue, dummyValue))
-            return nullptr;
-        ASSERT(!dummyValue);
-        break;
-    }
     case CSSPropertyJustifySelf:
         ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
         parsedValue = parseItemPositionOverflowPosition();
diff --git a/third_party/WebKit/Source/core/fetch/Resource.h b/third_party/WebKit/Source/core/fetch/Resource.h
index fb1f31f..03bdb79 100644
--- a/third_party/WebKit/Source/core/fetch/Resource.h
+++ b/third_party/WebKit/Source/core/fetch/Resource.h
@@ -336,7 +336,6 @@
     class CacheHandler;
     void cancelTimerFired(Timer<Resource>*);
 
-    void switchClientsToRevalidatedResource();
     void revalidationSucceeded(const ResourceResponse&);
     void revalidationFailed();
 
diff --git a/third_party/WebKit/Source/core/fetch/ResourceFetcher.h b/third_party/WebKit/Source/core/fetch/ResourceFetcher.h
index d3bd7fe2..8b92350 100644
--- a/third_party/WebKit/Source/core/fetch/ResourceFetcher.h
+++ b/third_party/WebKit/Source/core/fetch/ResourceFetcher.h
@@ -141,7 +141,6 @@
 
     String getCacheIdentifier() const;
 
-    void scheduleDocumentResourcesGC();
     bool clientDefersImage(const KURL&) const;
     static void determineRequestContext(ResourceRequest&, Resource::Type, bool isMainFrame);
     void determineRequestContext(ResourceRequest&, Resource::Type);
@@ -182,8 +181,6 @@
 
     void willTerminateResourceLoader(ResourceLoader*);
 
-    ResourceLoadPriority modifyPriorityForExperiments(ResourceLoadPriority, Resource::Type, const FetchRequest&);
-
     Member<FetchContext> m_context;
 
     HashSet<String> m_validatedURLs;
diff --git a/third_party/WebKit/Source/core/page/FocusController.cpp b/third_party/WebKit/Source/core/page/FocusController.cpp
index 00331f9..4617e58 100644
--- a/third_party/WebKit/Source/core/page/FocusController.cpp
+++ b/third_party/WebKit/Source/core/page/FocusController.cpp
@@ -72,7 +72,8 @@
 class FocusNavigationScope {
     STACK_ALLOCATED();
 public:
-    Node* rootNode() const;
+    Element* firstElement() const;
+    Element* lastElement() const;
     Element* owner() const;
     static FocusNavigationScope focusNavigationScopeOf(const Element&);
     static FocusNavigationScope ownedByNonFocusableFocusScopeOwner(Element&);
@@ -82,6 +83,7 @@
 
 private:
     explicit FocusNavigationScope(TreeScope*);
+    ContainerNode& rootNode() const;
     RawPtrWillBeMember<TreeScope> m_rootTreeScope;
 };
 
@@ -91,20 +93,31 @@
     ASSERT(treeScope);
 }
 
-Node* FocusNavigationScope::rootNode() const
+ContainerNode& FocusNavigationScope::rootNode() const
 {
-    return &m_rootTreeScope->rootNode();
+    return m_rootTreeScope->rootNode();
+}
+
+Element* FocusNavigationScope::firstElement() const
+{
+    ContainerNode& root = rootNode();
+    return root.isElementNode() ? &toElement(root) : ElementTraversal::next(root);
+}
+
+Element* FocusNavigationScope::lastElement() const
+{
+    return ElementTraversal::lastWithin(rootNode());
 }
 
 Element* FocusNavigationScope::owner() const
 {
-    Node* root = rootNode();
-    if (root->isShadowRoot()) {
-        ShadowRoot* shadowRoot = toShadowRoot(root);
-        return shadowRoot->isYoungest() ? shadowRoot->host() : shadowRoot->shadowInsertionPointOfYoungerShadowRoot();
+    ContainerNode& root = rootNode();
+    if (root.isShadowRoot()) {
+        ShadowRoot& shadowRoot = toShadowRoot(root);
+        return shadowRoot.isYoungest() ? shadowRoot.host() : shadowRoot.shadowInsertionPointOfYoungerShadowRoot();
     }
     // FIXME: Figure out the right thing for OOPI here.
-    if (Frame* frame = root->document().frame())
+    if (Frame* frame = root.document().frame())
         return frame->deprecatedLocalOwner();
     return nullptr;
 }
@@ -203,11 +216,8 @@
 }
 
 #if ENABLE(ASSERT)
-inline bool isNonFocusableShadowHost(const Node& node)
+inline bool isNonFocusableShadowHost(const Element& element)
 {
-    if (!node.isElementNode())
-        return false;
-    const Element& element = toElement(node);
     return isShadowHostWithoutCustomFocusLogic(element) && !element.isFocusable();
 }
 #endif
@@ -232,162 +242,154 @@
     return element.authorShadowRoot() && element.authorShadowRoot()->delegatesFocus();
 }
 
-inline int adjustedTabIndex(Node& node)
+inline int adjustedTabIndex(Element& element)
 {
-    return node.isElementNode() && isNonFocusableFocusScopeOwner(toElement(node)) ? 0 : node.tabIndex();
+    return isNonFocusableFocusScopeOwner(element) ? 0 : element.tabIndex();
 }
 
-inline bool shouldVisit(Node& node)
+inline bool shouldVisit(Element& element)
 {
-    if (!node.isElementNode())
-        return false;
-    Element& element = toElement(node);
     return element.isKeyboardFocusable() || isNonFocusableFocusScopeOwner(element);
 }
 
-Element* findElementWithExactTabIndex(Node* start, int tabIndex, WebFocusType type)
+Element* findElementWithExactTabIndex(Element* start, int tabIndex, WebFocusType type)
 {
     // Search is inclusive of start
-    for (Node* node = start; node; node = type == WebFocusTypeForward ? ElementTraversal::next(*node) : ElementTraversal::previous(*node)) {
-        if (shouldVisit(*node) && adjustedTabIndex(*node) == tabIndex)
-            return toElement(node);
+    for (Element* element = start; element; element = type == WebFocusTypeForward ? ElementTraversal::next(*element) : ElementTraversal::previous(*element)) {
+        if (shouldVisit(*element) && adjustedTabIndex(*element) == tabIndex)
+            return element;
     }
     return nullptr;
 }
 
-Element* nextElementWithGreaterTabIndex(Node* start, int tabIndex)
+Element* nextElementWithGreaterTabIndex(Element* start, int tabIndex)
 {
     // Search is inclusive of start
     int winningTabIndex = std::numeric_limits<short>::max() + 1;
-    Node* winner = nullptr;
-    for (Node& node : NodeTraversal::startsAt(start)) {
-        int currentTabIndex = adjustedTabIndex(node);
-        if (shouldVisit(node) && currentTabIndex > tabIndex && currentTabIndex < winningTabIndex) {
-            winner = &node;
+    Element* winner = nullptr;
+    for (Element& element : ElementTraversal::startsAt(start)) {
+        int currentTabIndex = adjustedTabIndex(element);
+        if (shouldVisit(element) && currentTabIndex > tabIndex && currentTabIndex < winningTabIndex) {
+            winner = &element;
             winningTabIndex = currentTabIndex;
         }
     }
-    ASSERT(!winner || winner->isElementNode());
-    return toElement(winner);
+    return winner;
 }
 
-Element* previousElementWithLowerTabIndex(Node* start, int tabIndex)
+Element* previousElementWithLowerTabIndex(Element* start, int tabIndex)
 {
     // Search is inclusive of start
     int winningTabIndex = 0;
-    Node* winner = nullptr;
-    for (Node* node = start; node; node = ElementTraversal::previous(*node)) {
-        int currentTabIndex = adjustedTabIndex(*node);
-        if (shouldVisit(*node) && currentTabIndex < tabIndex && currentTabIndex > winningTabIndex) {
-            winner = node;
+    Element* winner = nullptr;
+    for (Element* element = start; element; element = ElementTraversal::previous(*element)) {
+        int currentTabIndex = adjustedTabIndex(*element);
+        if (shouldVisit(*element) && currentTabIndex < tabIndex && currentTabIndex > winningTabIndex) {
+            winner = element;
             winningTabIndex = currentTabIndex;
         }
     }
-    ASSERT(!winner || winner->isElementNode());
-    return toElement(winner);
+    return winner;
 }
 
-Element* nextFocusableElement(const FocusNavigationScope& scope, Node* start)
+Element* nextFocusableElement(const FocusNavigationScope& scope, Element* start)
 {
     if (start) {
         int tabIndex = adjustedTabIndex(*start);
-        // If a node is excluded from the normal tabbing cycle, the next focusable node is determined by tree order
+        // If an element is excluded from the normal tabbing cycle, the next focusable element is determined by tree order
         if (tabIndex < 0) {
-            for (Node& node : NodeTraversal::startsAfter(*start)) {
-                if (shouldVisit(node) && adjustedTabIndex(node) >= 0)
-                    return &toElement(node);
+            for (Element& element : ElementTraversal::startsAfter(*start)) {
+                if (shouldVisit(element) && adjustedTabIndex(element) >= 0)
+                    return &element;
             }
         } else {
-            // First try to find a node with the same tabindex as start that comes after start in the scope.
+            // First try to find an element with the same tabindex as start that comes after start in the scope.
             if (Element* winner = findElementWithExactTabIndex(ElementTraversal::next(*start), tabIndex, WebFocusTypeForward))
                 return winner;
         }
         if (!tabIndex) {
-            // We've reached the last node in the document with a tabindex of 0. This is the end of the tabbing order.
+            // We've reached the last element in the document with a tabindex of 0. This is the end of the tabbing order.
             return nullptr;
         }
     }
 
-    // Look for the first node in the scope that:
+    // Look for the first element in the scope that:
     // 1) has the lowest tabindex that is higher than start's tabindex (or 0, if start is null), and
     // 2) comes first in the scope, if there's a tie.
-    if (Element* winner = nextElementWithGreaterTabIndex(scope.rootNode(), start ? adjustedTabIndex(*start) : 0))
+    if (Element* winner = nextElementWithGreaterTabIndex(scope.firstElement(), start ? adjustedTabIndex(*start) : 0))
         return winner;
 
-    // There are no nodes with a tabindex greater than start's tabindex,
-    // so find the first node with a tabindex of 0.
-    return findElementWithExactTabIndex(scope.rootNode(), 0, WebFocusTypeForward);
+    // There are no elements with a tabindex greater than start's tabindex,
+    // so find the first element with a tabindex of 0.
+    return findElementWithExactTabIndex(scope.firstElement(), 0, WebFocusTypeForward);
 }
 
-Element* previousFocusableElement(const FocusNavigationScope& scope, Node* start)
+Element* previousFocusableElement(const FocusNavigationScope& scope, Element* start)
 {
-    Node* last = nullptr;
-    for (Node* node = scope.rootNode(); node; node = node->lastChild())
-        last = node;
-    ASSERT(last);
+    Element* lastElement = scope.lastElement();
 
-    // First try to find the last node in the scope that comes before start and has the same tabindex as start.
-    // If start is null, find the last node in the scope with a tabindex of 0.
-    Node* startingNode;
-    int startingTabIndex;
+    // First try to find the last element in the scope that comes before start and has the same tabindex as start.
+    // If start is null, find the last element in the scope with a tabindex of 0.
+    Element* startElement;
+    int startTabIndex;
     if (start) {
-        startingNode = ElementTraversal::previous(*start);
-        startingTabIndex = adjustedTabIndex(*start);
+        startElement = ElementTraversal::previous(*start);
+        startTabIndex = adjustedTabIndex(*start);
     } else {
-        startingNode = last;
-        startingTabIndex = 0;
+        startElement = lastElement;
+        startTabIndex = 0;
     }
 
-    // However, if a node is excluded from the normal tabbing cycle, the previous focusable node is determined by tree order
-    if (startingTabIndex < 0) {
-        for (Node* node = startingNode; node; node = ElementTraversal::previous(*node)) {
-            if (shouldVisit(*node) && adjustedTabIndex(*node) >= 0)
-                return toElement(node);
+    // However, if an element is excluded from the normal tabbing cycle, the previous focusable element is determined by tree order
+    if (startTabIndex < 0) {
+        for (Element* element = startElement; element; element = ElementTraversal::previous(*element)) {
+            if (shouldVisit(*element) && adjustedTabIndex(*element) >= 0)
+                return element;
         }
     } else {
-        if (Element* winner = findElementWithExactTabIndex(startingNode, startingTabIndex, WebFocusTypeBackward))
+        if (Element* winner = findElementWithExactTabIndex(startElement, startTabIndex, WebFocusTypeBackward))
             return winner;
     }
 
-    // There are no nodes before start with the same tabindex as start, so look for a node that:
+    // There are no elements before start with the same tabindex as start, so look for an element that:
     // 1) has the highest non-zero tabindex (that is less than start's tabindex), and
     // 2) comes last in the scope, if there's a tie.
-    startingTabIndex = (start && startingTabIndex) ? startingTabIndex : std::numeric_limits<short>::max();
-    return previousElementWithLowerTabIndex(last, startingTabIndex);
+    startTabIndex = (start && startTabIndex) ? startTabIndex : std::numeric_limits<short>::max();
+    return previousElementWithLowerTabIndex(lastElement, startTabIndex);
 }
 
-// Searches through the given tree scope, starting from start node, for the next/previous
-// selectable element that comes after/before start node.
+// Searches through the given tree scope, starting from start element, for the next/previous
+// selectable element that comes after/before start element.
 // The order followed is as specified in the HTML spec[1], which is elements with tab indexes
 // first (from lowest to highest), and then elements without tab indexes (in document order).
 // The search algorithm also conforms the Shadow DOM spec[2], which inserts sequence in a shadow
 // tree into its host.
 //
-// @param start The node from which to start searching. The node after this will be focused.
+// @param start The element from which to start searching. The element after this will be focused.
 //        May be null.
-// @return The focus element that comes after/before start node.
+// @return The focus element that comes after/before start element.
 //
 // [1] https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation
 // [2] https://w3c.github.io/webcomponents/spec/shadow/#focus-navigation
-inline Element* findFocusableElementInternal(WebFocusType type, const FocusNavigationScope& scope, Node* node)
+inline Element* findFocusableElementInternal(WebFocusType type, const FocusNavigationScope& scope, Element* element)
 {
-    Element* found = (type == WebFocusTypeForward) ? nextFocusableElement(scope, node) : previousFocusableElement(scope, node);
+    Element* found = (type == WebFocusTypeForward) ? nextFocusableElement(scope, element) : previousFocusableElement(scope, element);
     return found;
 }
 
-Element* findFocusableElementRecursivelyForward(const FocusNavigationScope& scope, Node* start)
+Element* findFocusableElementRecursivelyForward(const FocusNavigationScope& scope, Element* start)
 {
-    // Starting node is exclusive.
+    // Starting element is exclusive.
     Element* found = findFocusableElementInternal(WebFocusTypeForward, scope, start);
     while (found) {
         if (isShadowHostDelegatesFocus(*found)) {
-            // If tabindex is positive, find focusable node inside its shadow tree.
+            // If tabindex is positive, find focusable element inside its shadow tree.
             if (found->tabIndex() >= 0 && isShadowHostWithoutCustomFocusLogic(*found)) {
                 FocusNavigationScope innerScope = FocusNavigationScope::ownedByShadowHost(*found);
                 if (Element* foundInInnerFocusScope = findFocusableElementRecursivelyForward(innerScope, nullptr))
                     return foundInInnerFocusScope;
             }
-            // Skip to the next node in the same scope.
+            // Skip to the next element in the same scope.
             found = findFocusableElementInternal(WebFocusTypeForward, scope, found);
             continue;
         }
@@ -406,9 +408,9 @@
     return nullptr;
 }
 
-Element* findFocusableElementRecursivelyBackward(const FocusNavigationScope& scope, Node* start)
+Element* findFocusableElementRecursivelyBackward(const FocusNavigationScope& scope, Element* start)
 {
-    // Starting node is exclusive.
+    // Starting element is exclusive.
     Element* found = findFocusableElementInternal(WebFocusTypeBackward, scope, start);
     while (found) {
         // Now |found| is on a focusable shadow host.
@@ -434,7 +436,7 @@
         }
 
         // Now |found| is on a non focusable scope owner (either shadow host or <shadow>).
-        // Find focusable node in descendant scope. If not found, find next focusable node within the
+        // Find focusable element in descendant scope. If not found, find next focusable element within the
         // current scope.
         if (isNonFocusableFocusScopeOwner(*found)) {
             FocusNavigationScope innerScope = FocusNavigationScope::ownedByNonFocusableFocusScopeOwner(*found);
@@ -451,7 +453,7 @@
     return nullptr;
 }
 
-Element* findFocusableElementRecursively(WebFocusType type, const FocusNavigationScope& scope, Node* start)
+Element* findFocusableElementRecursively(WebFocusType type, const FocusNavigationScope& scope, Element* start)
 {
     return (type == WebFocusTypeForward) ?
         findFocusableElementRecursivelyForward(scope, start) :
@@ -477,19 +479,19 @@
     return element;
 }
 
-Element* findFocusableElementAcrossFocusScopesForward(const FocusNavigationScope& scope, Node* currentNode)
+Element* findFocusableElementAcrossFocusScopesForward(const FocusNavigationScope& scope, Element* current)
 {
-    ASSERT(!currentNode || !isNonFocusableShadowHost(*currentNode));
+    ASSERT(!current || !isNonFocusableShadowHost(*current));
     Element* found;
-    if (currentNode && currentNode->isElementNode() && isShadowHostWithoutCustomFocusLogic(*toElement(currentNode))) {
-        FocusNavigationScope innerScope = FocusNavigationScope::ownedByShadowHost(*toElement(currentNode));
+    if (current && isShadowHostWithoutCustomFocusLogic(*current)) {
+        FocusNavigationScope innerScope = FocusNavigationScope::ownedByShadowHost(*current);
         Element* foundInInnerFocusScope = findFocusableElementRecursivelyForward(innerScope, nullptr);
-        found = foundInInnerFocusScope ? foundInInnerFocusScope : findFocusableElementRecursivelyForward(scope, currentNode);
+        found = foundInInnerFocusScope ? foundInInnerFocusScope : findFocusableElementRecursivelyForward(scope, current);
     } else {
-        found = findFocusableElementRecursivelyForward(scope, currentNode);
+        found = findFocusableElementRecursivelyForward(scope, current);
     }
 
-    // If there's no focusable node to advance to, move up the focus scopes until we find one.
+    // If there's no focusable element to advance to, move up the focus scopes until we find one.
     FocusNavigationScope currentScope = scope;
     while (!found) {
         Element* owner = currentScope.owner();
@@ -501,12 +503,12 @@
     return findFocusableElementDescendingDownIntoFrameDocument(WebFocusTypeForward, found);
 }
 
-Element* findFocusableElementAcrossFocusScopesBackward(const FocusNavigationScope& scope, Node* currentNode)
+Element* findFocusableElementAcrossFocusScopesBackward(const FocusNavigationScope& scope, Element* current)
 {
-    ASSERT(!currentNode || !isNonFocusableShadowHost(*currentNode));
-    Element* found = findFocusableElementRecursivelyBackward(scope, currentNode);
+    ASSERT(!current || !isNonFocusableShadowHost(*current));
+    Element* found = findFocusableElementRecursivelyBackward(scope, current);
 
-    // If there's no focusable node to advance to, move up the focus scopes until we find one.
+    // If there's no focusable element to advance to, move up the focus scopes until we find one.
     FocusNavigationScope currentScope = scope;
     while (!found) {
         Element* owner = currentScope.owner();
@@ -522,11 +524,11 @@
     return findFocusableElementDescendingDownIntoFrameDocument(WebFocusTypeBackward, found);
 }
 
-Element* findFocusableElementAcrossFocusScopes(WebFocusType type, const FocusNavigationScope& scope, Node* currentNode)
+Element* findFocusableElementAcrossFocusScopes(WebFocusType type, const FocusNavigationScope& scope, Element* current)
 {
     return (type == WebFocusTypeForward) ?
-        findFocusableElementAcrossFocusScopesForward(scope, currentNode) :
-        findFocusableElementAcrossFocusScopesBackward(scope, currentNode);
+        findFocusableElementAcrossFocusScopesForward(scope, current) :
+        findFocusableElementAcrossFocusScopesBackward(scope, current);
 }
 
 inline Element* adjustToElement(Node* node, WebFocusType type)
@@ -781,7 +783,7 @@
     ASSERT(element);
 
     if (element == document->focusedElement()) {
-        // Focus wrapped around to the same node.
+        // Focus wrapped around to the same element.
         return true;
     }
 
@@ -805,13 +807,13 @@
         return true;
     }
 
-    // FIXME: It would be nice to just be able to call setFocusedElement(node)
+    // FIXME: It would be nice to just be able to call setFocusedElement(element)
     // here, but we can't do that because some elements (e.g. HTMLInputElement
     // and HTMLTextAreaElement) do extra work in their focus() methods.
     Document& newDocument = element->document();
 
     if (&newDocument != document) {
-        // Focus is going away from this document, so clear the focused node.
+        // Focus is going away from this document, so clear the focused element.
         document->clearFocusedElement();
     }
 
@@ -916,7 +918,7 @@
     }
     setFocusedFrame(newFocusedFrame);
 
-    // Setting the focused node can result in losing our last reft to node when JS event handlers fire.
+    // Setting the focused element can result in losing our last reft to element when JS event handlers fire.
     RefPtrWillBeRawPtr<Element> protect = element;
     ALLOW_UNUSED_LOCAL(protect);
     if (newDocument) {
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.cpp b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
index 2b68b35..42d731c 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.cpp
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
@@ -583,6 +583,8 @@
             || rareInheritedData->m_textCombine != other.rareInheritedData->m_textCombine
             || rareInheritedData->m_tabSize != other.rareInheritedData->m_tabSize
             || rareInheritedData->listStyleImage != other.rareInheritedData->listStyleImage
+            || rareInheritedData->m_snapHeightUnit != other.rareInheritedData->m_snapHeightUnit
+            || rareInheritedData->m_snapHeightPosition != other.rareInheritedData->m_snapHeightPosition
             || rareInheritedData->textStrokeWidth != other.rareInheritedData->textStrokeWidth)
             return true;
 
diff --git a/third_party/WebKit/Source/platform/audio/ReverbConvolver.cpp b/third_party/WebKit/Source/platform/audio/ReverbConvolver.cpp
index a9550dc9..541f56b 100644
--- a/third_party/WebKit/Source/platform/audio/ReverbConvolver.cpp
+++ b/third_party/WebKit/Source/platform/audio/ReverbConvolver.cpp
@@ -28,6 +28,7 @@
 
 #include "platform/audio/ReverbConvolver.h"
 #include "platform/Task.h"
+#include "platform/ThreadSafeFunctional.h"
 #include "platform/audio/AudioBus.h"
 #include "platform/audio/VectorMath.h"
 #include "public/platform/Platform.h"
@@ -168,7 +169,7 @@
 
     // Now that we've buffered more input, post another task to the background thread.
     if (m_backgroundThread)
-        m_backgroundThread->taskRunner()->postTask(BLINK_FROM_HERE, new Task(WTF::bind(&ReverbConvolver::processInBackground, this)));
+        m_backgroundThread->taskRunner()->postTask(BLINK_FROM_HERE, new Task(threadSafeBind(&ReverbConvolver::processInBackground, AllowCrossThreadAccess(this))));
 }
 
 void ReverbConvolver::reset()
diff --git a/third_party/WebKit/Source/platform/heap/GCTaskRunner.h b/third_party/WebKit/Source/platform/heap/GCTaskRunner.h
index 631cf59e8..af91955 100644
--- a/third_party/WebKit/Source/platform/heap/GCTaskRunner.h
+++ b/third_party/WebKit/Source/platform/heap/GCTaskRunner.h
@@ -31,6 +31,7 @@
 #ifndef GCTaskRunner_h
 #define GCTaskRunner_h
 
+#include "platform/ThreadSafeFunctional.h"
 #include "platform/heap/ThreadState.h"
 #include "public/platform/WebTaskRunner.h"
 #include "public/platform/WebThread.h"
@@ -47,24 +48,18 @@
         // GCTask has an empty run() method. Its only purpose is to guarantee
         // that MessageLoop will have a task to process which will result
         // in GCTaskRunner::didProcessTask being executed.
-        m_taskRunner->postTask(BLINK_FROM_HERE, new GCTask);
+        m_taskRunner->postTask(BLINK_FROM_HERE, threadSafeBind(&runGCTask));
     }
 
 private:
-    class GCTask final : public WebTaskRunner::Task {
-        USING_FAST_MALLOC(GCTask);
-    public:
-        virtual ~GCTask() { }
-
-        void run() override
-        {
-            // Don't do anything here because we don't know if this is
-            // a nested event loop or not. GCTaskRunner::didProcessTask
-            // will enter correct safepoint for us.
-            // We are not calling onInterrupted() because that always
-            // conservatively enters safepoint with pointers on stack.
-        }
-    };
+    static void runGCTask()
+    {
+        // Don't do anything here because we don't know if this is
+        // a nested event loop or not. GCTaskRunner::didProcessTask
+        // will enter correct safepoint for us.
+        // We are not calling onInterrupted() because that always
+        // conservatively enters safepoint with pointers on stack.
+    }
 
     WebTaskRunner* m_taskRunner;
 };
diff --git a/third_party/WebKit/Source/platform/testing/UnitTestHelpers.cpp b/third_party/WebKit/Source/platform/testing/UnitTestHelpers.cpp
index 6dc019f..cb771fa8 100644
--- a/third_party/WebKit/Source/platform/testing/UnitTestHelpers.cpp
+++ b/third_party/WebKit/Source/platform/testing/UnitTestHelpers.cpp
@@ -40,17 +40,9 @@
 namespace blink {
 namespace testing {
 
-class QuitTask : public WebTaskRunner::Task {
-public:
-    virtual void run()
-    {
-        exitRunLoop();
-    }
-};
-
 void runPendingTasks()
 {
-    Platform::current()->currentThread()->taskRunner()->postTask(BLINK_FROM_HERE, new QuitTask);
+    Platform::current()->currentThread()->taskRunner()->postTask(BLINK_FROM_HERE, bind(&exitRunLoop));
     enterRunLoop();
 }
 
@@ -66,10 +58,10 @@
 PassRefPtr<SharedBuffer> readFromFile(const String& path)
 {
     StringUTF8Adaptor utf8(path);
-    base::FilePath file_path = base::FilePath::FromUTF8Unsafe(
+    base::FilePath filePath = base::FilePath::FromUTF8Unsafe(
         std::string(utf8.data(), utf8.length()));
     std::string buffer;
-    base::ReadFileToString(file_path, &buffer);
+    base::ReadFileToString(filePath, &buffer);
     return SharedBuffer::create(buffer.data(), buffer.size());
 }
 
diff --git a/ui/webui/resources/css/widgets.css b/ui/webui/resources/css/widgets.css
index d03f528a..5a5dbc2 100644
--- a/ui/webui/resources/css/widgets.css
+++ b/ui/webui/resources/css/widgets.css
@@ -38,7 +38,7 @@
   min-width: 4em;
   padding-top: 1px;
   padding-bottom: 1px;
-<if expr="is_win">
+<if expr="is_win or is_macosx">
   /* The following platform-specific rule is necessary to get adjacent
    * buttons, text inputs, and so forth to align on their borders while also
    * aligning on the text's baselines. */