service worker: Implement NavigationPreloadManager.getState

BUG=649558

Review-Url: https://codereview.chromium.org/2443103002
Cr-Commit-Position: refs/heads/master@{#427596}
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h
index d236a73..030995a6 100644
--- a/content/browser/bad_message.h
+++ b/content/browser/bad_message.h
@@ -169,6 +169,9 @@
   SWDH_ENABLE_NAVIGATION_PRELOAD_BAD_REGISTRATION_ID = 145,
   RDH_TRANSFERRING_REQUEST_NOT_FOUND = 146,
   RDH_TRANSFERRING_NONNAVIGATIONAL_REQUEST = 147,
+  SWDH_GET_NAVIGATION_PRELOAD_STATE_NO_HOST = 148,
+  SWDH_GET_NAVIGATION_PRELOAD_STATE_INVALID_ORIGIN = 149,
+  SWDH_GET_NAVIGATION_PRELOAD_STATE_BAD_REGISTRATION_ID = 150,
 
   // Please add new elements here. The naming convention is abbreviated class
   // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
diff --git a/content/browser/service_worker/service_worker_dispatcher_host.cc b/content/browser/service_worker/service_worker_dispatcher_host.cc
index 810c63f..978aec54 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host.cc
+++ b/content/browser/service_worker/service_worker_dispatcher_host.cc
@@ -53,6 +53,8 @@
 const char kInvalidStateErrorMessage[] = "The object is in an invalid state.";
 const char kEnableNavigationPreloadErrorPrefix[] =
     "Failed to enable or disable navigation preload: ";
+const char kGetNavigationPreloadStateErrorPrefix[] =
+    "Failed to get navigation preload state: ";
 
 const uint32_t kFilteredMessageClasses[] = {
     ServiceWorkerMsgStart, EmbeddedWorkerMsgStart,
@@ -188,6 +190,8 @@
     IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_TerminateWorker, OnTerminateWorker)
     IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_EnableNavigationPreload,
                         OnEnableNavigationPreload)
+    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_GetNavigationPreloadState,
+                        OnGetNavigationPreloadState)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
 
@@ -724,6 +728,69 @@
   Send(new ServiceWorkerMsg_DidEnableNavigationPreload(thread_id, request_id));
 }
 
+void ServiceWorkerDispatcherHost::OnGetNavigationPreloadState(
+    int thread_id,
+    int request_id,
+    int provider_id,
+    int64_t registration_id) {
+  ProviderStatus provider_status;
+  ServiceWorkerProviderHost* provider_host =
+      GetProviderHostForRequest(&provider_status, provider_id);
+  switch (provider_status) {
+    case ProviderStatus::NO_CONTEXT:  // fallthrough
+    case ProviderStatus::DEAD_HOST:
+      Send(new ServiceWorkerMsg_GetNavigationPreloadStateError(
+          thread_id, request_id, WebServiceWorkerError::ErrorTypeAbort,
+          std::string(kGetNavigationPreloadStateErrorPrefix) +
+              std::string(kShutdownErrorMessage)));
+      return;
+    case ProviderStatus::NO_HOST:
+      bad_message::ReceivedBadMessage(
+          this, bad_message::SWDH_GET_NAVIGATION_PRELOAD_STATE_NO_HOST);
+      return;
+    case ProviderStatus::NO_URL:
+      Send(new ServiceWorkerMsg_GetNavigationPreloadStateError(
+          thread_id, request_id, WebServiceWorkerError::ErrorTypeSecurity,
+          std::string(kGetNavigationPreloadStateErrorPrefix) +
+              std::string(kNoDocumentURLErrorMessage)));
+      return;
+    case ProviderStatus::OK:
+      break;
+  }
+
+  ServiceWorkerRegistration* registration =
+      GetContext()->GetLiveRegistration(registration_id);
+  if (!registration) {
+    // |registration| must be alive because a renderer retains a registration
+    // reference at this point.
+    bad_message::ReceivedBadMessage(
+        this,
+        bad_message::SWDH_GET_NAVIGATION_PRELOAD_STATE_BAD_REGISTRATION_ID);
+    return;
+  }
+
+  std::vector<GURL> urls = {provider_host->document_url(),
+                            registration->pattern()};
+  if (!ServiceWorkerUtils::AllOriginsMatchAndCanAccessServiceWorkers(urls)) {
+    bad_message::ReceivedBadMessage(
+        this, bad_message::SWDH_GET_NAVIGATION_PRELOAD_STATE_INVALID_ORIGIN);
+    return;
+  }
+
+  if (!GetContentClient()->browser()->AllowServiceWorker(
+          registration->pattern(), provider_host->topmost_frame_url(),
+          resource_context_, render_process_id_, provider_host->frame_id())) {
+    Send(new ServiceWorkerMsg_GetNavigationPreloadStateError(
+        thread_id, request_id, WebServiceWorkerError::ErrorTypeDisabled,
+        std::string(kGetNavigationPreloadStateErrorPrefix) +
+            std::string(kUserDeniedPermissionMessage)));
+    return;
+  }
+
+  Send(new ServiceWorkerMsg_DidGetNavigationPreloadState(
+      thread_id, request_id, registration->is_navigation_preload_enabled()));
+}
+
 void ServiceWorkerDispatcherHost::OnPostMessageToWorker(
     int handle_id,
     int provider_id,
diff --git a/content/browser/service_worker/service_worker_dispatcher_host.h b/content/browser/service_worker/service_worker_dispatcher_host.h
index 8b30dcf..3408e04 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host.h
+++ b/content/browser/service_worker/service_worker_dispatcher_host.h
@@ -139,6 +139,10 @@
                                  int provider_id,
                                  int64_t registration_id,
                                  bool enable);
+  void OnGetNavigationPreloadState(int thread_id,
+                                   int request_id,
+                                   int provider_id,
+                                   int64_t registration_id);
   void OnProviderDestroyed(int provider_id);
   void OnSetHostedVersionId(int provider_id,
                             int64_t version_id,
diff --git a/content/browser/service_worker/service_worker_registration.h b/content/browser/service_worker/service_worker_registration.h
index 12bd008..d332fca5 100644
--- a/content/browser/service_worker/service_worker_registration.h
+++ b/content/browser/service_worker/service_worker_registration.h
@@ -95,6 +95,10 @@
     return active_version() || waiting_version();
   }
 
+  bool is_navigation_preload_enabled() const {
+    return is_navigation_preload_enabled_;
+  }
+
   ServiceWorkerVersion* GetNewestVersion() const;
 
   void AddListener(Listener* listener);
diff --git a/content/child/service_worker/service_worker_dispatcher.cc b/content/child/service_worker/service_worker_dispatcher.cc
index 6ca9fac..5619cf4 100644
--- a/content/child/service_worker/service_worker_dispatcher.cc
+++ b/content/child/service_worker/service_worker_dispatcher.cc
@@ -24,6 +24,7 @@
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/common/content_constants.h"
 #include "third_party/WebKit/public/platform/WebString.h"
+#include "third_party/WebKit/public/platform/modules/serviceworker/WebNavigationPreloadState.h"
 #include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerProviderClient.h"
 #include "url/url_constants.h"
 
@@ -81,6 +82,8 @@
                         OnDidGetRegistrationForReady)
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidEnableNavigationPreload,
                         OnDidEnableNavigationPreload)
+    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidGetNavigationPreloadState,
+                        OnDidGetNavigationPreloadState)
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerRegistrationError,
                         OnRegistrationError)
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerUpdateError,
@@ -93,6 +96,8 @@
                         OnGetRegistrationsError)
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_EnableNavigationPreloadError,
                         OnEnableNavigationPreloadError)
+    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_GetNavigationPreloadStateError,
+                        OnGetNavigationPreloadStateError)
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerStateChanged,
                         OnServiceWorkerStateChanged)
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetVersionAttributes,
@@ -222,6 +227,17 @@
       CurrentWorkerId(), request_id, provider_id, registration_id, enable));
 }
 
+void ServiceWorkerDispatcher::GetNavigationPreloadState(
+    int provider_id,
+    int64_t registration_id,
+    std::unique_ptr<WebGetNavigationPreloadStateCallbacks> callbacks) {
+  DCHECK(callbacks);
+  int request_id =
+      get_navigation_preload_state_callbacks_.Add(callbacks.release());
+  thread_safe_sender_->Send(new ServiceWorkerHostMsg_GetNavigationPreloadState(
+      CurrentWorkerId(), request_id, provider_id, registration_id));
+}
+
 void ServiceWorkerDispatcher::AddProviderContext(
     ServiceWorkerProviderContext* provider_context) {
   DCHECK(provider_context);
@@ -547,6 +563,20 @@
   enable_navigation_preload_callbacks_.Remove(request_id);
 }
 
+void ServiceWorkerDispatcher::OnDidGetNavigationPreloadState(int thread_id,
+                                                             int request_id,
+                                                             bool enabled) {
+  WebGetNavigationPreloadStateCallbacks* callbacks =
+      get_navigation_preload_state_callbacks_.Lookup(request_id);
+  DCHECK(callbacks);
+  if (!callbacks)
+    return;
+  // TODO(falken): Implement populating headerValue.
+  callbacks->onSuccess(
+      blink::WebNavigationPreloadState(enabled, blink::WebString()));
+  get_navigation_preload_state_callbacks_.Remove(request_id);
+}
+
 void ServiceWorkerDispatcher::OnRegistrationError(
     int thread_id,
     int request_id,
@@ -674,6 +704,21 @@
   enable_navigation_preload_callbacks_.Remove(request_id);
 }
 
+void ServiceWorkerDispatcher::OnGetNavigationPreloadStateError(
+    int thread_id,
+    int request_id,
+    WebServiceWorkerError::ErrorType error_type,
+    const std::string& message) {
+  WebGetNavigationPreloadStateCallbacks* callbacks =
+      get_navigation_preload_state_callbacks_.Lookup(request_id);
+  DCHECK(callbacks);
+  if (!callbacks)
+    return;
+  callbacks->onError(
+      WebServiceWorkerError(error_type, blink::WebString::fromUTF8(message)));
+  get_navigation_preload_state_callbacks_.Remove(request_id);
+}
+
 void ServiceWorkerDispatcher::OnServiceWorkerStateChanged(
     int thread_id,
     int handle_id,
diff --git a/content/child/service_worker/service_worker_dispatcher.h b/content/child/service_worker/service_worker_dispatcher.h
index ccafedf..4a43c08 100644
--- a/content/child/service_worker/service_worker_dispatcher.h
+++ b/content/child/service_worker/service_worker_dispatcher.h
@@ -69,6 +69,8 @@
           WebServiceWorkerGetRegistrationForReadyCallbacks;
   using WebEnableNavigationPreloadCallbacks =
       blink::WebServiceWorkerRegistration::WebEnableNavigationPreloadCallbacks;
+  using WebGetNavigationPreloadStateCallbacks = blink::
+      WebServiceWorkerRegistration::WebGetNavigationPreloadStateCallbacks;
 
   ServiceWorkerDispatcher(
       ThreadSafeSender* thread_safe_sender,
@@ -111,6 +113,11 @@
       int64_t registration_id,
       bool enable,
       std::unique_ptr<WebEnableNavigationPreloadCallbacks> callbacks);
+  // Corresponds to NavigationPreloadManager.getState.
+  void GetNavigationPreloadState(
+      int provider_id,
+      int64_t registration_id,
+      std::unique_ptr<WebGetNavigationPreloadStateCallbacks> callbacks);
 
   // Called when a new provider context for a document is created. Usually
   // this happens when a new document is being loaded, and is called much
@@ -171,6 +178,8 @@
       IDMapOwnPointer> GetRegistrationForReadyCallbackMap;
   using EnableNavigationPreloadCallbackMap =
       IDMap<WebEnableNavigationPreloadCallbacks, IDMapOwnPointer>;
+  using GetNavigationPreloadStateCallbackMap =
+      IDMap<WebGetNavigationPreloadStateCallbacks, IDMapOwnPointer>;
 
   typedef std::map<int, blink::WebServiceWorkerProviderClient*>
       ProviderClientMap;
@@ -216,6 +225,9 @@
       const ServiceWorkerRegistrationObjectInfo& info,
       const ServiceWorkerVersionAttributes& attrs);
   void OnDidEnableNavigationPreload(int thread_id, int request_id);
+  void OnDidGetNavigationPreloadState(int thread_id,
+                                      int request_id,
+                                      bool enabled);
   void OnRegistrationError(int thread_id,
                            int request_id,
                            blink::WebServiceWorkerError::ErrorType error_type,
@@ -243,6 +255,11 @@
       int request_id,
       blink::WebServiceWorkerError::ErrorType error_type,
       const std::string& message);
+  void OnGetNavigationPreloadStateError(
+      int thread_id,
+      int request_id,
+      blink::WebServiceWorkerError::ErrorType error_type,
+      const std::string& message);
   void OnServiceWorkerStateChanged(int thread_id,
                                    int handle_id,
                                    blink::WebServiceWorkerState state);
@@ -283,6 +300,7 @@
   GetRegistrationsCallbackMap pending_get_registrations_callbacks_;
   GetRegistrationForReadyCallbackMap get_for_ready_callbacks_;
   EnableNavigationPreloadCallbackMap enable_navigation_preload_callbacks_;
+  GetNavigationPreloadStateCallbackMap get_navigation_preload_state_callbacks_;
 
   ProviderClientMap provider_clients_;
   ProviderContextMap provider_contexts_;
diff --git a/content/child/service_worker/web_service_worker_registration_impl.cc b/content/child/service_worker/web_service_worker_registration_impl.cc
index 8a00525d..3043e46 100644
--- a/content/child/service_worker/web_service_worker_registration_impl.cc
+++ b/content/child/service_worker/web_service_worker_registration_impl.cc
@@ -13,6 +13,7 @@
 #include "content/child/service_worker/web_service_worker_impl.h"
 #include "content/child/service_worker/web_service_worker_provider_impl.h"
 #include "content/common/service_worker/service_worker_types.h"
+#include "third_party/WebKit/public/platform/modules/serviceworker/WebNavigationPreloadState.h"
 #include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerError.h"
 #include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerRegistrationProxy.h"
 
@@ -160,6 +161,18 @@
                                       std::move(callbacks));
 }
 
+void WebServiceWorkerRegistrationImpl::getNavigationPreloadState(
+    blink::WebServiceWorkerProvider* provider,
+    std::unique_ptr<WebGetNavigationPreloadStateCallbacks> callbacks) {
+  WebServiceWorkerProviderImpl* provider_impl =
+      static_cast<WebServiceWorkerProviderImpl*>(provider);
+  ServiceWorkerDispatcher* dispatcher =
+      ServiceWorkerDispatcher::GetThreadSpecificInstance();
+  DCHECK(dispatcher);
+  dispatcher->GetNavigationPreloadState(
+      provider_impl->provider_id(), registration_id(), std::move(callbacks));
+}
+
 int64_t WebServiceWorkerRegistrationImpl::registration_id() const {
   return handle_ref_->registration_id();
 }
diff --git a/content/child/service_worker/web_service_worker_registration_impl.h b/content/child/service_worker/web_service_worker_registration_impl.h
index 18fb04a..d9ace78 100644
--- a/content/child/service_worker/web_service_worker_registration_impl.h
+++ b/content/child/service_worker/web_service_worker_registration_impl.h
@@ -59,6 +59,10 @@
       bool enable,
       blink::WebServiceWorkerProvider* provider,
       std::unique_ptr<WebEnableNavigationPreloadCallbacks> callbacks) override;
+  void getNavigationPreloadState(
+      blink::WebServiceWorkerProvider* provider,
+      std::unique_ptr<WebGetNavigationPreloadStateCallbacks> callbacks)
+      override;
 
   int64_t registration_id() const;
 
diff --git a/content/common/service_worker/service_worker_messages.h b/content/common/service_worker/service_worker_messages.h
index 0b858c9a..cf18879 100644
--- a/content/common/service_worker/service_worker_messages.h
+++ b/content/common/service_worker/service_worker_messages.h
@@ -195,6 +195,13 @@
                      int64_t /* registration_id */,
                      bool /* enable */)
 
+// Asks the browser to get navigation preload state for a registration.
+IPC_MESSAGE_CONTROL4(ServiceWorkerHostMsg_GetNavigationPreloadState,
+                     int /* thread_id */,
+                     int /* request_id */,
+                     int /* provider_id */,
+                     int64_t /* registration_id */)
+
 // Sends ExtendableMessageEvent to a service worker (renderer->browser).
 IPC_MESSAGE_CONTROL5(
     ServiceWorkerHostMsg_PostMessageToWorker,
@@ -491,6 +498,15 @@
                      int /* request_id */,
                      blink::WebServiceWorkerError::ErrorType /* code */,
                      std::string /* message */)
+IPC_MESSAGE_CONTROL3(ServiceWorkerMsg_DidGetNavigationPreloadState,
+                     int /* thread_id */,
+                     int /* request_id */,
+                     bool /* enabled */)
+IPC_MESSAGE_CONTROL4(ServiceWorkerMsg_GetNavigationPreloadStateError,
+                     int /* thread_id */,
+                     int /* request_id */,
+                     blink::WebServiceWorkerError::ErrorType /* code */,
+                     std::string /* message */)
 
 // Sends MessageEvent to a client document (browser->renderer).
 IPC_MESSAGE_CONTROL1(ServiceWorkerMsg_MessageToDocument,
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 802341e8..4156260 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -364,6 +364,11 @@
 crbug.com/635619 virtual/layout_ng/fast/block/float/width-update-after-clear.html [ Skip ]
 # ====== LayoutNG-only failures until here ======
 
+# Requires ServiceWorkerNavigationPreload feature enabled. Run under
+# virtual/service-worker-navigation-preload only.
+crbug.com/649558 http/tests/serviceworker/navigation-preload/ [ Skip ]
+crbug.com/649558 virtual/mojo-service-worker/http/tests/serviceworker/navigation-preload/ [ Skip ]
+
 # Run these tests with under virtual/scalefactor... only.
 crbug.com/567837 fast/hidpi/static [ Skip ]
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/get-state.html b/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/get-state.html
new file mode 100644
index 0000000..07555e97
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/get-state.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>NavigationPreloadManager.getState</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../resources/test-helpers.js"></script>
+<body>
+<script>
+function post_and_wait_for_reply(worker, message) {
+  return new Promise(resolve => {
+      navigator.serviceWorker.onmessage = e => { resolve(e.data); };
+      worker.postMessage(message);
+    });
+}
+
+promise_test(t => {
+  const scope = '../resources/getState';
+  const script = '../resources/empty-worker.js';
+  var registration;
+
+  return service_worker_unregister_and_register(t, script, scope)
+    .then(r => {
+        registration = r;
+        add_completion_callback(() => registration.unregister());
+        return registration.navigationPreload.getState();
+      })
+    .then(state => {
+        assert_equals(state.enabled, false,
+                      '.enabled should be false by default');
+        return registration.navigationPreload.enable();
+      })
+    .then(() => registration.navigationPreload.getState())
+    .then(state => {
+        assert_equals(state.enabled, true,
+                      '.enabled should be true after enable()');
+        return registration.navigationPreload.disable();
+      })
+    .then(() => registration.navigationPreload.getState())
+    .then(state => {
+        assert_equals(state.enabled, false,
+                      '.enabled should be false after disable()');
+      });
+  }, 'getState');
+
+// This test sends commands to a worker to call enable()/disable()/getState().
+// It checks the results from the worker and verifies that they match the
+// navigation preload state accessible from the page.
+promise_test(t => {
+  const scope = 'resources/getState-worker';
+  const script = 'resources/get-state-worker.js';
+  var worker;
+  var registration;
+
+  return service_worker_unregister_and_register(t, script, scope)
+    .then(r => {
+        registration = r;
+        add_completion_callback(() => registration.unregister());
+        worker = registration.installing;
+        // Call getState().
+        return post_and_wait_for_reply(worker, 'getState');
+      })
+    .then(data => {
+        return Promise.all([data, registration.navigationPreload.getState()]);
+      })
+    .then(states => {
+        assert_equals(states[0].enabled, false,
+                      '.enabled should be false by default (from worker)');
+        assert_equals(states[1].enabled, false,
+                      '.enabled should be false by default (from page)');
+        // Call enable() and then getState().
+        return post_and_wait_for_reply(worker, 'enable');
+      })
+    .then(data => {
+        assert_equals(data, undefined, 'enable() should resolve to undefined');
+        return Promise.all([
+            post_and_wait_for_reply(worker, 'getState'),
+            registration.navigationPreload.getState()
+          ]);
+      })
+    .then(states => {
+        assert_equals(states[0].enabled, true,
+                      '.enabled should be true after enable() (from worker)');
+        assert_equals(states[1].enabled, true,
+                      '.enabled should be true after enable() (from page)');
+        // Call disable() and then getState().
+        return post_and_wait_for_reply(worker, 'disable');
+      })
+    .then(data => {
+        assert_equals(data, undefined,
+                      '.disable() should resolve to undefined');
+        return Promise.all([
+            post_and_wait_for_reply(worker, 'getState'),
+            registration.navigationPreload.getState()
+          ]);
+      })
+    .then(states => {
+        assert_equals(states[0].enabled, false,
+                      '.enabled should be false after disable() (from worker)');
+        assert_equals(states[1].enabled, false,
+                      '.enabled should be false after disable() (from page)');
+      });
+  }, 'getState from a worker');
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/resources/get-state-worker.js b/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/resources/get-state-worker.js
new file mode 100644
index 0000000..93ee461
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/resources/get-state-worker.js
@@ -0,0 +1,19 @@
+// This worker listens for commands from the page and messages back
+// the result.
+
+function handle(message) {
+  const np = self.registration.navigationPreload;
+  switch (message) {
+    case 'getState':
+      return np.getState();
+    case 'enable':
+      return np.enable();
+    case 'disable':
+      return np.disable();
+  }
+  return Promise.reject('bad message');
+}
+
+self.addEventListener('message', e => {
+    e.waitUntil(handle(e.data).then(result => e.source.postMessage(result)));
+  });
diff --git a/third_party/WebKit/Source/modules/serviceworkers/NavigationPreloadCallbacks.cpp b/third_party/WebKit/Source/modules/serviceworkers/NavigationPreloadCallbacks.cpp
index 4a9e0366b..ac7afe3 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/NavigationPreloadCallbacks.cpp
+++ b/third_party/WebKit/Source/modules/serviceworkers/NavigationPreloadCallbacks.cpp
@@ -6,7 +6,9 @@
 
 #include "bindings/core/v8/ScriptPromiseResolver.h"
 #include "core/dom/DOMException.h"
+#include "modules/serviceworkers/NavigationPreloadState.h"
 #include "modules/serviceworkers/ServiceWorkerError.h"
+#include "public/platform/modules/serviceworker/WebNavigationPreloadState.h"
 
 namespace blink {
 
@@ -33,4 +35,32 @@
   m_resolver->reject(ServiceWorkerError::take(m_resolver.get(), error));
 }
 
+GetNavigationPreloadStateCallbacks::GetNavigationPreloadStateCallbacks(
+    ScriptPromiseResolver* resolver)
+    : m_resolver(resolver) {
+  DCHECK(m_resolver);
+}
+
+GetNavigationPreloadStateCallbacks::~GetNavigationPreloadStateCallbacks() {}
+
+void GetNavigationPreloadStateCallbacks::onSuccess(
+    const WebNavigationPreloadState& state) {
+  if (!m_resolver->getExecutionContext() ||
+      m_resolver->getExecutionContext()->activeDOMObjectsAreStopped())
+    return;
+  NavigationPreloadState dict;
+  dict.setEnabled(state.enabled);
+  if (!state.headerValue.isNull())
+    dict.setHeaderValue(state.headerValue);
+  m_resolver->resolve(dict);
+}
+
+void GetNavigationPreloadStateCallbacks::onError(
+    const WebServiceWorkerError& error) {
+  if (!m_resolver->getExecutionContext() ||
+      m_resolver->getExecutionContext()->activeDOMObjectsAreStopped())
+    return;
+  m_resolver->reject(ServiceWorkerError::take(m_resolver.get(), error));
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/serviceworkers/NavigationPreloadCallbacks.h b/third_party/WebKit/Source/modules/serviceworkers/NavigationPreloadCallbacks.h
index 612fbe0e..4e935d4 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/NavigationPreloadCallbacks.h
+++ b/third_party/WebKit/Source/modules/serviceworkers/NavigationPreloadCallbacks.h
@@ -10,6 +10,7 @@
 namespace blink {
 
 class ScriptPromiseResolver;
+struct WebNavigationPreloadState;
 struct WebServiceWorkerError;
 
 class EnableNavigationPreloadCallbacks final
@@ -27,6 +28,22 @@
   WTF_MAKE_NONCOPYABLE(EnableNavigationPreloadCallbacks);
 };
 
+class GetNavigationPreloadStateCallbacks final
+    : public WebServiceWorkerRegistration::
+          WebGetNavigationPreloadStateCallbacks {
+ public:
+  GetNavigationPreloadStateCallbacks(ScriptPromiseResolver*);
+  ~GetNavigationPreloadStateCallbacks() override;
+
+  // WebGetNavigationPreloadStateCallbacks interface.
+  void onSuccess(const WebNavigationPreloadState&) override;
+  void onError(const WebServiceWorkerError&) override;
+
+ private:
+  Persistent<ScriptPromiseResolver> m_resolver;
+  WTF_MAKE_NONCOPYABLE(GetNavigationPreloadStateCallbacks);
+};
+
 }  // namespace blink
 
 #endif  // NavigationPreloadCallbacks_h
diff --git a/third_party/WebKit/Source/modules/serviceworkers/NavigationPreloadManager.cpp b/third_party/WebKit/Source/modules/serviceworkers/NavigationPreloadManager.cpp
index 9e89e74..9d2a79c 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/NavigationPreloadManager.cpp
+++ b/third_party/WebKit/Source/modules/serviceworkers/NavigationPreloadManager.cpp
@@ -25,9 +25,19 @@
   return ScriptPromise();
 }
 
-ScriptPromise NavigationPreloadManager::getState(ScriptState*) {
-  NOTIMPLEMENTED();
-  return ScriptPromise();
+ScriptPromise NavigationPreloadManager::getState(ScriptState* scriptState) {
+  ServiceWorkerContainerClient* client =
+      ServiceWorkerContainerClient::from(m_registration->getExecutionContext());
+  if (!client || !client->provider()) {
+    return ScriptPromise::rejectWithDOMException(
+        scriptState, DOMException::create(InvalidStateError, "No provider."));
+  }
+  ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
+  ScriptPromise promise = resolver->promise();
+  m_registration->webRegistration()->getNavigationPreloadState(
+      client->provider(),
+      wrapUnique(new GetNavigationPreloadStateCallbacks(resolver)));
+  return promise;
 }
 
 NavigationPreloadManager::NavigationPreloadManager(
diff --git a/third_party/WebKit/public/BUILD.gn b/third_party/WebKit/public/BUILD.gn
index bab5fcab..3f99086 100644
--- a/third_party/WebKit/public/BUILD.gn
+++ b/third_party/WebKit/public/BUILD.gn
@@ -376,6 +376,7 @@
     "platform/modules/screen_orientation/WebScreenOrientationClient.h",
     "platform/modules/screen_orientation/WebScreenOrientationLockType.h",
     "platform/modules/screen_orientation/WebScreenOrientationType.h",
+    "platform/modules/serviceworker/WebNavigationPreloadState.h",
     "platform/modules/serviceworker/WebServiceWorker.h",
     "platform/modules/serviceworker/WebServiceWorkerCache.h",
     "platform/modules/serviceworker/WebServiceWorkerCacheError.h",
diff --git a/third_party/WebKit/public/platform/modules/serviceworker/WebNavigationPreloadState.h b/third_party/WebKit/public/platform/modules/serviceworker/WebNavigationPreloadState.h
new file mode 100644
index 0000000..c30845e
--- /dev/null
+++ b/third_party/WebKit/public/platform/modules/serviceworker/WebNavigationPreloadState.h
@@ -0,0 +1,19 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebNavigationPreloadState_h
+#define WebNavigationPreloadState_h
+
+namespace blink {
+
+struct WebNavigationPreloadState {
+  WebNavigationPreloadState(bool enabled, const WebString& headerValue)
+      : enabled(enabled), headerValue(headerValue) {}
+
+  bool enabled;
+  WebString headerValue;
+};
+}
+
+#endif  // WebNavigationPreloadState_h
diff --git a/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerRegistration.h b/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerRegistration.h
index fe5ba1b..980fa38 100644
--- a/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerRegistration.h
+++ b/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerRegistration.h
@@ -13,6 +13,7 @@
 
 class WebServiceWorkerProvider;
 class WebServiceWorkerRegistrationProxy;
+struct WebNavigationPreloadState;
 
 // The interface of the registration representation in the embedder. The
 // embedder implements this interface and passes its handle
@@ -28,6 +29,9 @@
       WebCallbacks<bool, const WebServiceWorkerError&>;
   using WebEnableNavigationPreloadCallbacks =
       WebCallbacks<void, const WebServiceWorkerError&>;
+  using WebGetNavigationPreloadStateCallbacks =
+      WebCallbacks<const WebNavigationPreloadState&,
+                   const WebServiceWorkerError&>;
 
   // The handle interface that retains a reference to the implementation of
   // WebServiceWorkerRegistration in the embedder and is owned by
@@ -53,6 +57,9 @@
       bool enable,
       WebServiceWorkerProvider*,
       std::unique_ptr<WebEnableNavigationPreloadCallbacks>) {}
+  virtual void getNavigationPreloadState(
+      WebServiceWorkerProvider*,
+      std::unique_ptr<WebGetNavigationPreloadStateCallbacks>) {}
 };
 
 }  // namespace blink
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 6439193..2b8f31d 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -74238,6 +74238,12 @@
   <int value="143" label="SWDH_ENABLE_NAVIGATION_PRELOAD_NO_HOST"/>
   <int value="144" label="SWDH_ENABLE_NAVIGATION_PRELOAD_INVALID_ORIGIN"/>
   <int value="145" label="SWDH_ENABLE_NAVIGATION_PRELOAD_BAD_REGISTRATION_ID"/>
+  <int value="146" label="RDH_TRANSFERRING_REQUEST_NOT_FOUND"/>
+  <int value="147" label="RDH_TRANSFERRING_NONNAVIGATIONAL_REQUEST"/>
+  <int value="148" label="SWDH_GET_NAVIGATION_PRELOAD_STATE_NO_HOST"/>
+  <int value="149" label="SWDH_GET_NAVIGATION_PRELOAD_STATE_INVALID_ORIGIN"/>
+  <int value="150"
+      label="SWDH_GET_NAVIGATION_PRELOAD_STATE_BAD_REGISTRATION_ID"/>
 </enum>
 
 <enum name="BadMessageReasonExtensions" type="int">